ternary follow me

This commit is contained in:
stopflock
2025-08-26 23:46:38 -05:00
parent b735283f27
commit 2d0dc7fd66
2 changed files with 75 additions and 21 deletions
+51 -8
View File
@@ -10,6 +10,12 @@ import '../widgets/add_camera_sheet.dart';
import '../widgets/camera_provider_with_cache.dart';
import '../widgets/download_area_dialog.dart';
enum FollowMeMode {
off, // No following
northUp, // Follow position, keep north up
rotating, // Follow position and rotation
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@@ -21,11 +27,44 @@ class _HomeScreenState extends State<HomeScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final GlobalKey<MapViewState> _mapViewKey = GlobalKey<MapViewState>();
final MapController _mapController = MapController();
bool _followMe = true;
FollowMeMode _followMeMode = FollowMeMode.northUp;
String _getFollowMeTooltip() {
switch (_followMeMode) {
case FollowMeMode.off:
return 'Enable follow-me (north up)';
case FollowMeMode.northUp:
return 'Enable follow-me (rotating)';
case FollowMeMode.rotating:
return 'Disable follow-me';
}
}
IconData _getFollowMeIcon() {
switch (_followMeMode) {
case FollowMeMode.off:
return Icons.gps_off;
case FollowMeMode.northUp:
return Icons.gps_fixed;
case FollowMeMode.rotating:
return Icons.navigation;
}
}
FollowMeMode _getNextFollowMeMode() {
switch (_followMeMode) {
case FollowMeMode.off:
return FollowMeMode.northUp;
case FollowMeMode.northUp:
return FollowMeMode.rotating;
case FollowMeMode.rotating:
return FollowMeMode.off;
}
}
void _openAddCameraSheet() {
// Disable follow-me when adding a camera so the map doesn't jump around
setState(() => _followMe = false);
setState(() => _followMeMode = FollowMeMode.off);
final appState = context.read<AppState>();
appState.startAddSession();
@@ -50,12 +89,14 @@ class _HomeScreenState extends State<HomeScreen> {
title: const Text('Flock Map'),
actions: [
IconButton(
tooltip: _followMe ? 'Disable followme' : 'Enable followme',
icon: Icon(_followMe ? Icons.gps_fixed : Icons.gps_off),
tooltip: _getFollowMeTooltip(),
icon: Icon(_getFollowMeIcon()),
onPressed: () {
setState(() => _followMe = !_followMe);
setState(() {
_followMeMode = _getNextFollowMeMode();
});
// If enabling follow-me, retry location init in case permission was granted
if (_followMe) {
if (_followMeMode != FollowMeMode.off) {
_mapViewKey.currentState?.retryLocationInit();
}
},
@@ -71,9 +112,11 @@ class _HomeScreenState extends State<HomeScreen> {
MapView(
key: _mapViewKey,
controller: _mapController,
followMe: _followMe,
followMeMode: _followMeMode,
onUserGesture: () {
if (_followMe) setState(() => _followMe = false);
if (_followMeMode != FollowMeMode.off) {
setState(() => _followMeMode = FollowMeMode.off);
}
},
),
Align(
+24 -13
View File
@@ -22,16 +22,18 @@ import 'map/map_overlays.dart';
import 'network_status_indicator.dart';
import '../dev_config.dart';
import '../screens/home_screen.dart' show FollowMeMode;
class MapView extends StatefulWidget {
final MapController controller;
const MapView({
super.key,
required this.controller,
required this.followMe,
required this.followMeMode,
required this.onUserGesture,
});
final bool followMe;
final FollowMeMode followMeMode;
final VoidCallback onUserGesture;
@override
@@ -135,12 +137,16 @@ class MapViewState extends State<MapView> {
@override
void didUpdateWidget(covariant MapView oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.followMe && !oldWidget.followMe && _currentLatLng != null) {
if (widget.followMeMode != FollowMeMode.off &&
oldWidget.followMeMode == FollowMeMode.off &&
_currentLatLng != null) {
// Use smooth animation when follow me is first enabled
_controller.animatedMove(
_currentLatLng!,
_controller.camera.zoom,
);
if (widget.followMeMode == FollowMeMode.northUp) {
_controller.move(_currentLatLng!, _controller.camera.zoom);
} else if (widget.followMeMode == FollowMeMode.rotating) {
// When switching to rotating mode, reset to north-up first, then let GPS handle rotation
_controller.moveAndRotate(_currentLatLng!, _controller.camera.zoom, 0.0);
}
}
}
@@ -160,15 +166,20 @@ class MapViewState extends State<MapView> {
Geolocator.getPositionStream(locationSettings: locationSettings).listen((Position position) {
final latLng = LatLng(position.latitude, position.longitude);
setState(() => _currentLatLng = latLng);
if (widget.followMe) {
if (widget.followMeMode != FollowMeMode.off) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
try {
// Use smooth animation instead of instant jump
_controller.animatedMove(
latLng,
_controller.camera.zoom,
);
if (widget.followMeMode == FollowMeMode.northUp) {
// Follow position only, keep current rotation
_controller.move(latLng, _controller.camera.zoom);
} else if (widget.followMeMode == FollowMeMode.rotating) {
// Follow position and rotation based on heading
final heading = position.heading;
final rotation = heading.isNaN ? 0.0 : -heading; // Convert to map rotation
_controller.moveAndRotate(latLng, _controller.camera.zoom, rotation);
}
} catch (e) {
debugPrint('MapController not ready yet: $e');
}