feat(location-map): auto-center on first fix, follow toggle, refresh feedback

This commit is contained in:
2026-04-15 22:13:12 +02:00
parent c7e32d1399
commit 51a3979c03
10 changed files with 66 additions and 2 deletions

View File

@@ -117,8 +117,9 @@ class _LocationMapState extends ConsumerState<LocationMap>
}
} else if (widget.selectedPosition != null &&
widget.selectedPosition != oldWidget.selectedPosition) {
final mapState = ref.read(locationMapViewModelProvider);
if (mapState.isFollowing) _centerOnDevice();
final firstFix = oldWidget.selectedPosition == null;
final isFollowing = ref.read(locationMapViewModelProvider).isFollowing;
if (firstFix || isFollowing) _centerOnDevice();
}
if (widget.positionHistory.length > 1 &&
@@ -661,6 +662,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
child: MapActionsPanel(
actionsExpanded: mapState.actionsExpanded,
hasPosition: widget.selectedPosition != null,
isFollowing: mapState.isFollowing,
onToggleExpanded: _vm.toggleActionsExpanded,
onListTap: _showListSheet,
onAddGeofence: () => _vm.startPlacing(PlacingMode.geofence),
@@ -671,8 +673,34 @@ class _LocationMapState extends ConsumerState<LocationMap>
ref.read(sfTrackingProvider).legacyLocationMapRefreshTapped(),
);
widget.onRefreshPosition();
showTopSnackbar(
context,
message: context.translate(I18n.locationMapRefreshRequested),
type: MessageType.info,
);
},
onCenterTap: _centerOnDevice,
onToggleFollow: () {
final willActivate = !mapState.isFollowing;
_vm.toggleFollowing();
unawaited(
ref
.read(sfTrackingProvider)
.legacyLocationMapFollowToggled(willActivate),
);
if (willActivate && widget.selectedPosition != null) {
_centerOnDevice();
}
showTopSnackbar(
context,
message: context.translate(
willActivate
? I18n.locationMapFollowEnabled
: I18n.locationMapFollowDisabled,
),
type: MessageType.success,
);
},
),
),
];

View File

