import 'package:geolocator/geolocator.dart'; /// Supplies the device's *approximate* location (or null when unavailable or /// denied). Behind an interface so the UI is fakeable in tests and the concrete /// platform plugin stays at the composition root. The caller immediately reduces /// the result to a low-precision geohash — a precise fix never leaves the device. abstract interface class CoarseLocationProvider { Future<({double lat, double lon})?> currentCoarseLatLon(); } /// [geolocator]-backed implementation. Requests low accuracy only, and returns /// null on any denial/error rather than throwing, so the UI can degrade quietly. class GeolocatorCoarseLocation implements CoarseLocationProvider { const GeolocatorCoarseLocation(); @override Future<({double lat, double lon})?> currentCoarseLatLon() async { try { if (!await Geolocator.isLocationServiceEnabled()) return null; var permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); } if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { return null; } final position = await Geolocator.getCurrentPosition( locationSettings: const LocationSettings( accuracy: LocationAccuracy.low, ), ); return (lat: position.latitude, lon: position.longitude); } catch (_) { return null; // service off, plugin unsupported (desktop), timeout, … } } }