Vanira Flutter SDK (vanira_sdk)
Production-oriented Flutter client for Vanira voice sessions over WebRTC. It aligns with the TypeScript SDK’s signaling, DataChannel protocol, and session flow—implemented with flutter_webrtc.
Features
- WebRTC voice: microphone,
RTCPeerConnection, control DataChannel, SDP offer/answer via HTTP signaling. createCall+ connect: passagentIdandapiKeyonly; optionalserverUrl/callIdif you create calls yourself.- ICE: fetches ephemeral servers from Vanira when
apiKeyis set; normalizesusername/credentialfor Android (avoids nativeusername == nullcrashes). - Layered layout: protocol, transport, observability, reconnect (mirrors the TS SDK structure conceptually).
Requirements
- Dart ^3.11.1
- Flutter ( mobile: Android / iOS )
- Microphone permission (see below)
Install
Path (monorepo / local dev):
dependencies:
vanira_sdk:
path: ../vanira-sdk/flutter-sdk # adjust to your layout
Published (when available on pub.flutter-io.cn):
flutter pub add vanira_sdk
Transitive deps used by the package: flutter_webrtc, http, permission_handler (versions are defined in this package’s pubspec.yaml).
App setup
1. Initialize WebRTC (recommended)
VaniraSession.connect() calls ensureFlutterWebRtcInitialized() first (same Android audio options as below), so a minimal main() that only uses the SDK for WebRTC can omit this block. Keep an explicit WebRTC.initialize in main() if you use flutter_webrtc elsewhere before connecting, or you want initialization as early as possible.
Call WebRTC.initialize once before creating a session—especially on Android—to reduce native edge cases:
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'dart:io' show Platform;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final options = <String, dynamic>{};
if (Platform.isAndroid) {
options['androidAudioConfiguration'] =
AndroidAudioConfiguration.communication.toMap();
options['bypassVoiceProcessing'] = true; // optional; helps some devices
}
await WebRTC.initialize(options: options);
runApp(const MyApp());
}
2. Microphone permission
The SDK requests mic access via permission_handler (MediaPipeline). Ensure Android RECORD_AUDIO and iOS NSMicrophoneUsageDescription are configured.
Usage
Minimal: API key + agent ID
You do not need serverUrl if apiKey is set—the SDK calls /calls/create, then connects to the returned worker URL.
import 'package:vanira_sdk/vanira_sdk.dart';
final session = VaniraSession(
VaniraSessionConfig(
agentId: 'your-agent-id',
apiKey: 'your-api-key',
// backendUrl defaults to https://api.vanira.io
onConnected: () => print('connected'),
onDisconnected: () => print('disconnected'),
onError: (e) => print('error: $e'),
onTranscription: (text, isFinal) => print('$text (final=$isFinal)'),
onClientToolCall: (tool) => print('tool: $tool'),
),
);
await session.connect();
Explicit createCall
Same as above flow-wise; useful if you want a clear two-step API:
await session.createCall(); // sets worker URL + call id, then connects
Bring your own serverUrl
If you already have a worker URL and call id:
await session.connect(
serverUrl: 'https://worker.example.com/...',
callId: 'call_…',
);
Disconnect
await session.disconnect();
Optional: logger, reconnect, ICE override
logger:VaniraLogger—(level, message, [context])to integrate with your logging.reconnect:ReconnectConfig— exponential backoff reconnection;nulldisables.iceServers: static ICE list; if set, replaces backend ICE fetch for that session.
Package layout
lib/
vanira_sdk.dart # export entrypoint
src/
vanira_session.dart # VaniraSession, VaniraSessionConfig, ReconnectConfig
protocol/ # events, state machine, kProtocolVersion
transport/ # ICE, signaling, media pipeline
observability/ # logger
reconnect/ # reconnect manager
Protocol overview
Outbound HTTP signaling includes protocol_version (see kProtocolVersion in code, aligned with TS PROTOCOL_VERSION). The SignalingClient builds the same JSON shape as the npm SDK’s buildSignalingPayload.
Server → client (DataChannel)
event |
Notes |
|---|---|
clearAudio |
No-op client-side; native stack handles audio. |
transcription |
onTranscription(text, isFinal). |
mark |
Logged; extend as needed. |
client_tool_call |
Immediate client_tool_ack when tool_call_id is present; then onClientToolCall. |
Client → server
| API | Event |
|---|---|
sendToolResult(callId, result) |
client_tool_result |
sendToolAck(toolCallId) |
client_tool_ack |
sendContextUpdate(map) |
client_context_update |
sendActionTrigger(name, [data]) |
client_action_trigger |
triggerActionInterrupt() |
action_interrupt |
sendEvent(name, [data]) |
custom / generic |
TypeScript ↔ Dart WebRTC mapping (mental model)
| TS / browser | Dart (flutter_webrtc) |
|---|---|
new RTCPeerConnection(config) |
await createPeerConnection(config, {}) |
getUserMedia |
navigator.mediaDevices.getUserMedia(...) |
pc.createDataChannel(...) |
await _pc.createDataChannel(..., RTCDataChannelInit()) |
createOffer / setLocalDescription / setRemoteDescription |
same async methods on RTCPeerConnection |
pc.onconnectionstatechange = … |
_pc.onConnectionState = (state) { ... } (setter + callback, not Stream.listen unless the plugin exposes streams in your version) |
fetch |
package:http (SignalingClient inside the SDK) |
Differences vs browser / React Native SDK
| Topic | Browser / RN | Flutter |
|---|---|---|
| Remote audio | <audio> / RN native |
Routed by flutter_webrtc (no Audio widget) |
playedStream |
Browser can send after playback ends | Not sent (no onended equivalent; backend should not block on it) |
| Screen share | Browser / N/A on RN | Not supported here |
registerGlobals() |
RN / react-native-webrtc |
Not used; use WebRTC.initialize for Flutter |
| Distribution | npm / CDN | pub.flutter-io.cn or path / git dependency |
Security
- Treat
apiKeyas a secret: avoid committing it (use--dart-define, CI secrets, or a short-lived token from your backend for production). - All Vanira HTTP calls use TLS against
backendUrl(defaulthttps://api.vanira.io).
Development
cd flutter-sdk
dart pub get
dart analyze lib
To run the in-repo smoke app, use vanira_flutter_test in this monorepo (path dependency on vanira_sdk).
License
See LICENSE.