Video Audio Call App Using Flutter

Code With Ammar
4 min readOct 10, 2021

how to make video and audio call app using flutter and agora

  • add this dependencies to pubsbec.yaml file

agora_rtc_engine: ^4.0.7

permission_handler:

  • Create this files in lab diroctory :

appBrain.dart

homeScreen.dart

videoCallScreen.dart

audioCallScreen.dart

  • Now let’s go to agora site and sign up there

https://console.agora.io

  • Create a new project and generate a new temp token
  • you will get 3 things you should use in your app

token — appId-channel name

for channel name you can set whatever name you want

  • now let’s go back to our flutter project
  • inside the appBrain.dart let’s create this class

class AgoraManager {
static String get appId {
return “YourAppId”;
}

static String get token {
return “YourToken”;
}

static String get channelName {
return “YourChannelName”;
}
}

  • The main file in this sample just to start project from the home screen

import ‘package:fluentapp/homeScreen.dart’;
import ‘package:flutter/material.dart’;

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: ‘Flutter Demo’,
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: HomeScreen(),
);
}
}

  • The home screen file in this example just a contact information and in the bottom there is to button the first one to make video Call and the second one to make audio call

import ‘package:fluentapp/audioCallScreen.dart’;
import ‘package:fluentapp/videoCallScreen.dart’;
import ‘package:flutter/material.dart’;

class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);

@override
_HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Fluent App’),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(150.0),
child: Image.network(
https://play-lh.googleusercontent.com/ZpQcKuCwbQnrCgNpsyUsgDjuBUnpcIBkVrPSDKS9LOJTAW1kxMsu6cLltOSUODjiEQ=w500-h280-rw',
height: 200.0,
width: 200.0,
fit: BoxFit.cover,
),
),
Text(
‘Amar Awni’,
style: Theme.of(context).textTheme.headline3,
),
Text(
‘+90 555 000 00 00’,
style: Theme.of(context).textTheme.headline6,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VideoCallScreen()));
},
icon: Icon(
Icons.video_call,
size: 44,
),
color: Colors.teal,
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AudioCallScreen()));
},
icon: Icon(
Icons.phone,
size: 35,
),
color: Colors.teal,
),
],
),
),
],
),
);
}
}

  • This part of lesson is the important one :

let’s code the Video Call Screen:

import this libraries :

import ‘package:agora_rtc_engine/rtc_engine.dart’;
import ‘package:fluentapp/appBrain.dart’;
import ‘package:flutter/material.dart’;
import ‘package:flutter/material.dart’;
import ‘package:permission_handler/permission_handler.dart’;
import ‘package:agora_rtc_engine/rtc_local_view.dart’ as RtcLocalView;
import ‘package:agora_rtc_engine/rtc_remote_view.dart’ as RtcRemoteView;

inside the class add this varibales and functions first at all

late int _remoteUid = 0;
late RtcEngine _engine;

@override
void initState() {
super.initState();
initAgora();
}

@override
void dispose() {
super.dispose();
_engine.leaveChannel();
}

now lets create the functions :

//Functions
Future<void> initAgora() async {
await [Permission.microphone, Permission.camera].request();
_engine = await RtcEngine.create(AgoraManager.appId);
_engine.enableVideo();
_engine.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) {
print(‘local user $uid joined successfully’);
},
userJoined: (int uid, int elapsed) {
// player.stop();
print(‘remote user $uid joined successfully’);
setState(() => _remoteUid = uid);
},
userOffline: (int uid, UserOfflineReason reason) {
print(‘remote user $uid left call’);
setState(() => _remoteUid = 0);
Navigator.of(context).pop(true);
},
),
);
await _engine.joinChannel(
AgoraManager.token, AgoraManager.channelName, null, 0);
}

//current User View
Widget _renderLocalPreview() {
return RtcLocalView.SurfaceView();
}
//remote User View

