Skip to content
Client SDK

Flutter

Add calls, chat and a prebuilt call UI to your Flutter app. The SDK wraps signalling (ring/accept), media (LiveKit) and chat behind one small client — with a drop-in UI-Kit, or bring your own.

Install

terminal
1flutter pub add socrits livekit_client permission_handler

Add platform permissions (Android needs min SDK 21):

permissions
1<!-- android/app/src/main/AndroidManifest.xml -->2<uses-permission android:name="android.permission.INTERNET"/>3<uses-permission android:name="android.permission.CAMERA"/>4<uses-permission android:name="android.permission.RECORD_AUDIO"/>5<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>6<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>78<!-- ios/Runner/Info.plist -->9<key>NSCameraUsageDescription</key><string>Video calls</string>10<key>NSMicrophoneUsageDescription</key><string>Voice calls</string>

Initialize & connect

Create the client with your Socrits base URL and connect the signalling socket. The app authenticates with a session token your backend mints — see Server & tokens.

lib/socrits.dart
1import 'package:socrits/socrits.dart';2import 'package:livekit_client/livekit_client.dart' as lk;34final client = Socrits(baseUrl: 'https://cloud.unitythink.com');56// Connect the signalling socket. Your backend issues the user's session token7// (POST /v1/session with your sk_live_ key) — never embed the key in the app.8await client.connect(9  sessionEndpoint: 'https://your-backend.com/socrits/session', // returns { token }10  userId: 'alice',11  userName: 'Alice',12);13// (or, if you already have a token) await client.connect(userToken: token, userId: 'alice');

Set up callbacks

lib/socrits.dart
1// Wire these once, right after connect()2client.onIncomingCall = (IncomingCall call) {3  // show your ringing screen, then accept or reject:4  // final room = await client.accept(call);5  // client.reject(call);6};7client.onCallConnected = (lk.Room room, CallMode mode) {8  // both sides are in the room now — show the call screen (see below)9};10client.onCallRejected = (callId) { /* callee declined */ };11client.onCallEnded    = (reason) { /* other side hung up / left */ };1213// Offline ringing: register the device's FCM token so Socrits can push a ring14client.registerDevice(fcmToken, platform: 'android');

Make & receive a call

lib/socrits.dart
1// Caller: start a call. Returns a callId; the callee gets onIncomingCall.2final callId = await client.invite(3  toUserId: 'bob',4  mode: CallMode.video, // or CallMode.voice5);67// Callee: inside onIncomingCall8final room = await client.accept(call);   // joins the media room9// onCallConnected fires on BOTH sides with the connected lk.Room.

Option A — with the UI-Kit

SocritsCallView is a complete WhatsApp-style call screen: remote video full-screen, your camera in a PiP, a live timer, and Speaker / Flip / Video / Mute / End controls (voice calls show a pulsing avatar). Hand it the connected room and you’re done.

lib/call_screen.dart
1import 'package:socrits/socrits.dart';23client.onCallConnected = (room, mode) {4  Navigator.of(context).push(MaterialPageRoute(5    builder: (_) => Scaffold(6      body: SocritsCallView(7        room: room,8        title: 'Bob',9        initialCam: mode == CallMode.video,10        onHangUp: () => Navigator.of(context).maybePop(),11      ),12    ),13  ));14};

Widget parameters

  • room — the connected lk.Room from accept() / onCallConnected. Required.
  • title — name shown for the other party.
  • initialMic / initialCam — starting mic/camera state (default true).
  • onHangUp — called when the user taps End or the call ends; pop your screen here.

Option B — without the UI-Kit

Prefer your own design? accept() and onCallConnected give you the raw LiveKit Room. Control tracks and render video yourself with livekit_client:

lib/custom_call.dart
1// You get the raw LiveKit Room from accept() / onCallConnected — build any UI you want.2final lk.Room room = await client.accept(call);34// controls5await room.localParticipant?.setMicrophoneEnabled(true);6await room.localParticipant?.setCameraEnabled(mode == CallMode.video);78// render remote video9final listener = room.createListener();10listener.on<lk.TrackSubscribedEvent>((e) {11  if (e.track is lk.VideoTrack) {12    setState(() => remote = e.track as lk.VideoTrack);13  }14});15// in build():  if (remote != null) lk.VideoTrackRenderer(remote!)1617// end the call18await room.disconnect();

Chat

lib/chat.dart
1// 1:1 or group chat rides the same connection2client.onMessage = (ChatMessage m) { /* append to the thread */ };34await client.sendMessage(toUserId: 'bob', text: 'Hey!');5final history = await client.messageHistory(conversationId, limit: 30);6final convos  = await client.conversations();7client.setTyping(conversationId, true);8client.markRead(conversationId);
Tip:Offline ringing needs an FCM token registered via registerDevice(), plus — on Xiaomi/Oppo/Vivo — Autostart enabled and battery set to “No restrictions” for your app, or the OS blocks the wake-up push.