Skip to content

Commit

Permalink
refactor: Add LocationRadiusSelectorMap.dart
Browse files Browse the repository at this point in the history
  • Loading branch information
Myzel394 committed Sep 12, 2023
1 parent a19d055 commit 958c5e7
Show file tree
Hide file tree
Showing 6 changed files with 575 additions and 422 deletions.
26 changes: 23 additions & 3 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -709,18 +709,38 @@
}
}
},
"location_addAlarm_radiusBased_title": "Add radius-based Alarm",
"location_addAlarm_geo_title": "Add geo-based alarm",
"location_addAlarm_geo_name_description": "Please enter a name for this region",
"location_addAlarm_geo_help_tapDescription": "Tap on the map to mark the center of your region",
"location_addAlarm_proximity_title": "Add proximity alarm",
"location_addAlarm_proximity_description": "Proximity alarms are triggered when you are within a certain distance of your friend.",
"location_addAlarm_radiusBased_radius_meters": "Radius: {value} meters",
"@location_addAlarm_radiusBased_radius_meters": {
"placeholders": {
"value": {
"type": "int"
}
}
},
"location_addAlarm_radiusBased_radius_kilometers": "Radius: {value} km",
"@location_addAlarm_radiusBased_radius_kilometers": {
"placeholders": {
"value": {
"type": "double"
}
}
},
"location_addAlarm_radiusBased_isInScaleMode": "You can now scale the radius by pinching and zooming into the map. Tap on the screen to go back to normal mode.",
"location_addAlarm_radiusBased_addLabel": "Select region",
"location_addAlarm_radiusBased_help_title": "Radius-based Alarms",
"location_addAlarm_radiusBased_help_description": "Radius-based alarms are triggered either when leaving or entering a region. After defining your region, you can decide when your alarm should trigger.",
"location_addAlarm_radiusBased_help_tapDescription": "Tap on the map to mark the center of your region",
"location_addAlarm_radiusBased_help_pinchDescription": "Press and hold to enter into scale mode. You can then change the radius by pinching and zooming into the map.",
"location_addAlarm_radiusBased_trigger_title": "When should your alarm trigger?",
"location_addAlarm_radiusBased_trigger_whenEnter": "Trigger when entering",
"location_addAlarm_radiusBased_trigger_whenLeave": "Trigger when leaving",
"location_addAlarm_radiusBased_name_description": "Please enter a name for this region",
"location_addAlarm_actionLabel": "Add Alarm",
"location_addAlarm_selectType_title": "Select Alarm Type",
"location_addAlarm_selectType_description": "What kind of alarm do you want to add?",
"permissions_openSettings_label": "Open settings",
"permissions_openSettings_failed_title": "Settings could not be opened",
"permissions_location_askPermission_title": "Grant access to your location",
Expand Down
214 changes: 214 additions & 0 deletions lib/screens/view_alarm_screen_widgets/LocationRadiusSelectorMap.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import 'dart:math';

import 'package:flutter/material.dart';
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/LocusFlutterMap.dart';
import 'package:apple_maps_flutter/apple_maps_flutter.dart' as apple_maps;
import 'package:locus/widgets/MapBanner.dart';
import 'package:shimmer/shimmer.dart';
import 'package:vibration/vibration.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

const INITIAL_RADIUS = 50.0;

class LocationRadiusSelectorMap extends StatefulWidget {
final MapController? flutterMapController;
final apple_maps.AppleMapController? appleMapController;
final LatLng? center;
final double? radius;
final void Function(LatLng, double)? onLocationSelected;

const LocationRadiusSelectorMap({
super.key,
this.flutterMapController,
this.appleMapController,
this.onLocationSelected,
this.center,
this.radius,
});

@override
State<LocationRadiusSelectorMap> createState() =>
_LocationRadiusSelectorMapState();
}

class _LocationRadiusSelectorMapState extends State<LocationRadiusSelectorMap> {
LatLng? center;
double? radius;

bool isInScaleMode = false;

double previousScale = 1;

@override
void initState() {
super.initState();

radius = widget.radius?.toDouble();
center = widget.center;
}

@override
void didUpdateWidget(covariant LocationRadiusSelectorMap oldWidget) {
super.didUpdateWidget(oldWidget);

radius = widget.radius;
center = widget.center;
}

Widget getFlutterMapCircleLayer() => CircleLayer(
circles: [
if (center != null && radius != null)
CircleMarker(
point: center!,
radius: radius!,
useRadiusInMeter: true,
color: Colors.red.withOpacity(.3),
borderStrokeWidth: 5,
borderColor: Colors.red,
),
],
);

void updateZoom(final ScaleUpdateDetails scaleUpdateDetails) async {
final mapZoom = await (() async {
if (widget.appleMapController != null) {
return widget.appleMapController!.getZoomLevel();
} else if (widget.flutterMapController != null) {
return widget.flutterMapController!.zoom;
} else {
return 0.0;
}
})() as double;
final difference = scaleUpdateDetails.scale - previousScale;
final multiplier = pow(2, 18 - mapZoom) * .2;

final newRadius = max<double>(
50,
// Radius can only be changed if a center is set;
// meaning it will always be defined here
difference > 0 ? radius! + multiplier : radius! - multiplier,
);

setState(() {
radius = newRadius;
});

previousScale = scaleUpdateDetails.scale;
}

@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);

