Agora Video Calling SDK with Flutter: Build a Real-Time Video Call App
Real-time video calling is common in healthcare, education, customer support, social networking, and collaboration applications. Instead of implementing WebRTC infrastructure yourself, Flutter developers can use the Agora RTC SDK.
In this tutorial, we'll build the foundation of a Flutter video calling application using agora_rtc_engine.
What We'll Build
- Initialize the Agora engine.
- Request camera and microphone permissions.
- Join an Agora channel.
- Publish local audio and video.
- Display remote participants.
- Leave the channel and release resources.
Step 1: Create a Flutter Project
flutter create agora_video_demo
cd agora_video_demo
Step 2: Add the Agora Flutter SDK
flutter pub add agora_rtc_engine
flutter pub add permission_handler
The current stable package is agora_rtc_engine 6.6.3 at the time of writing. Verify the current release before publishing or building your project.
Step 3: Configure Android Permissions
Add the required permissions to android/app/src/main/AndroidManifest.xml:
<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_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
Step 4: Request Runtime Permissions
await [
Permission.camera,
Permission.microphone,
].request();
Handle denied permissions properly in a production application.
Step 5: Create the RTC Engine
final RtcEngine engine = createAgoraRtcEngine();
await engine.initialize(
const RtcEngineContext(
appId: appId,
),
);
RtcEngine is the main object for Agora real-time audio and video features.
Step 6: Register Event Handlers
engine.registerEventHandler(
RtcEngineEventHandler(
onJoinChannelSuccess: (connection, elapsed) {
debugPrint('Joined: ${connection.channelId}');
},
onUserJoined: (connection, remoteUid, elapsed) {
debugPrint('Remote user: $remoteUid');
},
onUserOffline: (connection, remoteUid, reason) {
debugPrint('User left: $remoteUid');
},
),
);
Keep these events connected to your state-management layer for a larger application.
Step 7: Enable Video
await engine.enableVideo();
await engine.startPreview();
Step 8: Join a Channel
await engine.joinChannel(
token: token,
channelId: 'demo-room',
uid: 0,
options: const ChannelMediaOptions(),
);
For production, obtain short-lived tokens from your backend rather than embedding permanent credentials in the app.
Step 9: Render Local Video
AgoraVideoView(
controller: VideoViewController(
rtcEngine: engine,
canvas: const VideoCanvas(uid: 0),
),
)
Step 10: Render Remote Video
When onUserJoined fires, save the remote UID and render:
AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: engine,
canvas: VideoCanvas(uid: remoteUid),
connection: RtcConnection(
channelId: 'demo-room',
),
),
)
For multiple users, keep a list of remote UIDs in your application state.
Step 11: Mute Audio
await engine.muteLocalAudioStream(true);
Toggle the value when the user presses the microphone button.
Step 12: Disable Video
await engine.muteLocalVideoStream(true);
This lets the user turn video off without leaving the meeting.
Step 13: Leave the Channel
await engine.leaveChannel();
await engine.release();
Release the engine when the call is finished.
Production Token Architecture
Use this pattern:
Flutter App
|
| Request token
v
Your Backend
|
| Generate short-lived token
v
Flutter App
|
| Join channel
v
Agora
Never place server-side secrets in Flutter.
Common Problems
Black Camera Preview
Check camera permission, enableVideo(), startPreview(), and whether another application is using the camera.
Remote Video Missing
Verify that both users joined the same channel and that the remote UID is being stored and rendered correctly.
Call Fails in Production
Implement a backend token service and handle token expiration instead of using development credentials.
Recommended Architecture
Flutter UI
↓
Call Controller / BLoC / Riverpod
↓
Agora Service
↓
Agora RTC Engine
Keep Agora-specific code inside a service so your business logic remains independent of the SDK.
Conclusion
SDK integrations are easiest to maintain when credentials, platform-specific configuration, networking, and UI responsibilities are separated. Start with the smallest working flow, verify it on a physical device, and then add production concerns such as authentication, error handling, lifecycle management, and secure credential handling.
Stay tuned for more advanced Flutter SDK integration tutorials!
SEO Keywords
Flutter SDK tutorial, Flutter integration, Flutter mobile development, Dart SDK integration, Flutter Android, Flutter iOS, SDK integration
Tags
flutter dart mobiledevelopment sdk android
Useful Links
Website: www.v-modal.com
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter
SDK Android: https://github.com/v-modal/vmodal_sdk_android
Discord: https://discord.gg/K72z28KUx
Top comments (0)