voo_wear 0.1.2
voo_wear: ^0.1.2 copied to clipboard
Flutter SDK for building Wear OS apps. Round-screen-aware Scaffold, ambient mode, rotary input, and the building blocks for great smartwatch UX in Dart.
voo_wear #
Flutter SDK for building Wear OS apps in Dart.
voo_wear is the watch-side complement to voo_watch.
Where voo_watch is the phone↔watch bridge that works for both Apple Watch
and Wear OS, voo_wear lets you write the actual Wear OS UI in Flutter — no
Compose, no Kotlin.
Apple Watch note. Flutter cannot run on watchOS; that's an Apple constraint, not a project decision. For Apple Watch you still write a tiny SwiftUI app — or use
voo_watch_uito define one Dart tree that renders as native SwiftUI on Apple Watch and Flutter on Wear OS.
What you get #
VooWearScaffold— opinionated app shell for tiny screens (status bar, time tray, round-aware safe area, ambient dimming).- Round-screen detection —
VooWearShape.of(context)returnsroundorrectangular. Branch your layout once, render correctly everywhere. - Ambient mode —
VooWearAmbientBuilderrebuilds when the watch dims so you can swap to a low-color, black-background layout. - Rotary input —
VooRotaryListenerexposes bezel/crown rotation as a Dart stream. - Round safe areas —
VooWearRoundSafeAreainsets your content past the corner mask. - Native plugin — wraps
Configuration.isScreenRound,AmbientLifecycleObserver, andMotionEvent.AXIS_SCROLLfor you. - Test harness —
FakeVooWearPlatformfor widget tests; no AVD required.
Install #
dependencies:
voo_wear: ^0.1.0
voo_watch: ^0.2.0 # bridge to the phone (optional but typical)
Wear OS Flutter app setup #
-
Create a Flutter app for Wear OS. Either a separate Flutter project or a peer module in your monorepo:
flutter create --platforms=android my_wear_app -
Configure the Android manifest for Wear OS. In
android/app/src/main/AndroidManifest.xml, add inside<manifest>:<uses-feature android:name="android.hardware.type.watch" />And inside
<application>:<meta-data android:name="com.google.android.wearable.standalone" android:value="true" /> -
Set
minSdk = 26inandroid/app/build.gradle.kts(Wear OS requires API 26+):defaultConfig { minSdk = 26 } -
Use
VooWearScaffoldat your app root:import 'package:flutter/material.dart'; import 'package:voo_wear/voo_wear.dart'; void main() => runApp(const MaterialApp( theme: ThemeData(brightness: Brightness.dark, useMaterial3: true), home: WatchHome(), )); class WatchHome extends StatelessWidget { const WatchHome({super.key}); @override Widget build(BuildContext context) => VooWearScaffold( body: VooWearAmbientBuilder( builder: (context, ambient) { if (ambient == VooWearAmbientState.ambient) { return const _AmbientLayout(); } return const _ActiveLayout(); }, ), ); } -
Run on a Wear OS AVD. Open Android Studio → Device Manager → Create Device → Wear OS. Pick a Wear OS image (API 30+ recommended). Then:
flutter run -d <wear-avd-id>
Round vs rectangular #
VooWearShapeBuilder(
builder: (context, shape) => shape == VooWearShape.round
? _RoundLayout()
: _RectLayout(),
);
For round screens, wrap content in VooWearRoundSafeArea to clear the corner
clipping mask:
VooWearRoundSafeArea(
child: Column(
children: [Text('Centered text'), Text('Stays inside the curve')],
),
)
Ambient mode #
VooWearAmbientBuilder rebuilds when the watch enters or leaves ambient. Use
it to swap to a minimal, monochrome layout while the wrist is down:
VooWearAmbientBuilder(
builder: (context, ambient) =>
ambient == VooWearAmbientState.ambient
? const _AmbientFace()
: const _ActiveFace(),
);
Rotary input #
VooRotaryListener(
onScroll: (delta) => _scrollController.jumpTo(_scrollController.offset + delta),
child: ListView(...),
);
Or listen as a stream via VooWearPlatform.instance.rotaryEvents.
Pair with the phone #
Add voo_watch to send messages between the Wear OS
app and the phone:
import 'package:voo_watch/voo_watch.dart';
VooWatch.instance.registerCodec<MyMessage>('my_msg', MyMessage.fromJson);
VooWatch.instance.messages<MyMessage>().listen(_onMessage);
await VooWatch.instance.send(MyMessage(...));
For a one-stop same-tree-on-both-watches approach, see
voo_watch_ui — phone defines the UI; both watches
render it.
Companion demos #
apps/voo_watch_demo— Flutter phone app driving both watches via voo_watch_ui.apps/voo_wear_demo— Flutter Wear OS app usingVooWearScaffold+WatchUiTreeListener.
Pair a Pixel AVD with a Wear OS AVD, install both APKs, and you have a fully-Dart smartwatch experience on Wear OS.
Testing #
import 'package:flutter_test/flutter_test.dart';
import 'package:voo_wear/voo_wear.dart';
import 'package:voo_wear/testing.dart';
testWidgets('ambient builder swaps layout', (tester) async {
await FakeVooWearPlatform.run(() async {
await tester.pumpWidget(/* ... */);
FakeVooWearPlatform.setAmbient(VooWearAmbientState.ambient);
await tester.pump();
expect(find.text('Ambient'), findsOneWidget);
});
});
Status #
v0.1 covers the core widgets and platform-channel bindings (round detection,
ambient mode, rotary input). Tiles, Complications, and Watch Faces are
intentionally out of scope — those still require Compose on the native side.
If you need them, drop into dart run voo_watch:init --platform android --variant compose to scaffold a Compose module that lives alongside your
Flutter app.