Skip to main content

Real-time events

The Flutter SDK exposes a persistent WebSocket to Relavoi's event stream through Relavoi.instance.events. All session and call events for your tenant land here within ~100 ms of the underlying telephony event. This is pure Dart — no platform channel involved.

Connecting​

Register listeners, then call connect():

import 'package:relavoi_flutter/relavoi_flutter.dart';

Future<void> startEventStream() async {
final events = Relavoi.instance.events;

events.on(EventType.callIncoming, (event) {
debugPrint('Incoming call on session ${event.payload['sessionId']}');
});

events.on(EventType.callAnswered, (event) => _updateUi(event));
events.on(EventType.callEnded, (event) => _triggerPostCall(event));

await events.connect();
}

connect() returns a Future; disconnect() is synchronous. Both are safe to call more than once.

Event types​

EventType enumerates the streamed events. Each maps to a wire string:

EventTypeWire string
sessionCreatedsession.created
sessionExpiredsession.expired
callIncomingcall.incoming
callAnsweredcall.answered
callEndedcall.ended
callFailedcall.failed
smsSentsms.sent
unknown(forward-compat fallback for unrecognized types)

RelavoiEvent​

Every listener receives a RelavoiEvent:

class RelavoiEvent {
final EventType type; // parsed enum
final String rawType; // original wire string, e.g. "call.ended"
final Map<String, dynamic> payload;
final DateTime timestamp;
}

Read fields such as sessionId off the untyped payload map. rawType preserves the original string even when type resolves to EventType.unknown.

Listening to a single type​

on binds a callback to one EventType:

Relavoi.instance.events.on(EventType.sessionExpired, (event) {
debugPrint('Session ${event.payload['sessionId']} expired');
});

Listening to everything​

onAny fires for every event regardless of type — handy for logging or a central dispatcher:

Relavoi.instance.events.onAny((event) {
debugPrint('[${event.timestamp}] ${event.rawType}: ${event.payload}');
});

Auto-reconnect​

The SDK reconnects automatically with backoff on transport failures, governed by the autoReconnectWebSocket and maxWebSocketReconnectAttempts fields of RelavoiConfig. Check the current state synchronously with isConnected:

if (!Relavoi.instance.events.isConnected) {
await Relavoi.instance.events.connect();
}

Removing listeners​

removeAllListeners clears every registered callback in one call:

Relavoi.instance.events.removeAllListeners();

Disconnecting​

Relavoi.instance.events.disconnect();

Call this on user logout. Combined with removeAllListeners, it fully detaches the stream.

:::tip Foreground-only listening If your app does not need events while backgrounded, gate connect() / disconnect() on the app lifecycle (WidgetsBindingObserver.didChangeAppLifecycleState). This saves battery on long sessions. :::