EthioConnect for Flutter (Dart)
The commbaas package is the native EthioConnect SDK for Flutter — iOS, Android, and Flutter web. It mirrors the JavaScript (@comm-baas/client-sdk), Swift (CommBaaS), and Kotlin (com.commbaas:sdk) SDKs layer by layer: typed models, resource facades, and a signaling connection with the disconnected -> connecting -> connected -> resuming lifecycle. See ADR 0022 — Native Dart SDK and ADR 0017 — Mobile SDK Architecture.
The SDK lives in this repository at sdks/dart/commbaas and is not yet published to pub.dev (ADR 0022 §7). Until it is, consume it as a git dependency:
dependencies:
commbaas:
git:
url: https://github.com/comm-baas/communication_baas.git
path: sdks/dart/commbaas
or, from a checkout of this repository, as a path dependency:
dependencies:
commbaas:
path: ../communication_baas/sdks/dart/commbaas
Quick start
import 'package:commbaas/commbaas.dart';
final client = CommBaasClient(
baseUrl: 'https://api.example.com/v1',
// Called whenever the SDK needs a fresh JWT. Your app owns token
// storage and refresh; the tenant API key never ships in the app.
tokenProvider: () async => myBackend.fetchEthioConnectToken(),
);
final room = await client.joinRoom('room-uuid');
room.events.listen((event) {
switch (event) {
case ParticipantJoined(:final participant):
print('${participant.displayName} joined');
case ActiveSpeakersChanged(:final speakers):
print('${speakers.length} people speaking');
case Disconnected(:final reason):
print('disconnected: $reason');
default:
break;
}
});
await room.setMicEnabled(true);
// ...
await room.leave();
joinRoom() orchestrates the entire flow for you: the REST join call, the signaling WebSocket handshake, the LiveKit media connection (with server-provisioned ICE/TURN), and QoS telemetry — and returns a single RoomHandle.
What the SDK provides
| Layer | What you get |
|---|---|
| Typed models | Hand-written Dart classes for every REST resource and signaling message, taken verbatim from the platform contracts. No code generation. |
| Resource facades | client.auth, client.rooms, client.presence, client.analytics, client.billing, client.turn — same naming as the JS SDK. |
joinRoom() / RoomHandle | One call joins the room; the handle exposes events (a broadcast Stream of sealed RoomEvent subclasses), setMicEnabled / setCameraEnabled, moderation methods, and the roster. |
SignalingConnection | Heartbeat, session_id/last_seq tracking, and automatic session resume with 1 s / 2 s / 4 s backoff (3 attempts). Managed for you by joinRoom(). |
| Typed exceptions | AuthenticationException, AuthorizationException, NotFoundException, ValidationException, RateLimitException, NetworkException, SignalingException — mirroring the JS error hierarchy. |
Dependencies are exactly http, web_socket_channel, and livekit_client. The SDK never stores tokens and has no storage dependency — token supply is the async tokenProvider closure, and secure storage stays in your app.
Architecture
EthioConnect uses the same trust-boundary split on Flutter as on every other platform:
Your Backend Flutter App (iOS / Android / web)
============ ==================================
Tenant API key commbaas SDK, built on:
| http (REST)
| POST /v1/auth/token web_socket_channel (signaling)
v livekit_client (media)
EthioConnect API -- short-lived JWT --> | tokenProvider closure
v
EthioConnect API
|
| WebSocket signaling
v
LiveKit SFU (media)
- Your backend holds the tenant API key and mints short-lived JWTs for end users via
POST /v1/auth/token(see Authentication). The API key never ships inside the Flutter app — and by design the client SDK has nomintTokenmethod. - The Flutter app hands the SDK a
tokenProviderclosure; the SDK attachesAuthorization: Bearer <JWT>to every REST call and never caches the token beyond immediate use. client.joinRoom()obtains a signaling ticket for the WebSocket and a LiveKit token/URL for media, and connects both.- TURN relay is provisioned server-side (Coturn); the join grant already includes the
ice_serversthe media connection needs.
Under the hood
The SDK is a thin, typed layer over the platform's public REST and WebSocket contracts (contracts/openapi/*, contracts/ws-protocol/signaling.json). Each guide page in this section documents the SDK API first, then keeps the raw wire protocol in an "Under the hood" section — per ADR 0019 §4, the former integration-guide material now documents what the SDK does on the wire. In raw terms, joinRoom() performs:
// 1. POST /v1/rooms/{id}/join with Bearer auth -> join grant:
// { session_id, livekit_token, livekit_url,
// signal_url, signal_ticket, ice_servers }
// 2. WebSocketChannel.connect('<signal_url>?ticket=<signal_ticket>')
// and wait for the `hello` frame.
// 3. livekit Room().connect(livekit_url, livekit_token) with ice_servers.
// 4. Periodic QoS batches to POST /v1/analytics/qos.
For endpoints that do not yet have a facade method, client.httpClient exposes authenticated get/post/put/delete against the same base URL.
Guide contents
- Authentication — the
tokenProviderclosure, end-user auth viaclient.auth, secure storage. - Rooms —
client.rooms,joinRoom(),RoomHandle, moderation, recordings. - Signaling —
SignalingConnection, heartbeat, session resume, and the wire protocol. - Media — mic/camera control, the LiveKit escape hatch, TURN.
- Presence & Notifications —
client.presencequeries and event delivery.
Related
- ADR 0022 — Native Dart SDK (
commbaas) - ADR 0017 — Mobile SDK Architecture
- ADR 0019 — EthioConnect Branding & Dart Docs
- JavaScript SDK — the reference implementation the Dart SDK mirrors.