import 'dart:async'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:region_settings/region_settings.dart'; import 'package:speedometer/jotai.dart'; import 'main.dart'; import 'settings.dart'; extension on SpeedUnit { double fromMetersPerSecond(double metersPerSecond) => switch (this) { SpeedUnit.kilometersPerHour => metersPerSecond * 3.6, SpeedUnit.milesPerHour => metersPerSecond * 2.236936, }; String get acronym => switch (this) { SpeedUnit.kilometersPerHour => 'kmph', SpeedUnit.milesPerHour => 'mph', }; } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { StreamSubscription? _positionStream; bool _usesMetric = true; double _speed = 0.0; double _speedAccuracy = 0.0; void _initPositionStream(LocationAccuracy locationAccuracy) { _positionStream = Geolocator.getPositionStream( locationSettings: LocationSettings(accuracy: locationAccuracy), ).listen((Position? position) { if (position != null) { setState(() { _speed = position.speed; _speedAccuracy = position.speedAccuracy; }); } }); } @override void initState() { super.initState(); Geolocator.checkPermission() .then( (permission) => permission == LocationPermission.denied ? Geolocator.requestPermission() : permission, ) .then((permission) { if (![ LocationPermission.deniedForever, LocationPermission.denied, ].contains(permission)) { _initPositionStream(locationAccuracyObservable.value); } }); locationAccuracyObservable.subscribe((locationAccuracy) { _positionStream?.cancel(); _initPositionStream(locationAccuracy); }); RegionSettings.getUsesMetricSystem().then( (usesMetricSystem) => setState(() => _usesMetric = usesMetricSystem), ); } @override void dispose() { super.dispose(); _positionStream?.cancel(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( actions: [ TextButton.icon( icon: Icon(Icons.settings), label: Text('Settings'), onPressed: () => Navigator.of( context, ).push(MaterialPageRoute(builder: (context) => SettingsPage())), ), ], ), body: Center( child: ObserverBuilder( observable: speedUnitsObservable, builder: (context, speedUnitsSetting, _) { final speedUnits = speedUnitsSetting ?? (_usesMetric ? SpeedUnit.kilometersPerHour : SpeedUnit.milesPerHour); return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Flex(direction: Axis.horizontal), Text( '${speedUnits.fromMetersPerSecond(_speed).round()} ${speedUnits.acronym}', style: Theme.of(context).textTheme.displayLarge, ), ObserverBuilder( observable: showMarginOfErrorObservable, builder: (context, showMarginOfError, _) => showMarginOfError == true ? Text( '± ${speedUnits.fromMetersPerSecond(_speedAccuracy).round()} ${speedUnits.acronym}', style: Theme.of(context).textTheme.displaySmall, ) : SizedBox(), ), ], ); }, ), ), ); } }