I am making an attempt to construct a video calling function in my Flutter app utilizing Agora.
i’m making an attempt to make use of agora_rtc for video name , the black half above is digicam the place use can see him self, this function works will on anroid however in ios seem black display screen and no digicam opened
i setup all permissions for each ios and android anybody can assist me with thatenter picture description right here
My code is as follows:
import 'bundle:flutter/basis.dart';
import 'bundle:agora_rtc_engine/agora_rtc_engine.dart';
import 'bundle:permission_handler/permission_handler.dart';
import 'dart:async';
import 'bundle:flutter_svg/flutter_svg.dart';
class VideoChatWidget extends StatefulWidget {
const VideoChatWidget({
tremendous.key,
this.width,
this.peak,
required this.channelName,
required this.appId,
required this.token,
required this.userData,
});
closing double? width;
closing double? peak;
closing String channelName;
closing String appId;
closing String token;
closing 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;
Length _callDuration = Length(seconds: 0);
@override
void initState() {
tremendous.initState();
initializeAgora();
_startCallTimer();
}
Future initializeAgora() async {
if (!kIsWeb) {
closing permissions =
await [Permission.microphone, Permission.camera].request();
if (permissions.values
.any((standing) => standing != PermissionStatus.granted)) {
print("Permissions not granted");
return;
}
}
print('🚀 Initializing Agora');
attempt {
_engine = createAgoraRtcEngine();
await _engine.initialize(RtcEngineContext(
appId: widget.appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
print('✅ Agora Engine Initialized');
await _engine.enableVideo();
await _engine.startPreview(); // Begin native video preview
await _engine.joinChannel(
token: widget.token,
channelId: widget.channelName,
uid: 0,
choices: const ChannelMediaOptions(
clientRoleType: ClientRoleType.clientRoleBroadcaster,
channelProfile: ChannelProfileType.channelProfileCommunication,
),
);
print('✅ Joined Channel Efficiently');
setState(() {
_localUserJoined = true;
});
} catch (e) {
print('❌ Agora Initialization Error: $e');
}
}
@override
void dispose() {
_callTimer?.cancel();
_engine.leaveChannel();
_engine.launch();
tremendous.dispose();
}
void _startCallTimer() {
_callTimer = Timer.periodic(Length(seconds: 1), (timer) {
setState(() {
_callDuration = Length(seconds: _callDuration.inSeconds + 1);
});
});
}
String _formatDuration(Length period) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
closing hours = twoDigits(period.inHours);
closing minutes = twoDigits(period.inMinutes.the rest(60));
closing seconds = twoDigits(period.inSeconds.the rest(60));
return hours != '00' ? "$hours:$minutes:$seconds" : "$minutes:$seconds";
}
@override
Widget construct(BuildContext context) {
if (_engine == null) {
print('⚠️ _engine just isn't initialized but');
}
return Scaffold(
physique: Stack(
youngsters: [
_remoteVideo(),
Positioned(
top: 0,
child: _buildHeader(),
),
Align(
alignment: Alignment.bottomCenter,
child: _toolbar(),
),
isCameraOff ? Container() : _buildLocalPreview(),
],
),
);
}
Widget _buildHeader() {
return Container(
width: MediaQuery.of(context).measurement.width,
peak: 68,
ornament: BoxDecoration(
colour: Colours.black.withOpacity(0.5),
boxShadow: [
BoxShadow(
offset: Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
color: Color(0xFF999999).withOpacity(0.08),
),
]),
youngster: Padding(
padding: const EdgeInsets.all(16),
youngster: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
youngsters: [
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,
measurement: 18,
colour: Colours.white,
),
],
),
),
);
}
Widget _remoteVideo() {
if (_remoteUid != null) {
return AgoraVideoView(
controller: VideoViewController.distant(
rtcEngine: _engine,
canvas: VideoCanvas(uid: _remoteUid),
connection: RtcConnection(channelId: widget.channelName),
),
);
} else {
return Picture.community(
widget.userData.senderData.photograph,
peak: MediaQuery.of(context).measurement.peak,
width: MediaQuery.of(context).measurement.width,
match: BoxFit.cowl,
);
}
}
Widget _buildLocalPreview() {
return Positioned(
backside: 120,
proper: 16,
youngster: Container(
width: 137,
peak: 172,
ornament: BoxDecoration(
colour: Colours.black,
borderRadius: BorderRadius.round(20),
),
youngster: _localUserJoined
? ClipRRect(
borderRadius: BorderRadius.round(20),
youngster: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: VideoCanvas(uid: 0),
),
),
)
: const Middle(youngster: CircularProgressIndicator()),
),
);
}
Widget _toolbar() {
return Container(
youngster: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
youngsters: [
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);
});
}
}