diff --git a/green_walking/lib/map_utils.dart b/green_walking/lib/map_utils.dart index 3377e38..7d1bdd6 100644 --- a/green_walking/lib/map_utils.dart +++ b/green_walking/lib/map_utils.dart @@ -1,27 +1,28 @@ +import 'dart:developer' show log; import 'dart:io' show Platform; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; -class UserLocation { - UserLocation({required this.location, this.bearing}); +class PuckLocation { + PuckLocation({required this.location, this.bearing}); Position location; double? bearing; } extension PuckPosition on StyleManager { - Future getPuckPosition() async { + Future getPuckLocation() async { try { final Layer? layer = Platform.isAndroid - ? await getLayer("mapbox-location-indicator-layer") - : await getLayer("puck"); + ? await getLayer('mapbox-location-indicator-layer') + : await getLayer('puck'); final LocationIndicatorLayer liLayer = layer as LocationIndicatorLayer; final List? location = liLayer.location; - return Future.value(UserLocation( + return Future.value(PuckLocation( location: Position(location![1]!, location[0]!), bearing: liLayer.bearing)); } catch (e) { - // FIXME: Log error + log('failed to get puck location: $e'); return null; } } diff --git a/green_walking/lib/pages/map/location_button.dart b/green_walking/lib/pages/map/location_button.dart index ce3b0c5..55c49fb 100644 --- a/green_walking/lib/pages/map/location_button.dart +++ b/green_walking/lib/pages/map/location_button.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; -enum UserLocatinTracking { no, position, full } +enum UserLocationTracking { no, position, positionBearing } class LocationButton extends StatefulWidget { const LocationButton( @@ -14,7 +14,7 @@ class LocationButton extends StatefulWidget { final void Function(bool) onOkay; final VoidCallback onNoPermissions; - final ValueNotifier trackUserLocation; + final ValueNotifier trackUserLocation; @override State createState() => _LocationButtonState(); @@ -51,13 +51,13 @@ class _LocationButtonState extends State { child: FloatingActionButton( backgroundColor: Theme.of(context).colorScheme.secondary, onPressed: _onPressed, - child: ValueListenableBuilder( + child: ValueListenableBuilder( valueListenable: widget.trackUserLocation, builder: (context, value, child) { final IconData icon = _locationServiceEnabled - ? value == UserLocatinTracking.position + ? value == UserLocationTracking.position ? Icons.my_location - : value == UserLocatinTracking.full + : value == UserLocationTracking.positionBearing ? Icons.arrow_upward : Icons.location_searching : Icons.location_disabled; diff --git a/green_walking/lib/pages/map/map.dart b/green_walking/lib/pages/map/map.dart index bbe1099..87c398e 100644 --- a/green_walking/lib/pages/map/map.dart +++ b/green_walking/lib/pages/map/map.dart @@ -47,10 +47,11 @@ class MapPage extends StatefulWidget { class _MapPageState extends State { final GlobalKey _scaffoldKey = GlobalKey(); - final ValueNotifier _userlocationTracking = - ValueNotifier(UserLocatinTracking.no); + final ValueNotifier _userlocationTracking = + ValueNotifier(UserLocationTracking.no); late MapboxMap _mapboxMap; + CircleAnnotationManager? _circleAnnotationManager; Timer? _timer; @override @@ -132,54 +133,51 @@ class _MapPageState extends State { Widget map(BuildContext context, MapConfig config) { return MapWidget( - key: const ValueKey("mapWidget"), + key: const ValueKey('mapWidget'), resourceOptions: ResourceOptions(accessToken: config.accessToken), onMapCreated: (MapboxMap mapboxMap) { - //mapboxMap.setTelemetryEnabled(false); - mapboxMap.compass.updateSettings(CompassSettings(marginTop: 400.0)); - mapboxMap.scaleBar.updateSettings(ScaleBarSettings(enabled: false)); _mapboxMap = mapboxMap; + + //mapboxMap.setTelemetryEnabled(false); + _mapboxMap.compass.updateSettings(CompassSettings(marginTop: 400.0)); + _mapboxMap.scaleBar.updateSettings(ScaleBarSettings(enabled: false)); //mapController!.onSymbolTapped.add(_onSymbolTapped); //onPositionChanged(mapController!.cameraPosition); + + _mapboxMap.annotations.createCircleAnnotationManager().then((value) { + _circleAnnotationManager = value; + }); }, cameraOptions: config.lastPosition ?? CameraOptions( center: Point(coordinates: Position(9.8682, 53.5519)).toJson(), zoom: 11.0), - //myLocationEnabled: true, - //rotateGesturesEnabled: true, - //styleString: _mapboxStyle.id, styleUri: CustomMapboxStyles.outdoor, - //trackCameraPosition: true, onMapIdleListener: _onCameraIdle, - //onCameraTrackingDismissed: () => _userlocationTracking.value = false, - //onCameraChangeListener: - // (CameraChangedEventData cameraChangedEventData) { - // _userlocationTracking.value = UserLocatinTracking.no; onScrollListener: (ScreenCoordinate coordinate) { - if (_userlocationTracking.value != UserLocatinTracking.no) { - // Turn of tracking because user scrolled. - _userlocationTracking.value = UserLocatinTracking.no; + if (_userlocationTracking.value != UserLocationTracking.no) { + // Turn off tracking because user scrolled to another location. + _userlocationTracking.value = UserLocationTracking.no; } }, ); } - Future _getMapPosition() async { + Future _getCameraPosition() async { try { final CameraState mapCameraState = await _mapboxMap.getCameraState(); final List coordinates = - mapCameraState.center["coordinates"] as List; + mapCameraState.center['coordinates'] as List; return Position(coordinates[0] as num, coordinates[1] as num); } catch (e) { - // Fixme: Log error? + log('failed to get camera position: $e'); return null; } } Future _onSearchTab(String accessToken) async { - final Position? mapPosition = await _getMapPosition(); - if (mapPosition == null) { + final Position? cameraPosition = await _getCameraPosition(); + if (cameraPosition == null) { return; } // See https://dart.dev/tools/linter-rules/use_build_context_synchronously @@ -187,21 +185,25 @@ class _MapPageState extends State { final Position? moveToLoc = await Navigator.push( context, NoTransitionPageRoute( - builder: (BuildContext context) => - SearchPage(mapPosition: mapPosition, accessToken: accessToken)), + builder: (BuildContext context) => SearchPage( + mapPosition: cameraPosition, accessToken: accessToken)), ); if (moveToLoc == null) { return; } - setCameraPosition(moveToLoc, null, null); - /*_mapboxMap?.clearCircles(); - _mapboxMap?.addCircle(CircleOptions( - circleRadius: 12, - circleColor: '#FFC0CB', - circleOpacity: 0.6, - circleStrokeWidth: 2, - circleStrokeColor: '#FFC0CB', - geometry: moveToLoc));*/ + // If we keep the tracking on the map would move back to the user location. + _userlocationTracking.value = UserLocationTracking.no; + _setCameraPosition(moveToLoc, 0, 0); + + // Draw circle + await _circleAnnotationManager?.deleteAll(); + _circleAnnotationManager?.create(CircleAnnotationOptions( + geometry: Point(coordinates: moveToLoc).toJson(), + circleRadius: 12, + circleColor: const Color.fromRGBO(255, 192, 203, 1).value, + circleOpacity: 0.6, + circleStrokeWidth: 2, + circleStrokeColor: const Color.fromRGBO(255, 192, 203, 1).value)); } } @@ -214,13 +216,13 @@ class _MapPageState extends State { } } - void setCameraPosition(Position position, double? bearing, double? pitch) { - _mapboxMap.flyTo( + Future _setCameraPosition( + Position position, double? bearing, double? pitch) async { + return _mapboxMap.flyTo( CameraOptions( center: Point(coordinates: position).toJson(), bearing: bearing, pitch: pitch, - //padding: defaultEdgeInsets, zoom: 16.5, ), MapAnimationOptions(duration: 900 + 100)); @@ -228,41 +230,40 @@ class _MapPageState extends State { void refreshTrackLocation() async { _timer?.cancel(); - if (_userlocationTracking.value == UserLocatinTracking.no) { - return; - } - _timer = Timer.periodic(const Duration(milliseconds: 900), (timer) async { - if (_userlocationTracking.value == UserLocatinTracking.no) { + if (_userlocationTracking.value == UserLocationTracking.no) { _timer?.cancel(); return; } - final UserLocation? loc = await _mapboxMap.style - .getPuckPosition() + final PuckLocation? puckLocation = await _mapboxMap.style + .getPuckLocation() .timeout(const Duration(milliseconds: 900 - 100)); - if (loc == null) { + if (puckLocation == null) { + // FIXME: Show toast if no location. + // ScaffoldMessenger.of(context) + // .showSnackBar(SnackBar(content: Text(locale.errorNoPositionFound))); return; } switch (_userlocationTracking.value) { - case UserLocatinTracking.full: - setCameraPosition(loc.location, loc.bearing, 50.0); - case UserLocatinTracking.position: - setCameraPosition(loc.location, 0.0, 0.0); - case UserLocatinTracking.no: - // Handled above already. In case there is no location returned we would not cancel - // the timer otherwise. + case UserLocationTracking.positionBearing: + _setCameraPosition(puckLocation.location, puckLocation.bearing, 50.0); + case UserLocationTracking.position: + _setCameraPosition(puckLocation.location, 0.0, 0.0); + case UserLocationTracking.no: + // Handled above already. In case there is no location returned we would not cancel + // the timer otherwise. } }); } Future _onLocationSearchPressed( AppLocalizations locale, bool permissionGranted) async { - if (_userlocationTracking.value == UserLocatinTracking.position) { - _userlocationTracking.value = UserLocatinTracking.full; + if (_userlocationTracking.value == UserLocationTracking.position) { + _userlocationTracking.value = UserLocationTracking.positionBearing; } else { - _userlocationTracking.value = UserLocatinTracking.position; + _userlocationTracking.value = UserLocationTracking.position; } await _mapboxMap.location.updateSettings(LocationComponentSettings( enabled: true, @@ -270,21 +271,11 @@ class _MapPageState extends State { showAccuracyRing: true, puckBearingEnabled: true)); refreshTrackLocation(); - - // FIXME: Show toast if no location. - /* - } else { - // See https://dart-lang.github.io/linter/lints/use_build_context_synchronously.html - if (!mounted) return; - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(locale.errorNoPositionFound))); - } - */ } Future _onCameraIdle(MapIdleEventData mapIdleEventData) async { // TODO: Maybe use a Timer instead of writing data that often? - //final CameraState cameraState = await _mapboxMap.getCameraState(); - //SharedPrefs.setCameraState(SharedPrefs.keyLastPosition, cameraState); + final CameraState cameraState = await _mapboxMap.getCameraState(); + SharedPrefs.setCameraState(SharedPrefs.keyLastPosition, cameraState); } } diff --git a/green_walking/lib/services/shared_prefs.dart b/green_walking/lib/services/shared_prefs.dart index 4bbe042..730342c 100644 --- a/green_walking/lib/services/shared_prefs.dart +++ b/green_walking/lib/services/shared_prefs.dart @@ -26,7 +26,7 @@ class SharedPrefs { static void setCameraState(String key, CameraState val) { try { - final String raw = jsonEncode(val.encode()); // TODO?????????? + final String raw = jsonEncode(val.encode()); SharedPreferences.getInstance() .then((SharedPreferences prefs) => prefs.setString(key, raw)); } catch (e) {