@@ -6,6 +6,7 @@ import 'map_action_button.dart';
class MapActionsPanel extends StatelessWidget {
final bool actionsExpanded;
final bool hasPosition;
final bool isFollowing;
final VoidCallback onToggleExpanded;
final VoidCallback onListTap;
final VoidCallback onAddGeofence;
@@ -13,11 +14,13 @@ class MapActionsPanel extends StatelessWidget {
final VoidCallback onShareTap;
final VoidCallback onRefreshTap;
final VoidCallback onCenterTap;
final VoidCallback onToggleFollow;
const MapActionsPanel({
super.key,
required this.actionsExpanded,
required this.hasPosition,
required this.isFollowing,
required this.onToggleExpanded,
required this.onListTap,
required this.onAddGeofence,
@@ -25,6 +28,7 @@ class MapActionsPanel extends StatelessWidget {
required this.onShareTap,
required this.onRefreshTap,
required this.onCenterTap,
required this.onToggleFollow,
});
@override
@@ -67,6 +71,12 @@ class MapActionsPanel extends StatelessWidget {
if (hasPosition) ...[
const SizedBox(height: 8),
MapActionButton(icon: Icons.my_location, onTap: onCenterTap),
const SizedBox(height: 8),
MapActionButton(
icon: isFollowing ? Icons.gps_fixed : Icons.gps_not_fixed,
isActive: isFollowing,
onTap: onToggleFollow,
),
],
],
);

View File

@@ -580,6 +580,9 @@
"locationMapStyleLight": "Hell",
"locationMapStyleDark": "Dunkel",
"locationMapStyleSatellite": "Satellit",
"locationMapRefreshRequested": "Standort wird von der Uhr angefordert…",
"locationMapFollowEnabled": "Automatische Verfolgung aktiviert",
"locationMapFollowDisabled": "Automatische Verfolgung deaktiviert",
"locationLayerGeofences": "Sicherheitszonen",
"locationLayerFrequentPlaces": "Häufige Orte",
"locationLayerHistory": "Verlauf",

View File

@@ -715,6 +715,9 @@
"locationMapStyleLight": "Light",
"locationMapStyleDark": "Dark",
"locationMapStyleSatellite": "Satellite",
"locationMapRefreshRequested": "Requesting location from the watch…",
"locationMapFollowEnabled": "Auto-follow enabled",
"locationMapFollowDisabled": "Auto-follow disabled",
"locationLayerGeofences": "Safety zones",
"locationLayerFrequentPlaces": "Frequent Places",
"locationLayerHistory": "History",

View File

@@ -716,6 +716,9 @@
"locationMapStyleLight": "Claro",
"locationMapStyleDark": "Oscuro",
"locationMapStyleSatellite": "Satélite",
"locationMapRefreshRequested": "Solicitando ubicación al reloj…",
"locationMapFollowEnabled": "Seguimiento automático activado",
"locationMapFollowDisabled": "Seguimiento automático desactivado",
"locationLayerGeofences": "Zonas de seguridad",
"locationLayerFrequentPlaces": "Lugares Frecuentes",
"locationLayerHistory": "Historial",

View File

@@ -580,6 +580,9 @@
"locationMapStyleLight": "Clair",
"locationMapStyleDark": "Sombre",
"locationMapStyleSatellite": "Satellite",
"locationMapRefreshRequested": "Demande de localisation à la montre…",
"locationMapFollowEnabled": "Suivi automatique activé",
"locationMapFollowDisabled": "Suivi automatique désactivé",
"locationLayerGeofences": "Zones de sécurité",
"locationLayerFrequentPlaces": "Lieux fréquents",
"locationLayerHistory": "Historique",

View File

@@ -580,6 +580,9 @@
"locationMapStyleLight": "Chiaro",
"locationMapStyleDark": "Scuro",
"locationMapStyleSatellite": "Satellite",
"locationMapRefreshRequested": "Richiesta posizione all'orologio…",
"locationMapFollowEnabled": "Tracciamento automatico attivato",
"locationMapFollowDisabled": "Tracciamento automatico disattivato",
"locationLayerGeofences": "Zone di sicurezza",
"locationLayerFrequentPlaces": "Luoghi frequenti",
"locationLayerHistory": "Cronologia",

View File

@@ -580,6 +580,9 @@
"locationMapStyleLight": "Claro",
"locationMapStyleDark": "Escuro",
"locationMapStyleSatellite": "Satélite",
"locationMapRefreshRequested": "Solicitando localização ao relógio…",
"locationMapFollowEnabled": "Seguimento automático ativado",
"locationMapFollowDisabled": "Seguimento automático desativado",
"locationLayerGeofences": "Zonas de segurança",
"locationLayerFrequentPlaces": "Lugares frequentes",
"locationLayerHistory": "Histórico",

View File

@@ -567,6 +567,9 @@ class I18n {
static const String locationMapStyleSatellite = 'locationMapStyleSatellite';
static const String locationMapStyleStandard = 'locationMapStyleStandard';
static const String locationMapStyleVoyager = 'locationMapStyleVoyager';
static const String locationMapRefreshRequested = 'locationMapRefreshRequested';
static const String locationMapFollowEnabled = 'locationMapFollowEnabled';
static const String locationMapFollowDisabled = 'locationMapFollowDisabled';
static const String locationNewFrequentPlace = 'locationNewFrequentPlace';
static const String locationNewGeofence = 'locationNewGeofence';
static const String locationPlace = 'locationPlace';

View File

@@ -114,6 +114,11 @@ mixin LocationTracking on Tracking {
Future<void> legacyLocationMapRefreshTapped() =>
trackEvent('${_prefix}_map_refresh_tapped');
Future<void> legacyLocationMapFollowToggled(bool active) => trackEvent(
'${_prefix}_map_follow_toggled',
{'active': active.toString()},
);
Future<void> legacyLocationListSheetOpened() =>
trackEvent('${_prefix}_list_sheet_opened');