Merge pull request #41 from dougborg/fix/map-jitter-on-touch

Fix map jitter/wiggle when touching at low zoom
This commit is contained in:
stopflock
2026-02-14 13:17:43 -06:00
committed by GitHub
2 changed files with 50 additions and 29 deletions
+7 -2
View File
@@ -32,6 +32,7 @@ class GpsController {
List<OsmNode> Function()? _getNearbyNodes;
List<NodeProfile> Function()? _getEnabledProfiles;
VoidCallback? _onMapMovedProgrammatically;
bool Function()? _isUserInteracting;
/// Get the current GPS location (if available)
LatLng? get currentLocation => _currentLocation;
@@ -49,6 +50,7 @@ class GpsController {
required List<OsmNode> Function() getNearbyNodes,
required List<NodeProfile> Function() getEnabledProfiles,
VoidCallback? onMapMovedProgrammatically,
bool Function()? isUserInteracting,
}) async {
debugPrint('[GpsController] Initializing GPS controller');
@@ -61,7 +63,8 @@ class GpsController {
_getNearbyNodes = getNearbyNodes;
_getEnabledProfiles = getEnabledProfiles;
_onMapMovedProgrammatically = onMapMovedProgrammatically;
_isUserInteracting = isUserInteracting;
// Start location tracking
await _startLocationTracking();
}
@@ -235,9 +238,10 @@ class GpsController {
if (followMeMode == FollowMeMode.off || _mapController == null) {
return;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
try {
if (_isUserInteracting?.call() == true) return;
if (followMeMode == FollowMeMode.follow) {
// Follow position, preserve rotation
_mapController!.animateTo(
@@ -352,5 +356,6 @@ class GpsController {
_getNearbyNodes = null;
_getEnabledProfiles = null;
_onMapMovedProgrammatically = null;
_isUserInteracting = null;
}
}
+43 -27
View File
@@ -80,6 +80,9 @@ class MapViewState extends State<MapView> {
// State for proximity alert banner
bool _showProximityBanner = false;
// Track active pointers to suppress follow-me animations during touch
int _activePointers = 0;
@@ -189,6 +192,7 @@ class MapViewState extends State<MapView> {
// Refresh nodes when GPS controller moves the map
_refreshNodesFromProvider();
},
isUserInteracting: () => _activePointers > 0,
);
// Fetch initial cameras
@@ -380,10 +384,21 @@ class MapViewState extends State<MapView> {
children: [
SheetAwareMap(
sheetHeight: widget.sheetHeight,
child: FlutterMap(
key: ValueKey('map_${appState.offlineMode}_${appState.selectedTileType?.id ?? 'none'}_${_tileManager.mapRebuildKey}'),
mapController: _controller.mapController,
options: MapOptions(
child: Listener(
onPointerDown: (_) {
_activePointers++;
_controller.stopAnimations();
},
onPointerUp: (_) {
if (_activePointers > 0) _activePointers--;
},
onPointerCancel: (_) {
if (_activePointers > 0) _activePointers--;
},
child: FlutterMap(
key: ValueKey('map_${appState.offlineMode}_${appState.selectedTileType?.id ?? 'none'}_${_tileManager.mapRebuildKey}'),
mapController: _controller.mapController,
options: MapOptions(
initialCenter: _gpsController.currentLocation ?? _positionManager.initialLocation ?? LatLng(37.7749, -122.4194),
initialZoom: _positionManager.initialZoom ?? 15,
minZoom: 1.0,
@@ -486,30 +501,31 @@ class MapViewState extends State<MapView> {
_dataManager.showZoomWarningIfNeeded(context, pos.zoom, appState.uploadMode);
}
},
),
children: [
_tileManager.buildTileLayer(
selectedProvider: appState.selectedTileProvider,
selectedTileType: appState.selectedTileType,
),
cameraLayers,
// Custom scale bar that respects user's distance unit preference
Builder(
builder: (context) {
final safeArea = MediaQuery.of(context).padding;
return CustomScaleBar(
alignment: Alignment.bottomLeft,
padding: EdgeInsets.only(
left: leftPositionWithSafeArea(8, safeArea),
bottom: bottomPositionFromButtonBar(kScaleBarSpacingAboveButtonBar, safeArea.bottom),
),
maxWidthPx: 120,
barHeight: 8,
);
},
),
],
),
),
children: [
_tileManager.buildTileLayer(
selectedProvider: appState.selectedTileProvider,
selectedTileType: appState.selectedTileType,
),
cameraLayers,
// Custom scale bar that respects user's distance unit preference
Builder(
builder: (context) {
final safeArea = MediaQuery.of(context).padding;
return CustomScaleBar(
alignment: Alignment.bottomLeft,
padding: EdgeInsets.only(
left: leftPositionWithSafeArea(8, safeArea),
bottom: bottomPositionFromButtonBar(kScaleBarSpacingAboveButtonBar, safeArea.bottom)
),
maxWidthPx: 120,
barHeight: 8,
);
},
),
],
),
),
// All map overlays (mode indicator, zoom, attribution, add pin)