return GestureDetector(
onScaleUpdate: isInScaleMode ? updateZoom : null,
onTap: isInScaleMode
? () {
Vibration.vibrate(duration: 50);

setState(() {
isInScaleMode = false;
});
}
: null,
child: Expanded(
flex: 10,
child: Stack(
children: <Widget>[
Positioned.fill(
child: Stack(
children: <Widget>[
IgnorePointer(
ignoring: isInScaleMode,
child: LocusFlutterMap(
mapController: widget.flutterMapController!,
options: MapOptions(
onLongPress: (_, __) {
Vibration.vibrate(duration: 100);

setState(() {
isInScaleMode = true;
});
},
center: getFallbackLocation(context),
zoom: FALLBACK_LOCATION_ZOOM_LEVEL,
onTap: (tapPosition, location) {
location = LatLng(
location.latitude,
location.longitude,
);

if (radius == null) {
setState(() {
radius = INITIAL_RADIUS;
center = location;
});
}

widget.onLocationSelected
?.call(location, radius ?? INITIAL_RADIUS);
},
maxZoom: 18,
),
children: [
if (isInScaleMode)
Shimmer.fromColors(
baseColor: Colors.red,
highlightColor: Colors.red.withOpacity(.2),
child: getFlutterMapCircleLayer(),
)
else
getFlutterMapCircleLayer(),
CurrentLocationLayer(
followOnLocationUpdate: FollowOnLocationUpdate.always,
)
],
),
),
if (isInScaleMode)
MapBanner(
child: Row(
children: <Widget>[
const Icon(Icons.pinch_rounded),
const SizedBox(width: MEDIUM_SPACE),
Flexible(
child: Text(
l10n.location_addAlarm_radiusBased_isInScaleMode,
style: const TextStyle(
color: Colors.white,
),
),
),
],
),
),
if (widget.center != null && widget.radius != null)
Positioned(
bottom: LARGE_SPACE,
left: 0,
right: 0,
child: Text(
widget.radius! > 10000
? l10n
.location_addAlarm_radiusBased_radius_kilometers(
widget.radius! / 1000)
: l10n.location_addAlarm_radiusBased_radius_meters(
widget.radius!.round()),
textAlign: TextAlign.center,
),
),
],
),
)
],
),
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class _RadiusRegionMetaDataSheetState extends State<RadiusRegionMetaDataSheet> {
]
: [
Text(
l10n.location_addAlarm_radiusBased_name_description,
l10n.location_addAlarm_geo_name_description,
style: getSubTitleTextStyle(context),
),
const SizedBox(height: MEDIUM_SPACE),
Expand Down
54 changes: 51 additions & 3 deletions lib/screens/view_alarm_screen_widgets/ViewAlarmScreen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import 'package:flutter_map/plugin_api.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:latlong2/latlong.dart';
import 'package:locus/constants/spacing.dart';
import 'package:locus/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusRegionScreen.dart';
import 'package:locus/screens/view_alarm_screen_widgets/ViewAlarmSelectGeoBasedScreen.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/view_service.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';

Expand All @@ -38,21 +40,67 @@ class _ViewAlarmScreenState extends State<ViewAlarmScreen> {
LocationPointService? lastLocation;

void _addNewAlarm() async {
final l10n = AppLocalizations.of(context);

final alarmType = await showPlatformModalSheet(
context: context,
material: MaterialModalSheetData(
backgroundColor: Colors.transparent,
isScrollControlled: true,
isDismissible: true,
),
builder: (context) => ModalSheet(
child: ModalSheetContent(
icon: Icons.alarm_rounded,
title: l10n.location_addAlarm_selectType_title,
description: l10n.location_addAlarm_selectType_description,
children: [
PlatformListTile(
title: Text(l10n.location_addAlarm_geo_title),
leading: const Icon(Icons.location_on_rounded),
onTap: () {
Navigator.of(context).pop(
LocationAlarmType.radiusBasedRegion,
);
},
),
PlatformListTile(
title: Text(l10n.location_addAlarm_proximity_title),
leading: const Icon(Icons.location_searching_rounded),
onTap: () {
Navigator.of(context).pop(
LocationAlarmType.proximityLocation,
);
},
),
],
),
),
);

if (!mounted || alarmType == null) {
return;
}

final logService = context.read<LogService>();
final viewService = context.read<ViewService>();
final RadiusBasedRegionLocationAlarm? alarm = (await (() {
if (isCupertino(context)) {
return showCupertinoModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (_) => const ViewAlarmSelectRadiusRegionScreen(),
builder: (_) => ViewAlarmSelectGeoBasedScreen(
type: alarmType,
),
);
}

return Navigator.of(context).push(
NativePageRoute(
context: context,
builder: (context) => const ViewAlarmSelectRadiusRegionScreen(),
builder: (context) => ViewAlarmSelectGeoBasedScreen(
type: alarmType,
),
),
);
})()) as RadiusBasedRegionLocationAlarm?;
Expand Down
Loading

0 comments on commit 958c5e7

Please sign in to comment.