Does LiveKit have a Flutter SDK?
Yes — livekit_client is LiveKit's official Flutter SDK, supporting iOS, Android, and Web from a single Flutter codebase, with full room, track, and data-channel APIs.
How do I build a video calling app with LiveKit?
Stand up a LiveKit server (self-hosted or LiveKit Cloud), issue signed JWT access tokens per participant from your backend, then use the Flutter or React Native SDK to connect to a Room, publish local camera/microphone tracks, and render subscribed remote participant tracks.
How to Build a Video Calling App with LiveKit (Flutter & React Native) 2026
LiveKit ships official, first-class SDKs for both Flutter and React Native — this guide walks through connecting to a room, publishing local video/audio, and rendering remote participants on both platforms, plus the self-hosted vs LiveKit Cloud deployment decision.
Beyond the basic connect-and-publish flow, we also cover why LiveKit beats raw WebRTC for most teams, the rooms/participants/tracks data model, reconnection handling for real mobile networks, adding call recording, building a multi-participant video grid UI, testing strategies, common integration mistakes, and what changes once you need more than a handful of concurrent rooms.
livekit_client
Flutter package
@livekit/react-native
React Native package
~40 lines
To first working call
Cloud or Self-Host
Same client code either way
Why LiveKit Instead of Raw WebRTC
Building on raw WebRTC directly means implementing your own signalling server, handling ICE candidate exchange manually, negotiating SDP offers/answers, and building an SFU (or paying for one) once you need more than a handful of participants in a call. LiveKit collapses all of that into a single SDK call to connect to a room — the signalling, ICE negotiation, and SFU media routing are handled by the LiveKit server, and the client SDK exposes a much higher-level API (rooms, participants, tracks) instead of raw peer connections. For a mobile team without deep WebRTC internals experience, this is the difference between shipping a video feature in days versus months, which is why LiveKit has become the default recommendation for new video calling apps rather than hand-rolled WebRTC.
Step 1: Set Up a LiveKit Server
You need a running LiveKit server before any client code will connect to anything. For prototyping, spin up a free LiveKit Cloud project in minutes at livekit.io — you get a WebSocket URL and API key/secret immediately. For production, self-host via Docker: docker run --rm -p 7880:7880 -p 7881:7881 livekit/livekit-server --dev gets a local dev server running in seconds; production deployment additionally needs a TURN server (coturn) and Redis for multi-node setups.
Understanding Rooms, Participants, and Tracks
LiveKit's data model has three core concepts worth understanding before writing any code. A Room is the call itself — every participant connects to a specific room identified by name, and LiveKit's SFU handles routing media between everyone in that room. A Participant represents one connected user (or bot, for a voice AI agent), each with their own identity and permissions granted at token-issue time. A Track is a single media stream — one participant might publish a camera video track, a microphone audio track, and a screen-share track simultaneously, each independently subscribable by other participants. Almost everything you build sits on top of subscribing and unsubscribing to tracks as participants join, leave, and toggle their camera or microphone — the SDK events you wire up are fundamentally track lifecycle events, not call-level events.
Step 2: Generate Access Tokens (Backend)
Never embed your LiveKit API secret in the mobile app. Your backend issues a short-lived, signed JWT access token per participant, encoding which room they may join and their publish/subscribe permissions. Set a short expiry (minutes, not hours) on tokens issued for one-time calls, and always generate a fresh token per session rather than reusing one across multiple calls — a leaked long-lived token is effectively a permanent backdoor into whichever room it was scoped to.
# Python backend (livekit-api)
from livekit import api
token = api.AccessToken(api_key, api_secret) \
.with_identity("user-123") \
.with_grants(api.VideoGrants(room_join=True, room="my-room")) \
.to_jwt()Step 3: Connect and Publish — Flutter
// pubspec.yaml: livekit_client: ^2.x
import 'package:livekit_client/livekit_client.dart';
final room = Room();
await room.connect(
'wss://your-project.livekit.cloud',
accessToken,
);
// Publish local camera + microphone
final localParticipant = room.localParticipant!;
await localParticipant.setCameraEnabled(true);
await localParticipant.setMicrophoneEnabled(true);
// Render remote participants
room.events.on<TrackSubscribedEvent>((event) {
final videoTrack = event.track as VideoTrack;
// Attach videoTrack to a VideoTrackRenderer widget
});Step 4: Connect and Publish — React Native
// npm install @livekit/react-native @livekit/react-native-webrtc
import { Room, RoomEvent } from 'livekit-client';
const room = new Room();
await room.connect('wss://your-project.livekit.cloud', accessToken);
await room.localParticipant.setCameraEnabled(true);
await room.localParticipant.setMicrophoneEnabled(true);
room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
if (track.kind === 'video') {
// Render <VideoTrack track={track} /> in your component tree
}
});Step 5: Rendering a Multi-Participant Video Grid
Once more than one remote participant is in the room, the UI layer — not the LiveKit SDK — is what determines whether the call feels polished. Both SDKs give you a list of currently subscribed video tracks via room events; your job is to lay them out sensibly as participants join and leave. A common pattern is a responsive grid that reflows based on participant count (1 participant fills the screen, 2 split evenly, 3–4 form a 2x2 grid, beyond that scroll or paginate), with the active speaker highlighted using LiveKit's built-in active-speaker detection rather than a custom audio-level implementation.
room.events.on<ActiveSpeakersChangedEvent>((event) {
final activeSpeakerIds = event.speakers.map((p) => p.sid).toSet();
// Re-render grid, highlighting participants in activeSpeakerIds
});Handling Reconnection on Real Mobile Networks
A demo on office WiFi never exposes what happens when a user walks out of coverage or switches from WiFi to cellular mid-call — both SDKs handle short network blips with automatic reconnection, but your app needs to surface that state to the user rather than silently freezing on their last received frame. Subscribe to the room's connection-state events and show an explicit "Reconnecting..." indicator when the state changes, and a clear "Call ended" state if reconnection ultimately fails after LiveKit's internal retry window expires — the single most common complaint in early-stage LiveKit apps is users not knowing whether a frozen video means the call dropped or the network is just slow.
Adding Call Recording
Recording is a server-side concern, not a client SDK feature — you trigger it via LiveKit's Egress API, which composites the room's audio/video into an output file (or live stream) without any participant-side code changes. A typical flow: your backend calls the Egress API with a room name and output destination (S3-compatible storage is the common choice) when recording should start, and stops it the same way when the call ends. Because this happens server-side, recording works identically whether you're on LiveKit Cloud or self-hosted — only the storage credentials and egress worker configuration differ between the two.
Testing Strategies
Test on real devices over real networks before trusting a simulator or emulator test — camera/microphone permission flows, background/foreground transitions, and network handoffs all behave differently on physical hardware. For automated testing, LiveKit's server SDKs let you spin up headless bot participants that join a room and publish synthetic audio/video, which is useful for load-testing room capacity and verifying server-side logic (like egress triggers) without needing a human on a real call for every test run. Manually test the specific transition from WiFi to cellular mid-call at least once before shipping — it's the single most common real-world failure mode that a controlled test environment won't surface on its own.
Common Integration Mistakes
- Generating access tokens client-side instead of on a trusted backend — this exposes your API secret and lets any client mint tokens for any room
- Not handling the case where camera/microphone permissions are denied — the app should degrade to audio-only or a clear error state, not crash or hang silently
- Forgetting to dispose of the Room object and release camera/mic resources when a screen unmounts, which leaks resources and can leave the camera indicator lit after the user has left the call screen
- Assuming LiveKit Cloud and self-hosted behave identically under all conditions — self-hosted deployments need their own TURN server configured correctly, or calls between users on restrictive networks will fail silently
Scaling Beyond a Single Room
Everything above works identically whether your app runs 10 rooms or 10,000 — LiveKit's SFU architecture scales horizontally by adding more media server nodes behind the same signalling layer, and your client code doesn't need to know how many nodes exist. What does change at scale is operational: monitor per-node participant count so you can add capacity before a node saturates, and if you're self-hosting, make sure your Redis-backed multi-node configuration is in place before you need it — retrofitting multi-node support under production load is far riskier than provisioning for it from the start.
Step 5: Self-Hosted vs LiveKit Cloud — the Only Thing That Changes Is the URL
This is LiveKit's biggest practical advantage: the client code above is identical whether you connect to LiveKit Cloud or your own self-hosted server — only the WebSocket URL changes. This means you can prototype on LiveKit Cloud, validate your product, and migrate to self-hosted infrastructure later without rewriting any client-side integration code.
- LiveKit Cloud: fastest to start, $0.004/audio track-min and $0.006–$0.024/video track-min, zero ops
- Self-hosted: ~$60/month handles ~200 concurrent users, no per-minute fee, you own the data path
- Migration between the two requires no client code changes — just a URL and token-issuing endpoint change
FAQ
Do I need a separate signalling server with LiveKit?
No — LiveKit's server handles signalling, SFU media routing, and room state internally. You only need your own backend for issuing access tokens (a JWT-signing endpoint), not for signalling.
Can I add screen sharing with the same SDKs?
Yes — both livekit_client (Flutter) and the React Native SDK expose a setScreenShareEnabled() style API that publishes a screen-capture track the same way camera/microphone tracks are published.
How do I add a voice AI agent to a LiveKit room?
Use the LiveKit Agents SDK (Python) to build a server-side participant that joins the room like any other client, subscribes to the human participant's audio, runs it through an STT→LLM→TTS pipeline, and publishes synthesized speech back as its own audio track.
How do I record a LiveKit call?
Trigger LiveKit's server-side Egress API from your backend when the call starts, pointing it at S3-compatible storage or a live streaming destination — recording is not a client SDK feature and requires no changes to your Flutter or React Native code.
What happens to a call when a user's network drops briefly?
Both SDKs handle short network interruptions with automatic reconnection logic, but your app should surface a 'Reconnecting...' UI state during that window rather than leaving the user staring at a frozen frame with no explanation.
Can I test LiveKit apps without two physical devices?
Yes — LiveKit's server SDKs support headless bot participants for automated testing and load testing, though you should still validate the real device experience (permissions, background/foreground transitions, network handoffs) manually before shipping.
Does LiveKit work well on slow or unreliable mobile networks?
Yes — LiveKit implements adaptive bitrate and simulcast, automatically lowering video quality for participants on constrained connections rather than dropping the call, and both SDKs expose connection-quality events so your UI can show a visual indicator when a participant's network is degraded.
How much does it cost to run a LiveKit app in production?
Self-hosted, a single server handling roughly 200 concurrent users costs about $60/month in infrastructure with no per-minute fee. On LiveKit Cloud, cost scales with usage at $0.004 per audio track-minute and $0.006–$0.024 per video track-minute — budget accordingly based on expected concurrent usage and call duration.
Do I need to handle audio and video separately in my UI code?
Conceptually yes — they arrive as separate tracks and can be enabled/disabled independently (a participant might have audio on with camera off), so build your UI to handle each track type's presence and absence independently rather than assuming they always travel together.
Choosing Between Flutter and React Native for a LiveKit App
Both SDKs are first-class and actively maintained, so the choice usually comes down to your team's existing expertise rather than any meaningful capability gap between them. Flutter's advantage is a single codebase that also targets Web with reasonable effort, which matters if a browser client is on your roadmap; React Native's advantage is a larger existing ecosystem of general-purpose libraries and a shallower learning curve for teams already fluent in JavaScript/TypeScript. Neither SDK is meaningfully behind the other in LiveKit feature coverage as of this writing — pick based on your team, not based on a perceived technical advantage of one over the other.
Summary
A working 1:1 video call with LiveKit is genuinely a same-day project once you understand the rooms/participants/tracks model — the SDKs handle the hard WebRTC internals for you. Where real engineering time goes is everything around that core loop: reconnection UX, recording, a video grid that scales gracefully, and testing on real devices over real networks rather than trusting a clean office WiFi demo.
Need Help Shipping Your LiveKit App?
CelloIP builds and operates production LiveKit apps on Flutter and React Native — from MVP to self-hosted scale.