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.2.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
  • Rendering — flutter_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.