Я пытаюсь использоватьagora_rtc для видеовызовов, черная часть выше — это камера, которую можно использовать. посмотри сам, эта функция работает на Android, но в iOS появляется черный экран и камера не открывается
Я настраиваю все разрешения как для iOS, так и для Android, кто-нибудь может мне помочь с этим, введите описание изображения здесь
Мой код такой следует:
import 'package:flutter/foundation.dart';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:async';
import 'package:flutter_svg/flutter_svg.dart';
class VideoChatWidget extends StatefulWidget {
const VideoChatWidget({
super.key,
this.width,
this.height,
required this.channelName,
required this.appId,
required this.token,
required this.userData,
});
final double? width;
final double? height;
final String channelName;
final String appId;
final String token;
final ChatsStruct userData;
@override
State createState() => _VideoChatWidgetState();
}
class _VideoChatWidgetState extends State {
bool isCameraOff = false;
bool isMuted = false;
late RtcEngine _engine;
bool _localUserJoined = true;
int? _remoteUid;
Timer? _callTimer;
Duration _callDuration = Duration(seconds: 0);
@override
void initState() {
super.initState();
initializeAgora();
_startCallTimer();
}
Future initializeAgora() async {
if (!kIsWeb) {
final permissions =
await [Permission.microphone, Permission.camera].request();
if (permissions.values
.any((status) => status != PermissionStatus.granted)) {
print("Permissions not granted");
return;
}
}
print('
try {
_engine = createAgoraRtcEngine();
await _engine.initialize(RtcEngineContext(
appId: widget.appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
print('
await _engine.enableVideo();
await _engine.startPreview(); // Start local video preview
await _engine.joinChannel(
token: widget.token,
channelId: widget.channelName,
uid: 0,
options: const ChannelMediaOptions(
clientRoleType: ClientRoleType.clientRoleBroadcaster,
channelProfile: ChannelProfileType.channelProfileCommunication,
),
);
print('
setState(() {
_localUserJoined = true;
});
} catch (e) {
print('
}
}
@override
void dispose() {
_callTimer?.cancel();
_engine.leaveChannel();
_engine.release();
super.dispose();
}
void _startCallTimer() {
_callTimer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
_callDuration = Duration(seconds: _callDuration.inSeconds + 1);
});
});
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final hours = twoDigits(duration.inHours);
final minutes = twoDigits(duration.inMinutes.remainder(60));
final seconds = twoDigits(duration.inSeconds.remainder(60));
return hours != '00' ? "$hours:$minutes:$seconds" : "$minutes:$seconds";
}
@override
Widget build(BuildContext context) {
if (_engine == null) {
print('
}
return Scaffold(
body: Stack(
children: [
_remoteVideo(),
Positioned(
top: 0,
child: _buildHeader(),
),
Align(
alignment: Alignment.bottomCenter,
child: _toolbar(),
),
isCameraOff ? Container() : _buildLocalPreview(),
],
),
);
}
Widget _buildHeader() {
return Container(
width: MediaQuery.of(context).size.width,
height: 68,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
boxShadow: [
BoxShadow(
offset: Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
color: Color(0xFF999999).withOpacity(0.08),
),
]),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () => Navigator.pop(context),
child: Icon(
Icons.arrow_back,
size: 18,
color: Colors.white,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Call with ${widget.userData.senderData.firstName} ${widget.userData.senderData.lastName}",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.white),
),
SizedBox(height: 2),
Text(
_formatDuration(_callDuration),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w300,
color: Colors.white,
),
),
],
),
Icon(
Icons.more_vert,
size: 18,
color: Colors.white,
),
],
),
),
);
}
Widget _remoteVideo() {
if (_remoteUid != null) {
return AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: _engine,
canvas: VideoCanvas(uid: _remoteUid),
connection: RtcConnection(channelId: widget.channelName),
),
);
} else {
return Image.network(
widget.userData.senderData.photo,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
);
}
}
Widget _buildLocalPreview() {
return Positioned(
bottom: 120,
right: 16,
child: Container(
width: 137,
height: 172,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(20),
),
child: _localUserJoined
? ClipRRect(
borderRadius: BorderRadius.circular(20),
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: VideoCanvas(uid: 0),
),
),
)
: const Center(child: CircularProgressIndicator()),
),
);
}
Widget _toolbar() {
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: _onToggleMute,
child: Icon(isMuted ? Icons.mic_off : Icons.mic),
),
GestureDetector(
onTap: () => Navigator.pop(context),
child: Icon(Icons.call_end),
),
GestureDetector(
onTap: _onToggleCamera,
child: Icon(isCameraOff ? Icons.videocam_off : Icons.videocam),
),
],
),
);
}
void _onToggleCamera() {
setState(() {
isCameraOff = !isCameraOff;
_engine.muteLocalVideoStream(isCameraOff);
});
}
void _onToggleMute() {
setState(() {
isMuted = !isMuted;
_engine.muteLocalAudioStream(isMuted);
});
}
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... ios-device