Skip to main content

Flutter SDK — metered_realtime

The official Flutter / Dart SDK for Metered Realtime Messaging. Speaks the same wire protocol as the JavaScript SDK and the raw WebSocket path — framing, reconnection, ack correlation, presence diffing, perfect-negotiation WebRTC, and TURN credential injection are all done for you, behind an idiomatic Dart API.

Published on pub.dev as metered_realtime.

pubspec.yaml
dependencies:
metered_realtime: ^0.1.0
flutter_webrtc: ^1.4.1

WebRTC is provided by flutter_webrtc, so the SDK runs everywhere flutter_webrtc does: Android, iOS, web, macOS, Windows, and Linux. The signalling layer is pure Dart with a single injectable seam for testing.

Pick your path

The SDK ships two public classes. Pick by what you're building:

You're building…UseWhy
WebRTC video / voice call, screen share, peer-to-peer gameMeteredPeerJoins a channel, discovers peers via presence, manages each RTCPeerConnection for you, fans media out automatically, recovers ICE on network changes
Live chat with presence, classroom roster, multiplayer lobbyMeteredPeerThe onPeerJoined / onPeerLeft streams drive your UI; peer.send(data) broadcasts or directs
IoT telemetry, MQTT-style pub/sub, AI agent message bus, collaborative cursorsSignallingClientPub/sub + directed messages without the WebRTC overhead. Smaller, simpler, multiple channels per connection

Not sure? Start with MeteredPeer. Drop down to SignallingClient later if you find you don't need the channel-+-peer model.

Events are Dart Streams

The biggest difference from the JavaScript SDK: where JS uses an EventEmitter (peer.on('peer-joined', …)), the Flutter SDK exposes idiomatic broadcast Stream getters you .listen() to.

// JavaScript:  peer.on('peer-joined', ({ peer: remote }) => …)
// Flutter:
peer.onPeerJoined.listen((remote) { … });

Each event maps to a Stream:

ConceptJavaScriptFlutter
Peer joinedpeer.on('peer-joined', …)peer.onPeerJoined.listen((remote) {…})
Inbound datapeer.on('data', …)peer.onData.listen((MeteredData m) {…})
State changepeer.on('state-change', …)peer.stateChanges.listen((c) {…})
Remote streamremote.on('stream-added', …)remote.onStreamAdded.listen((ev) {…})

Listeners returned by .listen() are StreamSubscriptions — cancel them in your widget's dispose() just like any other Dart stream. The SDK closes its controllers when you close() the peer.

What the SDK does for you

These are the things you'd write yourself if you used flutter_webrtc + a raw WebSocket — the SDK handles them:

  • Framing + ack correlation — every subscribe, publish, send returns a Future that completes when the server acks
  • Auto-reconnect with exponential backoff, jitter, and close-code-aware behaviour (terminal codes stop retrying, rate-limit codes slow-backoff)
  • Token refresh — your tokenProvider is re-invoked on every reconnect so JWT-rotated TURN creds + permissions land automatically
  • Inactivity watchdog — if the server stops sending frames, the SDK closes-and-reconnects instead of leaving you on a half-dead socket
  • Presence diff — translates raw presence events into per-peer onPeerJoined / onPeerLeft
  • Perfect negotiation — automatic SDP + ICE handling with a tie-breaker that prevents glare (including the native-platform rollback that browsers do implicitly)
  • ICE-restart ladder — 9 retries spanning ~121 s for WebRTC-level recovery (Wi-Fi → cellular roam, TURN failover)
  • Channel-level reconcile — when the signalling socket drops, your RemotePeer references survive the reconnect; only the underlying RTCPeerConnection is swapped (see Reconnect Best Practices)

What your code still owns

The SDK does not make decisions on your behalf for things that affect UX or billing. You still own:

  • Platform permissions — camera / microphone strings in the Android manifest + iOS Info.plist. See Platform Setup
  • Renderingflutter_webrtc gives you RTCVideoRenderer + RTCVideoView; you decide the layout. See WebRTC Video Call
  • Auth mode + token minting — see Authentication
  • Reconnect UI — the SDK fires stateChanges; how (or whether) you surface a "reconnecting…" banner is up to you. See Reconnect Best Practices
  • Renderer re-bind on reconnect — after a reconnect the SDK surfaces a fresh MediaStream object (same id). Re-assign renderer.srcObject on every onStreamAdded

Where to start

If you want to…Go here
Get something running in 10 minutesGetting Started
Configure Android / iOS / webPlatform Setup
Build a WebRTC video callGuide: WebRTC Video Call
Build chat / presence / a lobbyGuide: Presence & Chat
Open a low-latency P2P data channelGuide: Data Channels & Low Latency
Handle reconnects properly (production-grade)Guide: Reconnect Best Practices
Port from raw flutter_webrtcMigration: from flutter_webrtc
Look up a method / streamAPI Reference: MeteredPeer · SignallingClient

Same protocol, every platform

A Flutter peer, a browser peer (JS SDK), and a raw-WebSocket client can all be peers in the same channel — the wire protocol is identical byte-for-byte. Build your call on Flutter and let web users join from the browser with no extra work.