voo_watch 0.3.1
voo_watch: ^0.3.1 copied to clipboard
Flutter SDK for Apple Watch and Wear OS companion apps. Typed message bridge, haptics, health, complications, and a scaffolding CLI.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:voo_watch/complications.dart';
import 'package:voo_watch/haptics.dart';
import 'package:voo_watch/health.dart';
import 'package:voo_watch/voo_watch.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
VooWatch.instance.registerCodec<PingMessage>('ping', PingMessage.fromJson);
runApp(const _App());
}
class PingMessage extends VooWatchMessage {
const PingMessage(this.text);
factory PingMessage.fromJson(Map<String, Object?> json) => PingMessage(json['text']! as String);
final String text;
@override
String get type => 'ping';
@override
Map<String, Object?> toJson() => <String, Object?>{'text': text};
}
class _App extends StatelessWidget {
const _App();
@override
Widget build(BuildContext context) => MaterialApp(
title: 'voo_watch example',
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
home: const _Home(),
);
}
class _Home extends StatefulWidget {
const _Home();
@override
State<_Home> createState() => _HomeState();
}
class _HomeState extends State<_Home> {
String _lastReceived = '';
double _heartRate = 0;
@override
void initState() {
super.initState();
VooWatch.instance.messages<PingMessage>().listen((msg) {
setState(() => _lastReceived = msg.text);
});
VooWatch.instance.health.samples.listen((sample) {
if (sample.metric == VooWatchHealthMetric.heartRate) {
setState(() => _heartRate = sample.value);
}
});
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('voo_watch')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
VooWatchReachabilityBuilder(
builder: (context, state) => Card(
child: ListTile(leading: Icon(_iconFor(state)), title: const Text('Watch reachability'), subtitle: Text(state.name)),
),
),
const SizedBox(height: 12),
Card(
child: ListTile(title: const Text('Last message from watch'), subtitle: Text(_lastReceived.isEmpty ? '— none yet —' : _lastReceived)),
),
const SizedBox(height: 12),
Card(
child: ListTile(title: const Text('Heart rate'), subtitle: Text(_heartRate == 0 ? '—' : '${_heartRate.toStringAsFixed(0)} bpm')),
),
const SizedBox(height: 24),
FilledButton.icon(
onPressed: () => VooWatch.instance.send(const PingMessage('hello watch')),
icon: const Icon(Icons.send),
label: const Text('Send ping'),
),
const SizedBox(height: 8),
FilledButton.tonalIcon(
onPressed: () => VooWatch.instance.haptics.fire(VooWatchHapticType.success),
icon: const Icon(Icons.vibration),
label: const Text('Fire haptic'),
),
const SizedBox(height: 8),
FilledButton.tonalIcon(
onPressed: () =>
VooWatch.instance.complications.update(const VooWatchComplicationData(id: 'steps', shortText: '8.2k', longText: '8,234 steps', progress: 0.82)),
icon: const Icon(Icons.dashboard_customize),
label: const Text('Update complication'),
),
const SizedBox(height: 8),
FilledButton.tonalIcon(
onPressed: () => VooWatch.instance.health.subscribe(<VooWatchHealthMetric>{VooWatchHealthMetric.heartRate}),
icon: const Icon(Icons.favorite),
label: const Text('Subscribe to heart rate'),
),
],
),
),
);
IconData _iconFor(VooWatchReachability state) {
switch (state) {
case VooWatchReachability.notPaired:
return Icons.watch_off;
case VooWatchReachability.pairedButNotReachable:
return Icons.watch_later;
case VooWatchReachability.reachable:
return Icons.watch;
}
}
}