mimic google behavior with location icon button

This commit is contained in:
bytedream 2024-03-27 22:08:09 +01:00
parent 507b321311
commit c00d2fac05
4 changed files with 89 additions and 58 deletions

View File

@ -0,0 +1,8 @@
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
extension LatLngPositionExtension on Position {
LatLng latlng() {
return LatLng(latitude, longitude);
}
}

View File

@ -4,13 +4,13 @@ import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
class MapState extends ChangeNotifier { class MapState extends ChangeNotifier {
bool _isCurrentlyLoading = false; bool _isLocationLive = false;
LatLng? _currentLocation; LatLng? _currentLocation;
LatLng? _activeMarker; LatLng? _activeMarker;
final MapController _mapController = MapController(); final MapController _mapController = MapController();
final Geolocator _geolocator = Geolocator(); final Geolocator _geolocator = Geolocator();
bool get isCurrentlyLoading => _isCurrentlyLoading; bool get isLocationLive => _isLocationLive;
LatLng? get getCurrentLocation => _currentLocation; LatLng? get getCurrentLocation => _currentLocation;
LatLng? get getActiveMarker => _activeMarker; LatLng? get getActiveMarker => _activeMarker;
MapController get getMapController => _mapController; MapController get getMapController => _mapController;
@ -18,16 +18,21 @@ class MapState extends ChangeNotifier {
void setCurrentLocation(LatLng? currentLocation) { void setCurrentLocation(LatLng? currentLocation) {
_currentLocation = currentLocation; _currentLocation = currentLocation;
if (currentLocation != null) _mapController.move(currentLocation, 16);
notifyListeners(); notifyListeners();
} }
void setActiveMarker(LatLng? marker) { void setActiveMarker(LatLng? marker) {
_activeMarker = marker; _activeMarker = marker;
if (marker != null) {
_isLocationLive = false;
_mapController.move(marker, 16);
}
notifyListeners(); notifyListeners();
} }
void setLoading(bool loading) { void setLocationLive(bool live) {
_isCurrentlyLoading = loading; _isLocationLive = live;
notifyListeners(); notifyListeners();
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:app/extensions/obtainProviderExtension.dart'; import 'package:app/extensions/obtainProviderExtension.dart';
import 'package:app/extensions/positionLatLngExtension.dart';
import 'package:app/state/mapState.dart'; import 'package:app/state/mapState.dart';
import 'package:app/state/timeStatusState.dart'; import 'package:app/state/timeStatusState.dart';
import 'package:app/util/watchState.dart'; import 'package:app/util/watchState.dart';
@ -19,41 +20,70 @@ class HomeView extends StatefulWidget {
class _HomePageState extends State<HomeView> { class _HomePageState extends State<HomeView> {
bool locationEnabled = false; bool locationEnabled = false;
bool locationLoading = false;
Future<LatLng?> 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;
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
Geolocator.checkPermission().then((permission) => setState(() { Geolocator.getServiceStatusStream().listen((status) {
locationEnabled = permission != LocationPermission.denied && permission != LocationPermission.deniedForever; setState(() {
locationEnabled = status == ServiceStatus.enabled;
});
});
requestLiveLocation(context.obtainState<MapState>(), false, true, true);
}
Future<void> 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 @override
@ -72,9 +102,6 @@ class _HomePageState extends State<HomeView> {
delegate: LocationSearchDelegate(), delegate: LocationSearchDelegate(),
); );
mapState.setActiveMarker(latLng); mapState.setActiveMarker(latLng);
if (latLng != null) {
mapState.getMapController.move(latLng, 16);
}
}, },
), ),
IconButton( IconButton(
@ -90,26 +117,15 @@ class _HomePageState extends State<HomeView> {
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
child: WatchState<MapState>((context, state) { child: WatchState<MapState>((context, state) {
if(locationEnabled) { if (locationLoading) {
if(state.isCurrentlyLoading) {
return const Padding(padding: EdgeInsets.all(15), child: CircularProgressIndicator(strokeWidth: 3)); return const Padding(padding: EdgeInsets.all(15), child: CircularProgressIndicator(strokeWidth: 3));
} else {
return const Icon(Icons.my_location);
} }
if (locationEnabled) {
return Icon(state.isLocationLive ? Icons.my_location : Icons.location_searching);
} }
return const Icon(Icons.location_disabled); return const Icon(Icons.location_disabled);
}), }),
onPressed: () async { onPressed: () => requestLiveLocation(context.obtainState<MapState>(), true, false, true),
MapState mapState = context.obtainState<MapState>();
mapState.setLoading(true);
LatLng? latLng = await getLatLng();
if (latLng != null) {
mapState.setCurrentLocation(latLng);
mapState.getMapController.move(latLng, 16);
}
mapState.setLoading(false);
},
), ),
body: Column( body: Column(
children: [ children: [

View File

@ -1,7 +1,6 @@
import 'package:app/extensions/obtainProviderExtension.dart'; import 'package:app/extensions/obtainProviderExtension.dart';
import 'package:app/state/mapState.dart'; import 'package:app/state/mapState.dart';
import 'package:app/util/watchState.dart'; import 'package:app/util/watchState.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
@ -20,10 +19,13 @@ class _MapViewState extends State<MapView> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FlutterMap( return FlutterMap(
mapController: context.obtainState<MapState>().getMapController, mapController: context.obtainState<MapState>().getMapController,
options: const MapOptions( options: MapOptions(
initialCenter: LatLng(50.354, 7.5845), onPositionChanged: (position, hasGesture) => {
if (hasGesture) context.obtainState<MapState>().setLocationLive(false)
},
initialCenter: const LatLng(50.354, 7.5845),
initialZoom: 16, initialZoom: 16,
interactionOptions: InteractionOptions( interactionOptions: const InteractionOptions(
flags: InteractiveFlag.all & ~InteractiveFlag.rotate flags: InteractiveFlag.all & ~InteractiveFlag.rotate
) )
), ),