diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index a3b82037..29e6479b 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -45,6 +45,7 @@ import 'package:locus/widgets/GoToMyLocationMapAction.dart'; import 'package:locus/widgets/LocationsMap.dart'; import 'package:locus/widgets/LocusFlutterMap.dart'; import 'package:locus/widgets/CompassMapAction.dart'; +import 'package:locus/widgets/MapActionsContainer.dart'; import 'package:locus/widgets/Paper.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; @@ -1328,69 +1329,62 @@ class _LocationsOverviewScreenState extends State final shades = getPrimaryColorShades(context); if (settings.getMapProvider() == MapProvider.openStreetMap) { - return Positioned( - // Add half the difference to center the button - right: FAB_MARGIN + diff / 2, - bottom: FAB_SIZE + - FAB_MARGIN + - (isCupertino(context) ? LARGE_SPACE : SMALL_SPACE), - child: Column( - children: [ - AnimatedScale( - scale: showDetailedLocations ? 1 : 0, - duration: - showDetailedLocations ? 1200.milliseconds : 100.milliseconds, - curve: showDetailedLocations ? Curves.elasticOut : Curves.easeIn, - child: Tooltip( - message: disableShowDetailedLocations - ? l10n.locationsOverview_mapAction_detailedLocations_show - : l10n.locationsOverview_mapAction_detailedLocations_hide, - preferBelow: false, - margin: const EdgeInsets.only(bottom: margin), - child: SizedBox.square( - dimension: dimension, - child: Center( - child: Paper( - width: null, - borderRadius: BorderRadius.circular(HUGE_SPACE), - padding: EdgeInsets.zero, - child: IconButton( - color: shades[400], - icon: Icon(disableShowDetailedLocations - ? MdiIcons.mapMarkerMultipleOutline - : MdiIcons.mapMarkerMultiple), - onPressed: () { - setState(() { - disableShowDetailedLocations = - !disableShowDetailedLocations; - }); - }, - ), + return MapActionsContainer( + children: [ + AnimatedScale( + scale: showDetailedLocations ? 1 : 0, + duration: + showDetailedLocations ? 1200.milliseconds : 100.milliseconds, + curve: showDetailedLocations ? Curves.elasticOut : Curves.easeIn, + child: Tooltip( + message: disableShowDetailedLocations + ? l10n.locationsOverview_mapAction_detailedLocations_show + : l10n.locationsOverview_mapAction_detailedLocations_hide, + preferBelow: false, + margin: const EdgeInsets.only(bottom: margin), + child: SizedBox.square( + dimension: dimension, + child: Center( + child: Paper( + width: null, + borderRadius: BorderRadius.circular(HUGE_SPACE), + padding: EdgeInsets.zero, + child: IconButton( + color: shades[400], + icon: Icon(disableShowDetailedLocations + ? MdiIcons.mapMarkerMultipleOutline + : MdiIcons.mapMarkerMultiple), + onPressed: () { + setState(() { + disableShowDetailedLocations = + !disableShowDetailedLocations; + }); + }, ), ), ), ), ), - const SizedBox(height: SMALL_SPACE), - CompassMapAction( - onAlignNorth: () { - flutterMapController!.rotate(0); - }, - mapController: flutterMapController!, - ), - const SizedBox(height: SMALL_SPACE), - GoToMyLocationMapAction( - animate: locationStatus == LocationStatus.fetching, - onGoToMyLocation: () { - updateCurrentPosition( - askPermissions: true, - goToPosition: true, - showErrorMessage: true, - ); - }, - ), - ], - ), + ), + const SizedBox(height: SMALL_SPACE), + CompassMapAction( + onAlignNorth: () { + flutterMapController!.rotate(0); + }, + mapController: flutterMapController!, + ), + const SizedBox(height: SMALL_SPACE), + GoToMyLocationMapAction( + animate: locationStatus == LocationStatus.fetching, + onGoToMyLocation: () { + updateCurrentPosition( + askPermissions: true, + goToPosition: true, + showErrorMessage: true, + ); + }, + ), + ], ); } diff --git a/lib/screens/ViewDetailsScreen.dart b/lib/screens/ViewDetailsScreen.dart index 355d89c8..700c3e66 100644 --- a/lib/screens/ViewDetailsScreen.dart +++ b/lib/screens/ViewDetailsScreen.dart @@ -1,45 +1,24 @@ -import 'dart:async'; - -import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart' hide PlatformListTile; -import 'package:geolocator/geolocator.dart'; import 'package:locus/screens/view_alarm_screen_widgets/ViewAlarmScreen.dart'; import 'package:locus/screens/view_details_screen_widgets/LocationPointsList.dart'; -import 'package:locus/screens/view_details_screen_widgets/ViewLocationPointsScreen.dart'; -import 'package:locus/services/location_alarm_service.dart'; import 'package:locus/services/view_service.dart'; import 'package:locus/utils/PageRoute.dart'; -import 'package:locus/utils/bunny.dart'; -import 'package:locus/utils/permissions/has-granted.dart'; -import 'package:locus/utils/permissions/request.dart'; -import 'package:locus/widgets/EmptyLocationsThresholdScreen.dart'; -import 'package:locus/widgets/FillUpPaint.dart'; -import 'package:locus/widgets/LocationFetchEmpty.dart'; -import 'package:locus/widgets/LocationsMap.dart'; import 'package:locus/widgets/OpenInMaps.dart'; import 'package:locus/widgets/Paper.dart'; import 'package:locus/widgets/PlatformFlavorWidget.dart'; import 'package:locus/widgets/PlatformPopup.dart'; -import 'package:map_launcher/map_launcher.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:provider/provider.dart'; import '../constants/spacing.dart'; -import '../services/location_fetch_controller.dart'; -import '../services/location_point_service.dart'; import '../utils/theme.dart'; -import '../widgets/LocationFetchError.dart'; -import '../widgets/LocationStillFetchingBanner.dart'; -import '../widgets/LocationsLoadingScreen.dart'; import '../widgets/PlatformListTile.dart'; import 'locations_overview_screen_widgets/LocationFetchers.dart'; -const DEBOUNCE_DURATION = Duration(seconds: 2); - class ViewDetailsScreen extends StatefulWidget { final TaskView view; diff --git a/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusRegionScreen.dart b/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusRegionScreen.dart index 3563dba9..7d67d25e 100644 --- a/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusRegionScreen.dart +++ b/lib/screens/view_alarm_screen_widgets/ViewAlarmSelectRadiusRegionScreen.dart @@ -9,6 +9,7 @@ import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; import 'package:locus/constants/spacing.dart'; import 'package:locus/constants/values.dart'; +import 'package:locus/screens/locations_overview_screen_widgets/constants.dart'; import 'package:locus/screens/view_alarm_screen_widgets/RadiusRegionMetaDataSheet.dart'; import 'package:locus/services/location_alarm_service.dart'; import 'package:locus/services/settings_service/index.dart'; @@ -18,7 +19,9 @@ 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/utils/theme.dart'; +import 'package:locus/widgets/GoToMyLocationMapAction.dart'; import 'package:locus/widgets/LocationsMap.dart'; +import 'package:locus/widgets/MapActionsContainer.dart'; import 'package:locus/widgets/RequestNotificationPermissionMixin.dart'; import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart'; @@ -48,6 +51,8 @@ class _ViewAlarmSelectRadiusRegionScreenState double previousScale = 1; Stream? _positionStream; + bool isGoingToCurrentPosition = false; + @override void initState() { super.initState(); @@ -79,34 +84,36 @@ class _ViewAlarmSelectRadiusRegionScreenState showHelperSheet( context: context, - builder: (context) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text(l10n.location_addAlarm_radiusBased_help_description), - const SizedBox(height: MEDIUM_SPACE), - Row( + builder: (context) => + Column( + mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.touch_app_rounded), - const SizedBox(width: MEDIUM_SPACE), - Flexible( - child: Text( - l10n.location_addAlarm_radiusBased_help_tapDescription), + Text(l10n.location_addAlarm_radiusBased_help_description), + const SizedBox(height: MEDIUM_SPACE), + Row( + children: [ + const Icon(Icons.touch_app_rounded), + const SizedBox(width: MEDIUM_SPACE), + Flexible( + child: Text( + l10n.location_addAlarm_radiusBased_help_tapDescription), + ), + ], ), - ], - ), - const SizedBox(height: MEDIUM_SPACE), - Row( - children: [ - const Icon(Icons.pinch_rounded), - const SizedBox(width: MEDIUM_SPACE), - Flexible( - child: Text( - l10n.location_addAlarm_radiusBased_help_pinchDescription), + const SizedBox(height: MEDIUM_SPACE), + Row( + children: [ + const Icon(Icons.pinch_rounded), + const SizedBox(width: MEDIUM_SPACE), + Flexible( + child: Text( + l10n + .location_addAlarm_radiusBased_help_pinchDescription), + ), + ], ), ], ), - ], - ), title: l10n.location_addAlarm_radiusBased_help_title, sheetName: HelperSheet.radiusBasedAlarms, ); @@ -117,8 +124,16 @@ class _ViewAlarmSelectRadiusRegionScreenState return; } + setState(() { + isGoingToCurrentPosition = true; + }); + _positionStream = getLastAndCurrentPosition() ..listen((position) { + setState(() { + isGoingToCurrentPosition = false; + }); + flutterMapController?.move( LatLng(position.latitude, position.longitude), 13, @@ -139,7 +154,8 @@ class _ViewAlarmSelectRadiusRegionScreenState super.dispose(); } - CircleLayer getFlutterMapCircleLayer() => CircleLayer( + CircleLayer getFlutterMapCircleLayer() => + CircleLayer( circles: [ CircleMarker( point: alarmCenter!, @@ -160,14 +176,15 @@ class _ViewAlarmSelectRadiusRegionScreenState isDismissible: true, isScrollControlled: true, ), - builder: (_) => RadiusRegionMetaDataSheet( - center: alarmCenter!, - radius: radius, - ), + builder: (_) => + RadiusRegionMetaDataSheet( + center: alarmCenter!, + radius: radius, + ), ); final hasGrantedNotificationAccess = - await showNotificationPermissionDialog(); + await showNotificationPermissionDialog(); if (!hasGrantedNotificationAccess) { return; @@ -293,53 +310,54 @@ class _ViewAlarmSelectRadiusRegionScreenState ); } + Widget buildMapActions() { + return MapActionsContainer( + children: [ + GoToMyLocationMapAction( + onGoToMyLocation: goToCurrentPosition, + animate: isGoingToCurrentPosition, + ), + ], + ); + } + @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return PlatformScaffold( - material: (_, __) => MaterialScaffoldData( - resizeToAvoidBottomInset: false, - ), + material: (_, __) => + MaterialScaffoldData( + resizeToAvoidBottomInset: false, + ), appBar: PlatformAppBar( title: Text(l10n.location_addAlarm_radiusBased_title), trailingActions: [ PlatformIconButton( - cupertino: (_, __) => CupertinoIconButtonData( - padding: EdgeInsets.zero, - ), - icon: const Icon(Icons.my_location_rounded), - onPressed: () async { - final hasGrantedLocation = await requestBasicLocationPermission(); - - if (hasGrantedLocation) { - goToCurrentPosition(); - } - }, - ), - PlatformIconButton( - cupertino: (_, __) => CupertinoIconButtonData( - padding: EdgeInsets.zero, - ), + cupertino: (_, __) => + CupertinoIconButtonData( + padding: EdgeInsets.zero, + ), icon: Icon(context.platformIcons.help), onPressed: showHelp, ), ], - cupertino: (_, __) => CupertinoNavigationBarData( - backgroundColor: isInScaleMode - ? null - : getCupertinoAppBarColorForMapScreen(context), - ), + cupertino: (_, __) => + CupertinoNavigationBarData( + backgroundColor: isInScaleMode + ? null + : getCupertinoAppBarColorForMapScreen(context), + ), ), body: GestureDetector( onScaleUpdate: isInScaleMode ? updateZoom : null, onTap: isInScaleMode ? () { - Vibration.vibrate(duration: 50); - setState(() { - isInScaleMode = false; - }); - } + Vibration.vibrate(duration: 50); + setState(() { + isInScaleMode = false; + }); + } : null, // We need a `Stack` to disable the map, but also need to show a container to detect the long press again child: Column( @@ -352,7 +370,12 @@ class _ViewAlarmSelectRadiusRegionScreenState Positioned.fill( child: IgnorePointer( ignoring: isInScaleMode, - child: buildMap(), + child: Stack( + children: [ + buildMap(), + buildMapActions(), + ], + ), ), ), if (isInScaleMode) ...[ diff --git a/lib/screens/view_details_screen_widgets/ViewLocationPointsScreen.dart b/lib/screens/view_details_screen_widgets/ViewLocationPointsScreen.dart deleted file mode 100644 index 40a68100..00000000 --- a/lib/screens/view_details_screen_widgets/ViewLocationPointsScreen.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; -import 'package:locus/constants/spacing.dart'; -import 'package:locus/services/location_fetch_controller.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - -import '../task_detail_screen_widgets/LocationDetails.dart'; - -class ViewLocationPointsScreen extends StatefulWidget { - final LocationFetcher locationFetcher; - - const ViewLocationPointsScreen({ - required this.locationFetcher, - super.key, - }); - - @override - State createState() => _ViewLocationPointsScreenState(); -} - -class _ViewLocationPointsScreenState extends State { - final ScrollController _controller = ScrollController(); - - @override - void initState() { - super.initState(); - - widget.locationFetcher.addListener(updateView); - - _controller.addListener(() { - print(widget.locationFetcher.canFetchMore); - if (!widget.locationFetcher.canFetchMore) { - return; - } - - if (_controller.position.atEdge) { - final isTop = _controller.position.pixels == 0; - - if (!isTop) { - widget.locationFetcher.fetchMore(onEnd: () { - setState(() {}); - }); - } - } - }); - } - - updateView() { - setState(() {}); - } - - @override - void dispose() { - widget.locationFetcher.removeListener(updateView); - _controller.dispose(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final l10n = AppLocalizations.of(context); - - return PlatformScaffold( - appBar: PlatformAppBar( - title: Text(l10n.locationPointsScreen_title), - material: (_, __) => MaterialAppBarData( - centerTitle: true, - ), - ), - body: SafeArea( - child: Padding( - padding: const EdgeInsets.all(MEDIUM_SPACE), - child: ListView.builder( - shrinkWrap: true, - controller: _controller, - itemCount: widget.locationFetcher.controller.locations.length + (widget.locationFetcher.isLoading ? 1 : 0), - itemBuilder: (_, index) { - if (index == widget.locationFetcher.controller.locations.length) { - return const Center( - child: SizedBox.square( - dimension: 20, - child: CircularProgressIndicator(), - ), - ); - } - - return LocationDetails( - location: widget.locationFetcher.controller.locations[index], - isPreview: false, - ); - }, - ), - ), - ), - ); - } -} diff --git a/lib/widgets/MapActionsContainer.dart b/lib/widgets/MapActionsContainer.dart new file mode 100644 index 00000000..fba19fd2 --- /dev/null +++ b/lib/widgets/MapActionsContainer.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:locus/constants/spacing.dart'; +import 'package:locus/screens/locations_overview_screen_widgets/constants.dart'; + +const MAP_ACTION_SIZE = 50.0; +const diff = FAB_SIZE - MAP_ACTION_SIZE; + +class MapActionsContainer extends StatelessWidget { + final List children; + + const MapActionsContainer({ + super.key, + required this.children, + }); + + @override + Widget build(BuildContext context) { + return Positioned( + // Add half the difference to center the button + right: FAB_MARGIN + diff / 2, + bottom: FAB_SIZE + + FAB_MARGIN + + (isCupertino(context) ? LARGE_SPACE : SMALL_SPACE), + child: Column( + children: children, + ), + ); + } +}