flutter_watch_bridge 0.1.0
flutter_watch_bridge: ^0.1.0 copied to clipboard
One API for messages, state sync and file transfer between a Flutter phone app and its paired Apple Watch or Wear OS watch, with pairing and reachability streams.

One Dart API for talking to a paired Apple Watch (WatchConnectivity) or Wear OS watch (Wearable Data Layer) from a Flutter phone app.
Flutter can't draw the watch UI, so you write that natively (SwiftUI / Compose for Wear OS). This package gives the phone a single API and ships a Swift package and a Kotlin module so the watch side speaks the same protocol.
Platform support #
| Android | iOS | Web | Windows | Linux | macOS |
|---|---|---|---|---|---|
| Wear OS | Apple Watch | fallback | fallback | fallback | fallback |
Windows, Linux, macOS and web have no OS API for pairing with a watch. There
status.supported is false and sends throw WatchUnsupportedException, so an
app that targets those platforms still compiles and runs.
| Apple Watch | Wear OS | |
|---|---|---|
| Live messages + replies | sendMessage |
MessageClient |
| Latest state sync | application context | DataClient item |
| Guaranteed queue | transferUserInfo |
DataClient items, consumed once |
| File transfer | transferFile |
ChannelClient |
| Pairing / reachability | isPaired, isReachable, ... |
connected nodes + capability |
How it fits together #
Try it without a watch #
The example app has a Simulated watch switch that wires the phone to an
in-memory watch, so you can send messages, sync state and queue items
immediately. Run it scripted with flutter run --dart-define=DEMO=true.

Quick start #
final bridge = WatchBridge.instance;
await bridge.initialize(); // call early, then attach listeners
bridge.status.listen((s) => print('reachable: ${s.reachable}'));
// Live message, optionally waiting for an answer
await bridge.sendMessage({'hello': 'watch'});
final reply = await bridge.request({'ping': 1}, timeout: const Duration(seconds: 5));
// Answer requests coming from the watch
bridge.setRequestHandler((request) => {'pong': request.data['ping']});
// State: the watch always ends up with the latest value
await bridge.syncState('settings', {'units': 'metric'});
bridge.stateStream('workout').listen((s) => print(s));
// Guaranteed, ordered delivery (survives offline + restarts)
await bridge.enqueue({'event': 'login'});
// Live if reachable, queued otherwise
final outcome = await bridge.sendReliable({'event': 'sync'});
// Files, with progress
final transfer = await bridge.sendFile(File(path), metadata: {'name': 'a.png'});
transfer.progress.listen((p) => print(p.fraction));
await transfer.done;
bridge.incomingFiles.listen((f) => print(f.path));
Payloads are Map<String, Object?> holding null, bool, num, String,
List, nested maps and Uint8List. They are limited to about 90 KB; use
sendFile for more.
Choosing a delivery method #
| Need | Use |
|---|---|
| Instant, watch is open | sendMessage / request (throws WatchNotReachableException) |
| "What is the current X" | syncState (older values are replaced) |
| Every event, in order | enqueue |
| Don't care which of the two | sendReliable |
| Large data | sendFile |
Setup #
- Add the dependency, then run
dart run flutter_watch_bridge:doctorto check the native setup. - Add a watch app: watchOS / Wear OS.
- Use
WatchBridge.instanceon the phone and the helper in watch/apple or watch/android on the watch.
See doc/platform_differences.md for the places where the two platforms behave differently, and doc/migration.md if you come from another package.
Testing without a watch in code #
import 'package:flutter_watch_bridge/testing.dart';
final link = SimulatedWatchLink();
final phone = WatchBridge.forPlatform(link.phone);
final watch = WatchBridge.forPlatform(link.watch);
link.reachable = false; // state and queue are held, messages throw
The example app has a "Simulated watch" switch that uses the same thing.
Limitations #
- Real communication works on Android and iOS phones only. Windows, Linux, macOS and web have no OS API for
pairing with an Apple Watch or Wear OS watch, so there
status.supportedis false and sends throwWatchUnsupportedException. Apps that target those platforms still compile and run. - The watch UI is native. Sample sources are in
example/watch_samples. - Wear OS messages are only received while the app process runs; state and
queued items are picked up on the next
initialize(). - Not a Health/sensor API, and no complications or tiles.
