From c00d2fac05ffd30865691acb88dbfde1f92829e9 Mon Sep 17 00:00:00 2001 From: bytedream Date: Wed, 27 Mar 2024 22:08:09 +0100 Subject: [PATCH] mimic google behavior with location icon button --- lib/extensions/positionLatLngExtension.dart | 8 ++ lib/state/mapState.dart | 13 ++- lib/view/home.dart | 116 +++++++++++--------- lib/view/map.dart | 10 +- 4 files changed, 89 insertions(+), 58 deletions(-) create mode 100644 lib/extensions/positionLatLngExtension.dart diff --git a/lib/extensions/positionLatLngExtension.dart b/lib/extensions/positionLatLngExtension.dart new file mode 100644 index 0000000..76d4497 --- /dev/null +++ b/lib/extensions/positionLatLngExtension.dart @@ -0,0 +1,8 @@ +import 'package:geolocator/geolocator.dart'; +import 'package:latlong2/latlong.dart'; + +extension LatLngPositionExtension on Position { + LatLng latlng() { + return LatLng(latitude, longitude); + } +} diff --git a/lib/state/mapState.dart b/lib/state/mapState.dart index 8f33ed2..76f4383 100644 --- a/lib/state/mapState.dart +++ b/lib/state/mapState.dart @@ -4,13 +4,13 @@ import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; class MapState extends ChangeNotifier { - bool _isCurrentlyLoading = false; + bool _isLocationLive = false; LatLng? _currentLocation; LatLng? _activeMarker; final MapController _mapController = MapController(); final Geolocator _geolocator = Geolocator(); - bool get isCurrentlyLoading => _isCurrentlyLoading; + bool get isLocationLive => _isLocationLive; LatLng? get getCurrentLocation => _currentLocation; LatLng? get getActiveMarker => _activeMarker; MapController get getMapController => _mapController; @@ -18,16 +18,21 @@ class MapState extends ChangeNotifier { void setCurrentLocation(LatLng? currentLocation) { _currentLocation = currentLocation; + if (currentLocation != null) _mapController.move(currentLocation, 16); notifyListeners(); } void setActiveMarker(LatLng? marker) { _activeMarker = marker; + if (marker != null) { + _isLocationLive = false; + _mapController.move(marker, 16); + } notifyListeners(); } - void setLoading(bool loading) { - _isCurrentlyLoading = loading; + void setLocationLive(bool live) { + _isLocationLive = live; notifyListeners(); } } \ No newline at end of file diff --git a/lib/view/home.dart b/lib/view/home.dart index 541c2ab..1ccb512 100644 --- a/lib/view/home.dart +++ b/lib/view/home.dart @@ -1,4 +1,5 @@ import 'package:app/extensions/obtainProviderExtension.dart'; +import 'package:app/extensions/positionLatLngExtension.dart'; import 'package:app/state/mapState.dart'; import 'package:app/state/timeStatusState.dart'; import 'package:app/util/watchState.dart'; @@ -19,41 +20,70 @@ class HomeView extends StatefulWidget { class _HomePageState extends State { bool locationEnabled = false; - - Future getLatLng() async { - LocationPermission permission = await Geolocator.checkPermission(); - if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); - if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { - return null; - } - } - - setState(() { - locationEnabled = true; - }); - - Position? position; - try { - position = await Geolocator.getCurrentPosition(); - } catch (e) { - position = await Geolocator.getLastKnownPosition(); - } - - if (position != null) { - return LatLng(position.latitude, position.longitude); - } - return null; - } - + bool locationLoading = false; @override void initState() { super.initState(); - Geolocator.checkPermission().then((permission) => setState(() { - locationEnabled = permission != LocationPermission.denied && permission != LocationPermission.deniedForever; - })); + Geolocator.getServiceStatusStream().listen((status) { + setState(() { + locationEnabled = status == ServiceStatus.enabled; + }); + }); + + requestLiveLocation(context.obtainState(), false, true, true); + } + + Future requestLiveLocation(MapState mapState, bool ask, bool orLast, bool loadingIndicator) async { + Position? position; + LocationPermission permission = await Geolocator.checkPermission(); + + if (loadingIndicator && permission != LocationPermission.deniedForever) { + setState(() { + locationLoading = true; + }); + } + + if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { + if (ask && permission != LocationPermission.deniedForever) { + permission = await Geolocator.requestPermission(); + } + if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { + if (orLast) position = await Geolocator.getLastKnownPosition(); + } + } + + if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { + if (position != null) { + setState(() => locationEnabled = true); + mapState.setCurrentLocation(position.latlng()); + } + setState(() => locationLoading = false); + return; + } + + try { + position = await Geolocator.getCurrentPosition(); + setState(() { + locationEnabled = true; + }); + mapState.setLocationLive(true); + mapState.setCurrentLocation(position.latlng()); + Geolocator.getServiceStatusStream().first.then((_) => setState(() { + mapState.setLocationLive(false); + })); + } catch (_) { + if (orLast) { + position = await Geolocator.getLastKnownPosition(); + if (position != null) { + setState(() => locationEnabled = true); + mapState.setCurrentLocation(position.latlng()); + } + } + } + + setState(() => locationLoading = false); } @override @@ -72,9 +102,6 @@ class _HomePageState extends State { delegate: LocationSearchDelegate(), ); mapState.setActiveMarker(latLng); - if (latLng != null) { - mapState.getMapController.move(latLng, 16); - } }, ), IconButton( @@ -90,26 +117,15 @@ class _HomePageState extends State { ), floatingActionButton: FloatingActionButton( child: WatchState((context, state) { - if(locationEnabled) { - if(state.isCurrentlyLoading) { - return const Padding(padding: EdgeInsets.all(15), child: CircularProgressIndicator(strokeWidth: 3)); - } else { - return const Icon(Icons.my_location); - } + if (locationLoading) { + return const Padding(padding: EdgeInsets.all(15), child: CircularProgressIndicator(strokeWidth: 3)); + } + if (locationEnabled) { + return Icon(state.isLocationLive ? Icons.my_location : Icons.location_searching); } - return const Icon(Icons.location_disabled); }), - onPressed: () async { - MapState mapState = context.obtainState(); - mapState.setLoading(true); - LatLng? latLng = await getLatLng(); - if (latLng != null) { - mapState.setCurrentLocation(latLng); - mapState.getMapController.move(latLng, 16); - } - mapState.setLoading(false); - }, + onPressed: () => requestLiveLocation(context.obtainState(), true, false, true), ), body: Column( children: [ diff --git a/lib/view/map.dart b/lib/view/map.dart index 5f17b5a..127c473 100644 --- a/lib/view/map.dart +++ b/lib/view/map.dart @@ -1,7 +1,6 @@ import 'package:app/extensions/obtainProviderExtension.dart'; import 'package:app/state/mapState.dart'; import 'package:app/util/watchState.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:http/http.dart'; @@ -20,10 +19,13 @@ class _MapViewState extends State { Widget build(BuildContext context) { return FlutterMap( mapController: context.obtainState().getMapController, - options: const MapOptions( - initialCenter: LatLng(50.354, 7.5845), + options: MapOptions( + onPositionChanged: (position, hasGesture) => { + if (hasGesture) context.obtainState().setLocationLive(false) + }, + initialCenter: const LatLng(50.354, 7.5845), initialZoom: 16, - interactionOptions: InteractionOptions( + interactionOptions: const InteractionOptions( flags: InteractiveFlag.all & ~InteractiveFlag.rotate ) ),