Widget _renderRemoteVideo() {
if (_remoteUid != 0) {
return RtcRemoteView.SurfaceView(
uid: _remoteUid,
);
} else {
return Text(
‘Calling …’,
style: Theme.of(context).textTheme.headline6,
textAlign: TextAlign.center,
);
}
}

and the last part is the code of screen inside the build widget return :

return Scaffold(
body: Stack(
children: [
Center(
child: _renderRemoteVideo(),
),
SafeArea(
child: Align(
alignment: Alignment.bottomLeft,
child: ClipRRect(
borderRadius: BorderRadius.circular(150.0),
child: Container(
height: 150, width: 150, child: _renderLocalPreview()),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 25.0, right: 25),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
onPressed: () {
Navigator.of(context).pop(true);
},
icon: Icon(
Icons.call_end,
size: 44,
color: Colors.redAccent,
)),
],
),
),
),
],
),
);

for the audio call it’s so similar :

import ‘package:agora_rtc_engine/rtc_engine.dart’;
import ‘package:fluentapp/appBrain.dart’;
import ‘package:flutter/material.dart’;
import ‘package:permission_handler/permission_handler.dart’;
import ‘package:agora_rtc_engine/rtc_local_view.dart’ as RtcLocalView;
import ‘package:agora_rtc_engine/rtc_remote_view.dart’ as RtcRemoteView;

class AudioCallScreen extends StatefulWidget {
const AudioCallScreen({Key? key}) : super(key: key);

@override
_AudioCallScreenState createState() => _AudioCallScreenState();
}

class _AudioCallScreenState extends State<AudioCallScreen> {
late int _remoteUid = 0;
late RtcEngine _engine;

@override
void initState() {
super.initState();
initAgora();
}

@override
void dispose() {
super.dispose();
_engine.leaveChannel();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.black87,
child: Center(
child: _remoteUid == 0
? Text(
‘Calling …’,
style: TextStyle(color: Colors.white),
)
: Text(
‘Calling with $_remoteUid’,
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 25.0, right: 25),
child: Container(
height: 50,
color: Colors.black12,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
onPressed: () {
Navigator.of(context).pop(true);
},
icon: Icon(
Icons.call_end,
size: 44,
color: Colors.redAccent,
)),
],
),
),
),
),
],
),
);
}

Future<void> initAgora() async {
await [Permission.microphone, Permission.camera].request();
_engine = await RtcEngine.create(AgoraManager.appId);
_engine.enableVideo();
_engine.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) {
print(‘local user $uid joined successfully’);
},
userJoined: (int uid, int elapsed) {
print(‘remote user $uid joined successfully’);
setState(() => _remoteUid = uid);
},
userOffline: (int uid, UserOfflineReason reason) {
print(‘remote user $uid left call’);
setState(() => _remoteUid = 0);
Navigator.of(context).pop(true);
},
),
);
await _engine.joinChannel(
AgoraManager.token, AgoraManager.channelName, null, 0);
}

Widget _renderRemoteAudio() {
if (_remoteUid != 0) {
return Text(
‘Calling with $_remoteUid’,
style: TextStyle(color: Colors.white),
);
} else {
return Text(
‘Calling …’,
style: TextStyle(color: Colors.white),
);
}
}
}

for more information about how you can use agora with flutter :

and for the android don’t forget to add this code to AndroidManifest file above the application and inside Manifest<>

<uses-permission android:name=”android.permission.READ_PHONE_STATE”/>
<uses-permission android:name=”android.permission.INTERNET” />
<uses-permission android:name=”android.permission.RECORD_AUDIO” />
<uses-permission android:name=”android.permission.CAMERA” />
<uses-permission android:name=”android.permission.MODIFY_AUDIO_SETTINGS” />
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
<uses-permission android:name=”android.permission.BLUETOOTH” />

Amar Awni

Thank you ..

--

--

Code With Ammar

Mobile apps developer I'm Amar, I'm a developer with a passion for teaching. with all wishes for success .