Integrate Agora.io video calling into FlutterFlow via a Custom Widget that handles WebRTC streams and a Cloud Function that generates secure channel tokens. Store room state in Firestore with participant tracking. Build controls for mute, camera toggle, and screen sharing, with a dynamic grid layout that adapts from one-on-one to multi-participant calls.
Building a Video Conferencing App with Agora SDK in FlutterFlow
Video conferencing requires real-time media streaming, token-based authentication, and dynamic UI that adapts as participants join or leave. This tutorial uses Agora.io's Flutter SDK wrapped in a Custom Widget, Cloud Functions for secure token generation, and Firestore for room state management.
Prerequisites
- A FlutterFlow project with Firestore and Firebase Authentication configured
- An Agora.io account with an App ID and App Certificate from the Agora Console
- Firebase Blaze plan for Cloud Functions deployment
- Agora App Certificate stored in Cloud Function environment config (never on client)
Step-by-step guide
Set up the Firestore room model and Cloud Function for token generation
Set up the Firestore room model and Cloud Function for token generation
Create a rooms collection with fields: roomId (String, short joinable code), createdBy (Reference to users), participants (Array of user References), status (String: waiting/active/ended), createdAt (Timestamp). Deploy a Cloud Function generateAgoraToken(channelName, uid) that uses the Agora RTC Token Builder with your App Certificate to generate a temporary token valid for 1 hour. Store the App Certificate only in the Cloud Function environment config, never expose it to the client. The function returns the token and the Agora App ID.
Expected result: Firestore has a rooms collection for call state, and a Cloud Function securely generates Agora tokens without exposing credentials.
Build the Custom Widget that initializes Agora and renders video streams
Build the Custom Widget that initializes Agora and renders video streams
Create a Custom Widget named VideoCallView with parameters: agoraAppId (String), agoraToken (String), channelName (String), localUid (Int). Add the agora_rtc_engine package as a dependency. In initState, create an RtcEngine instance, enable video, set channel profile to communication, and join the channel with the token. Use onUserJoined and onUserOffline callbacks to maintain a local list of remote UIDs. Render the local video with AgoraVideoView(controller: VideoViewController) and each remote video with AgoraVideoView(controller: VideoViewController.remote(uid)). Arrange them in a layout that switches between full-screen for one participant and a GridView for multiple.
Expected result: The Custom Widget connects to the Agora channel and displays local and remote video feeds that update as participants join and leave.
Build the room creation and join flow with Firestore tracking
Build the room creation and join flow with Firestore tracking
On the Create Room page, generate a 6-character alphanumeric room code. On tap, create a Firestore room doc with the code, createdBy as the current user, participants array containing the current user, and status waiting. Then call the generateAgoraToken Cloud Function with the room code as channelName. Navigate to the Call page passing the token, appId, and channelName. On the Join Room page, provide a TextField for entering the room code. On tap, query rooms where roomId equals the entered code and status is not ended. If found, add the current user to the participants array, call generateAgoraToken, and navigate to the Call page.
Expected result: Users can create rooms with shareable codes or join existing rooms, with Firestore tracking all participants.
Add mute audio, mute video, camera switch, and end call controls
Add mute audio, mute video, camera switch, and end call controls
On the Call page, place the VideoCallView Custom Widget in the center. At the bottom, add a Row of circular IconButtons: microphone (toggle mute), camera (toggle video), camera switch (front/back), and red phone (end call). Create Page State booleans: isMuted and isVideoOff. On microphone tap, toggle isMuted and call the Custom Widget's muteLocalAudioStream method. On camera tap, toggle isVideoOff and call enableLocalVideo. On camera switch, call switchCamera. On end call, leave the Agora channel, remove the current user from the Firestore room participants array, and navigate back. If participants array is empty after removal, update room status to ended.
Expected result: Users can mute audio, disable video, switch cameras, and end the call with proper cleanup of Firestore room state.
Build the dynamic participant grid layout for multi-person calls
Build the dynamic participant grid layout for multi-person calls
Inside the VideoCallView Custom Widget, implement adaptive layout logic. For 1 remote participant: local video as small overlay in top-right corner (Positioned inside a Stack), remote video full-screen. For 2 remote participants: Column with two equal-height video views plus local overlay. For 3 or more: GridView with crossAxisCount of 2, each cell containing a remote video, with the local video occupying one cell. Display participant display names by querying Firestore user docs by UID. Show a subtle name label at the bottom of each video tile. Add a participant count badge in the AppBar.
Expected result: The video layout dynamically adapts as participants join or leave, showing an optimal arrangement for any group size.
Complete working example
1FIRESTORE DATA MODEL:2 rooms/{roomDocId}3 roomId: String (6-char code, e.g., 'XK4M9P')4 createdBy: Reference (users)5 participants: Array of References (users)6 status: String (waiting | active | ended)7 createdAt: Timestamp89CLOUD FUNCTION: generateAgoraToken10 Input: channelName (String), uid (Int)11 Process: RtcTokenBuilder.buildTokenWithUid(12 appId, appCertificate, channelName, uid,13 RtcRole.PUBLISHER, tokenExpireSeconds: 360014 )15 Returns: { token: '...', appId: '...' }1617CUSTOM WIDGET: VideoCallView18 Dependencies: agora_rtc_engine19 Parameters: agoraAppId, agoraToken, channelName, localUid20 Action Parameters: onCallEnded callback21 State: List<int> remoteUids, bool isMuted, bool isVideoOff2223PAGE: CreateRoomPage24 Column (center)25 Text "Start a Video Call" (headlineMedium)26 SizedBox(24)27 Button "Create Room"28 On Tap:29 1. Generate 6-char room code30 2. Create Firestore room doc31 3. Call generateAgoraToken Cloud Function32 4. Navigate to CallPage with token + appId + channelName3334PAGE: JoinRoomPage35 Column (center)36 Text "Join a Call" (headlineMedium)37 TextField (room code, maxLength: 6, uppercase)38 Button "Join"39 On Tap:40 1. Query room by roomId41 2. Add user to participants array42 3. Call generateAgoraToken43 4. Navigate to CallPage4445PAGE: CallPage46 Stack47 VideoCallView (fills screen)48 Positioned (bottom: 32, centerHorizontal)49 Row (mainAxisAlignment: spaceEvenly)50 CircleAvatar IconButton (mic / mic_off) → toggle mute51 CircleAvatar IconButton (videocam / videocam_off) → toggle video52 CircleAvatar IconButton (cameraswitch) → switch camera53 CircleAvatar IconButton (call_end, red) → leave + navigate back5455LAYOUT LOGIC (inside VideoCallView):56 if remoteUids.isEmpty → local video full screen + "Waiting..."57 if remoteUids.length == 1 → Stack: remote full + local small overlay58 if remoteUids.length == 2 → Column of 2 remotes + local overlay59 if remoteUids.length >= 3 → GridView(crossAxisCount:2) all videosCommon mistakes
Why it's a problem: Generating Agora tokens on the client side, exposing the App Certificate
How to avoid: Always generate tokens in a Cloud Function. Store the App Certificate in the function's environment config. Return only the temporary token to the client.
Why it's a problem: Creating a new RtcEngine instance every time the call page rebuilds
How to avoid: Initialize RtcEngine once in the Custom Widget's initState and dispose it in dispose(). Do not reinitialize on rebuilds.
Why it's a problem: Not cleaning up the Firestore room document when all participants leave
How to avoid: On end call, remove the user from participants. If the array becomes empty, set room status to ended. Run a scheduled Cloud Function to clean up rooms stuck in active status for more than 2 hours.
Best practices
- Store the Agora App Certificate exclusively in Cloud Function environment config, never in client code
- Generate tokens with a 1-hour expiry and refresh them if the call lasts longer
- Use Firestore real-time listeners on the room doc to detect when participants join or leave
- Dispose the Agora RtcEngine in the Custom Widget's dispose method to free camera and microphone resources
- Show a connection quality indicator using Agora's onNetworkQuality callback
- Add a lobby or waiting room screen before joining the actual call so users can check their camera and mic
- Limit maximum participants to 8-12 for communication mode to maintain call quality
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a video conferencing app in FlutterFlow using Agora.io. Show me how to create a Cloud Function for token generation, a Custom Widget for video rendering, and Firestore room management with join codes.
Create a video call page with a full-screen video area in the center. At the bottom, add a row of four circular buttons: microphone toggle, camera toggle, switch camera, and a red end call button. Add a small draggable picture-in-picture view in the top right corner.
Frequently asked questions
Do I need to pay for Agora to build a video conferencing app?
Agora offers 10,000 free minutes per month. For a small app or MVP, this is usually sufficient. Beyond that, pricing starts at $0.99 per 1,000 minutes for video calls.
Can I add screen sharing to the video call?
Yes. Agora supports screen sharing via the startScreenCapture method. Add a screen share IconButton that calls this method. The shared screen appears as a video stream to other participants.
How do I handle poor network conditions during a call?
Use Agora's onNetworkQuality callback to monitor connection quality. Display a signal strength indicator to the user. If quality degrades, you can programmatically lower video resolution using setVideoEncoderConfiguration.
Can I record video calls for later playback?
Yes. Agora offers cloud recording as a paid add-on. Trigger recording via Agora's REST API from a Cloud Function when the call starts. The recording is saved to your cloud storage bucket.
What is the maximum number of participants supported?
Agora communication mode supports up to 17 simultaneous video streams. For larger meetings, switch to Agora's live broadcasting mode where a few hosts broadcast and the audience watches without sending video.
What if I need help integrating complex video features like recording and breakout rooms?
RapidDev has integrated Agora, 100ms, and custom WebRTC solutions into FlutterFlow apps with features like recording, breakout rooms, virtual backgrounds, and participant management across hundreds of projects.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation