diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index fb503a3..48b07cf 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -834,14 +834,14 @@ class _LocationsOverviewScreenState extends State final lastCenter = settings.getLastMapLocation()?.toLatLng(); final colorOpacityMultiplier = selectedViewID == null ? 1.0 : .1; return LocusFlutterMap( - mapController: flutterMapController, - options: MapOptions( + flutterMapController: flutterMapController, + flutterMapOptions: MapOptions( maxZoom: 18, minZoom: 2, center: lastCenter ?? getFallbackLocation(context), zoom: lastCenter == null ? FALLBACK_LOCATION_ZOOM_LEVEL : 13, ), - children: [ + flutterChildren: [ CircleLayer( circles: circleLocations .map((data) { diff --git a/lib/screens/locations_overview_screen_widgets/ViewDetails.dart b/lib/screens/locations_overview_screen_widgets/ViewDetails.dart index 76732a1..b10b185 100644 --- a/lib/screens/locations_overview_screen_widgets/ViewDetails.dart +++ b/lib/screens/locations_overview_screen_widgets/ViewDetails.dart @@ -2,21 +2,18 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get_time_ago/get_time_ago.dart'; +import 'package:latlong2/latlong.dart'; import 'package:locus/services/view_service/index.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:locus/utils/date.dart'; -import 'package:locus/utils/navigation.dart'; -import 'package:latlong2/latlong.dart'; - import 'package:locus/utils/location/index.dart'; import 'package:locus/utils/permissions/has-granted.dart'; -import 'package:locus/utils/permissions/request.dart'; import 'package:locus/widgets/OpenInMaps.dart'; -import 'package:map_launcher/map_launcher.dart'; + import '../../constants/spacing.dart'; import '../../services/location_point_service.dart'; import '../../utils/icon.dart'; @@ -24,7 +21,6 @@ import '../../utils/theme.dart'; import '../../widgets/BentoGridElement.dart'; import '../../widgets/LocusFlutterMap.dart'; import '../../widgets/RequestLocationPermissionMixin.dart'; -import '../ViewDetailsScreen.dart'; class ViewDetails extends StatefulWidget { final TaskView? view; @@ -54,20 +50,22 @@ class _ViewDetailsState extends State { oldLastLocation = oldWidget.location; } - Widget buildHeadingMap(final LocationPointService lastLocation,) { + Widget buildHeadingMap( + final LocationPointService lastLocation, + ) { return ClipRRect( borderRadius: BorderRadius.circular(LARGE_SPACE), child: SizedBox( height: 200, child: LocusFlutterMap( - options: MapOptions( + flutterMapOptions: MapOptions( center: LatLng( lastLocation.latitude, lastLocation.longitude, ), zoom: 13, ), - children: [ + flutterChildren: [ MarkerLayer( markers: [ Marker( @@ -75,15 +73,14 @@ class _ViewDetailsState extends State { lastLocation.latitude, lastLocation.longitude, ), - builder: (context) => - Transform.rotate( - angle: lastLocation.heading!, - child: Icon( - CupertinoIcons.location_north_fill, - color: getPrimaryColorShades(context)[0], - size: 30, - ), - ), + builder: (context) => Transform.rotate( + angle: lastLocation.heading!, + child: Icon( + CupertinoIcons.location_north_fill, + color: getPrimaryColorShades(context)[0], + size: 30, + ), + ), ), ], ) @@ -122,8 +119,8 @@ class _ViewDetailsState extends State { title: lastLocation.altitude == null ? l10n.unknownValue : l10n.locations_values_altitude_m( - lastLocation.altitude!.round(), - ), + lastLocation.altitude!.round(), + ), icon: platformThemeData( context, material: (_) => Icons.height_rounded, @@ -136,8 +133,8 @@ class _ViewDetailsState extends State { title: lastLocation.speed == null ? l10n.unknownValue : l10n.locations_values_speed_kmh( - (lastLocation.speed! * 3.6).round(), - ), + (lastLocation.speed! * 3.6).round(), + ), icon: platformThemeData( context, material: (_) => Icons.speed, @@ -150,8 +147,8 @@ class _ViewDetailsState extends State { title: lastLocation.batteryLevel == null ? l10n.unknownValue : l10n.locations_values_battery_value( - (lastLocation.batteryLevel! * 100).round(), - ), + (lastLocation.batteryLevel! * 100).round(), + ), icon: getIconDataForBatteryLevel( context, lastLocation.batteryLevel, @@ -163,8 +160,8 @@ class _ViewDetailsState extends State { title: lastLocation.batteryState == null ? l10n.unknownValue : l10n.locations_values_batteryState_value( - lastLocation.batteryState!.name, - ), + lastLocation.batteryState!.name, + ), icon: Icons.cable_rounded, type: BentoType.tertiary, description: l10n.locations_values_batteryState_description, @@ -242,10 +239,9 @@ class _DistanceBentoElementState extends State showPlatformModalSheet( context: context, material: MaterialModalSheetData(), - builder: (context) => - OpenInMaps( - destination: widget.lastLocation.asCoords(), - ), + builder: (context) => OpenInMaps( + destination: widget.lastLocation.asCoords(), + ), ); }, title: (() { @@ -342,10 +338,10 @@ class _LastLocationBentoElementState extends State { title: showAbsolute ? formatDateTimeHumanReadable(widget.lastLocation.createdAt) : GetTimeAgo.parse( - DateTime.now().subtract( - DateTime.now().difference(widget.lastLocation.createdAt), - ), - ), + DateTime.now().subtract( + DateTime.now().difference(widget.lastLocation.createdAt), + ), + ), icon: Icons.location_on_rounded, description: l10n.locations_values_lastLocation_description, ); diff --git a/lib/screens/view_alarm_screen_widgets/GeoLocationAlarmPreview.dart b/lib/screens/view_alarm_screen_widgets/GeoLocationAlarmPreview.dart index 8b7f2f8..ad9477e 100644 --- a/lib/screens/view_alarm_screen_widgets/GeoLocationAlarmPreview.dart +++ b/lib/screens/view_alarm_screen_widgets/GeoLocationAlarmPreview.dart @@ -25,10 +25,7 @@ class GeoLocationAlarmPreview extends StatelessWidget { Widget build(BuildContext context) { final locationFetchers = context.watch(); final lastLocation = - locationFetchers - .getLocations(view) - .lastOrNull - ?.asLatLng(); + locationFetchers.getLocations(view).lastOrNull?.asLatLng(); return Column( mainAxisSize: MainAxisSize.min, @@ -49,13 +46,13 @@ class GeoLocationAlarmPreview extends StatelessWidget { child: IgnorePointer( ignoring: true, child: LocusFlutterMap( - options: MapOptions( + flutterMapOptions: MapOptions( center: alarm.center, maxZoom: 18, // create zoom based of radius zoom: getZoomLevelForRadius(alarm.radius), ), - children: [ + flutterChildren: [ CircleLayer( circles: [ CircleMarker( diff --git a/lib/screens/view_alarm_screen_widgets/LocationRadiusSelectorMap.dart b/lib/screens/view_alarm_screen_widgets/LocationRadiusSelectorMap.dart index 35bca5c..48925de 100644 --- a/lib/screens/view_alarm_screen_widgets/LocationRadiusSelectorMap.dart +++ b/lib/screens/view_alarm_screen_widgets/LocationRadiusSelectorMap.dart @@ -7,8 +7,7 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; import 'package:latlong2/latlong.dart'; import 'package:locus/constants/spacing.dart'; -import 'package:locus/constants/values.dart'; -import 'package:locus/utils/location/get-fallback-location.dart'; +import 'package:locus/widgets/LocationsMap.dart'; import 'package:locus/widgets/LocusFlutterMap.dart'; import 'package:locus/widgets/MapBanner.dart'; import 'package:shimmer/shimmer.dart'; @@ -19,6 +18,8 @@ const INITIAL_RADIUS = 50.0; class LocationRadiusSelectorMap extends StatefulWidget { final MapController? flutterMapController; final apple_maps.AppleMapController? appleMapController; + final void Function(apple_maps.AppleMapController controller)? + onAppleMapCreated; final LatLng? center; final double? radius; final void Function(LatLng)? onLocationChange; @@ -32,9 +33,10 @@ class LocationRadiusSelectorMap extends StatefulWidget { this.appleMapController, this.onLocationChange, this.onRadiusChange, - this.enableRealTimeRadiusUpdate = false, + this.onAppleMapCreated, this.center, this.radius, + this.enableRealTimeRadiusUpdate = false, this.children = const [], }); @@ -137,37 +139,35 @@ class _LocationRadiusSelectorMapState extends State { child: IgnorePointer( ignoring: isInScaleMode, child: LocusFlutterMap( - mapController: widget.flutterMapController!, - options: MapOptions( - onLongPress: (_, __) { - Vibration.vibrate(duration: 100); + flutterMapController: widget.flutterMapController, + appleMapController: widget.appleMapController, + initialZoom: 13.0, + onAppleMapCreated: widget.onAppleMapCreated, + onTap: (location) { + location = LatLng( + location.latitude, + location.longitude, + ); + widget.onLocationChange?.call(location); + + if (radius == null) { setState(() { - isInScaleMode = true; + radius = INITIAL_RADIUS; + center = location; }); - }, - center: getFallbackLocation(context), - zoom: FALLBACK_LOCATION_ZOOM_LEVEL, - onTap: (tapPosition, location) { - location = LatLng( - location.latitude, - location.longitude, - ); - - widget.onLocationChange?.call(location); - - if (radius == null) { - setState(() { - radius = INITIAL_RADIUS; - center = location; - }); - - widget.onRadiusChange?.call(INITIAL_RADIUS); - } - }, - maxZoom: 18, - ), - children: [ + + widget.onRadiusChange?.call(INITIAL_RADIUS); + } + }, + onLongPress: (location) { + Vibration.vibrate(duration: 100); + + setState(() { + isInScaleMode = true; + }); + }, + flutterChildren: [ if (isInScaleMode) Shimmer.fromColors( baseColor: Colors.red, @@ -180,6 +180,27 @@ class _LocationRadiusSelectorMapState extends State { followOnLocationUpdate: FollowOnLocationUpdate.once, ) ], + appleMapCircles: { + if (center != null && radius != null) + if (isInScaleMode) + apple_maps.Circle( + circleId: apple_maps.CircleId('radius-$radius-scale'), + center: toAppleMapsCoordinates(center!), + radius: radius!, + fillColor: Colors.orangeAccent.withOpacity(.35), + strokeColor: Colors.orangeAccent, + strokeWidth: 2, + ) + else + apple_maps.Circle( + circleId: apple_maps.CircleId('radius-$radius'), + center: toAppleMapsCoordinates(center!), + radius: radius!, + fillColor: Colors.red.withOpacity(.25), + strokeColor: Colors.red, + strokeWidth: 2, + ) + }, ), ), ), diff --git a/lib/screens/view_alarm_screen_widgets/ProximityAlarmPreview.dart b/lib/screens/view_alarm_screen_widgets/ProximityAlarmPreview.dart index a7f8abd..2145c91 100644 --- a/lib/screens/view_alarm_screen_widgets/ProximityAlarmPreview.dart +++ b/lib/screens/view_alarm_screen_widgets/ProximityAlarmPreview.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; -import 'package:flutter_osm_plugin/flutter_osm_plugin.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:latlong2/latlong.dart'; import 'package:locus/constants/spacing.dart'; @@ -35,15 +34,12 @@ class ProximityAlarmPreview extends StatelessWidget { final centerPosition = currentLocation.currentPosition == null ? getFallbackLocation(context) : LatLng( - currentLocation.currentPosition!.latitude, - currentLocation.currentPosition!.longitude, - ); + currentLocation.currentPosition!.latitude, + currentLocation.currentPosition!.longitude, + ); final locationFetchers = context.watch(); final lastLocation = - locationFetchers - .getLocations(view) - .lastOrNull - ?.asLatLng(); + locationFetchers.getLocations(view).lastOrNull?.asLatLng(); return Column( mainAxisSize: MainAxisSize.min, @@ -52,12 +48,12 @@ class ProximityAlarmPreview extends StatelessWidget { title: Text( alarm.radius > 10000 ? l10n.location_addAlarm_radiusBased_radius_kilometers( - double.parse( - (alarm.radius / 1000).toStringAsFixed(1), - ), - ) + double.parse( + (alarm.radius / 1000).toStringAsFixed(1), + ), + ) : l10n.location_addAlarm_radiusBased_radius_meters( - alarm.radius.round()), + alarm.radius.round()), ), leading: getIconForLocationRadiusBasedTrigger(context, alarm.type), trailing: PlatformIconButton( @@ -73,13 +69,13 @@ class ProximityAlarmPreview extends StatelessWidget { child: IgnorePointer( ignoring: true, child: LocusFlutterMap( - options: MapOptions( + flutterMapOptions: MapOptions( center: centerPosition, maxZoom: 18, // create zoom based of radius zoom: getZoomLevelForRadius(alarm.radius), ), - children: [ + flutterChildren: [ CurrentLocationLayer( positionStream: currentLocation.locationMarkerStream, followOnLocationUpdate: FollowOnLocationUpdate.never, diff --git a/lib/screens/view_alarm_screen_widgets/ViewAlarmScreen.dart b/lib/screens/view_alarm_screen_widgets/ViewAlarmScreen.dart index 8133fe0..e314513 100644 --- a/lib/screens/view_alarm_screen_widgets/ViewAlarmScreen.dart +++ b/lib/screens/view_alarm_screen_widgets/ViewAlarmScreen.dart @@ -10,23 +10,22 @@ import 'package:locus/constants/spacing.dart'; import 'package:locus/screens/view_alarm_screen_widgets/GeoLocationAlarmPreview.dart'; import 'package:locus/screens/view_alarm_screen_widgets/ProximityAlarmPreview.dart'; import 'package:locus/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusBasedScreen.dart'; +import 'package:locus/services/current_location_service.dart'; import 'package:locus/services/location_alarm_service/LocationAlarmServiceBase.dart'; import 'package:locus/services/location_alarm_service/ProximityLocationAlarm.dart'; import 'package:locus/services/location_alarm_service/enums.dart'; import 'package:locus/services/location_alarm_service/index.dart'; import 'package:locus/services/location_point_service.dart'; import 'package:locus/services/log_service.dart'; +import 'package:locus/services/manager_service/helpers.dart'; import 'package:locus/services/view_service/index.dart'; +import 'package:locus/utils/navigation.dart'; import 'package:locus/utils/theme.dart'; import 'package:locus/widgets/ModalSheet.dart'; import 'package:locus/widgets/ModalSheetContent.dart'; -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:provider/provider.dart'; -import 'package:locus/services/manager_service/helpers.dart'; -import 'package:locus/services/current_location_service.dart'; import '../../models/log.dart'; -import '../../utils/PageRoute.dart'; import '../../widgets/LocusFlutterMap.dart'; import '../../widgets/PlatformFlavorWidget.dart'; import '../locations_overview_screen_widgets/LocationFetchers.dart'; @@ -93,26 +92,12 @@ class _ViewAlarmScreenState extends State { final logService = context.read(); final viewService = context.read(); - final LocationAlarmServiceBase? alarm = (await (() { - if (isCupertino(context)) { - return showCupertinoModalBottomSheet( - context: context, - backgroundColor: Colors.transparent, - builder: (_) => ViewAlarmSelectRadiusBasedScreen( - type: alarmType, - ), - ); - } - - return Navigator.of(context).push( - NativePageRoute( - context: context, - builder: (context) => ViewAlarmSelectRadiusBasedScreen( - type: alarmType, - ), - ), - ); - })()) as LocationAlarmServiceBase?; + final LocationAlarmServiceBase? alarm = await pushRoute( + context, + (context) => ViewAlarmSelectRadiusBasedScreen( + type: alarmType, + ), + ) as LocationAlarmServiceBase?; if (!mounted) { return; @@ -233,18 +218,18 @@ class _ViewAlarmScreenState extends State { locationFetchers.getLocations(widget.view).lastOrNull?.asLatLng(); return LocusFlutterMap( - options: MapOptions( + flutterMapOptions: MapOptions( center: alarm.center, maxZoom: 18, // create zoom based of radius zoom: 18 - log(alarm.radius / 35) / log(2), ), - children: [ + flutterChildren: [ CircleLayer( circles: [ if (lastLocation != null) CircleMarker( - point: LatLng(lastLocation!.latitude, lastLocation!.longitude), + point: LatLng(lastLocation.latitude, lastLocation.longitude), radius: 5, color: Colors.blue, ), diff --git a/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusBasedScreen.dart b/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusBasedScreen.dart index 4a19c0c..6e387cf 100644 --- a/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusBasedScreen.dart +++ b/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusBasedScreen.dart @@ -318,6 +318,9 @@ class _ViewAlarmSelectRadiusBasedScreenState alarmCenter = location; }); }, + onAppleMapCreated: (controller) { + appleMapController = controller; + }, onRadiusChange: (newRadius) { setState(() { radius = newRadius; diff --git a/lib/widgets/LocationsMap.dart b/lib/widgets/LocationsMap.dart index 8d10756..afa93ed 100644 --- a/lib/widgets/LocationsMap.dart +++ b/lib/widgets/LocationsMap.dart @@ -8,13 +8,12 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; import 'package:geolocator/geolocator.dart'; import 'package:intl/intl.dart'; - import 'package:latlong2/latlong.dart'; import 'package:locus/services/settings_service/index.dart'; import 'package:locus/utils/location/get-fallback-location.dart'; +import 'package:locus/utils/location/index.dart'; import 'package:locus/utils/permissions/has-granted.dart'; import 'package:locus/widgets/Paper.dart'; -import 'package:locus/utils/location/index.dart'; import 'package:provider/provider.dart'; import '../constants/values.dart'; @@ -367,13 +366,13 @@ class _LocationsMapState extends State { ); case MapProvider.openStreetMap: return LocusFlutterMap( - options: MapOptions( + flutterMapOptions: MapOptions( center: getInitialPosition() ?? getFallbackLocation(context), zoom: widget.initialZoomLevel, maxZoom: 18, ), - mapController: flutterMapController, - children: [ + flutterMapController: flutterMapController, + flutterChildren: [ if (widget.circles.isNotEmpty) AnimatedOpacity( duration: const Duration(milliseconds: 300), diff --git a/lib/widgets/LocusFlutterMap.dart b/lib/widgets/LocusFlutterMap.dart index 909717c..9842bfe 100644 --- a/lib/widgets/LocusFlutterMap.dart +++ b/lib/widgets/LocusFlutterMap.dart @@ -1,25 +1,65 @@ +import "package:apple_maps_flutter/apple_maps_flutter.dart" as apple_maps; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; import 'package:locus/constants/values.dart'; +import 'package:locus/services/current_location_service.dart'; import 'package:locus/utils/location/get-fallback-location.dart'; import 'package:locus/utils/theme.dart'; +import 'package:locus/widgets/LocationsMap.dart'; +import 'package:provider/provider.dart'; class LocusFlutterMap extends StatelessWidget { - final List children; + final List flutterChildren; final List nonRotatedChildren; - final MapOptions? options; - final MapController? mapController; + final MapOptions? flutterMapOptions; + final double? initialZoom; + final MapController? flutterMapController; + final apple_maps.AppleMapController? appleMapController; + final Set appleMapCircles; + final void Function(apple_maps.AppleMapController)? onAppleMapCreated; + + final void Function(LatLng)? onTap; + final void Function(LatLng)? onLongPress; const LocusFlutterMap({ super.key, - this.options, - this.children = const [], + this.flutterMapOptions, + this.flutterChildren = const [], this.nonRotatedChildren = const [], - this.mapController, + this.appleMapCircles = const {}, + this.initialZoom, + this.flutterMapController, + this.appleMapController, + this.onTap, + this.onLongPress, + this.onAppleMapCreated, }); - @override - Widget build(BuildContext context) { + LatLng getInitialPosition(final BuildContext context) { + final currentLocation = context.read(); + + return currentLocation.currentPosition == null + ? getFallbackLocation(context) + : LatLng( + currentLocation.currentPosition!.latitude, + currentLocation.currentPosition!.longitude, + ); + } + + double getInitialZoom(final BuildContext context) { + if (initialZoom != null) { + return initialZoom!; + } + + final currentLocation = context.read(); + + return currentLocation.currentPosition == null + ? FALLBACK_LOCATION_ZOOM_LEVEL + : 13.0; + } + + Widget buildFlutterMaps(final BuildContext context) { final isDarkMode = getIsDarkMode(context); final tileLayer = TileLayer( @@ -29,15 +69,17 @@ class LocusFlutterMap extends StatelessWidget { ); return FlutterMap( - options: options ?? + options: flutterMapOptions ?? MapOptions( maxZoom: 18, minZoom: 2, - center: getFallbackLocation(context), - zoom: FALLBACK_LOCATION_ZOOM_LEVEL, + center: getInitialPosition(context), + zoom: getInitialZoom(context), + onTap: (_, location) => onTap?.call(location), + onLongPress: (_, location) => onTap?.call(location), ), nonRotatedChildren: nonRotatedChildren, - mapController: mapController, + mapController: flutterMapController, children: [ if (isDarkMode) ColorFiltered( @@ -55,8 +97,37 @@ class LocusFlutterMap extends StatelessWidget { ) else tileLayer, - ...children, + ...flutterChildren, ], ); } + + Widget buildAppleMaps(final BuildContext context) { + return apple_maps.AppleMap( + initialCameraPosition: apple_maps.CameraPosition( + target: toAppleMapsCoordinates(getInitialPosition(context)), + zoom: getInitialZoom(context), + ), + compassEnabled: true, + onTap: (location) => onTap?.call(LatLng( + location.latitude, + location.longitude, + )), + onLongPress: (location) => onLongPress?.call(LatLng( + location.latitude, + location.longitude, + )), + onMapCreated: onAppleMapCreated, + circles: appleMapCircles, + ); + } + + @override + Widget build(BuildContext context) { + if (flutterMapController != null) { + return buildFlutterMaps(context); + } else { + return buildAppleMaps(context); + } + } }