From a3aaf1b6de30f5134d8180dbeee145d598e6e9a8 Mon Sep 17 00:00:00 2001 From: Shautvast Date: Sat, 4 Apr 2026 16:15:02 +0200 Subject: [PATCH] fix: stop syncing gesture positions back to mapProvider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed the updateCamera() call from onPositionChanged. Previously, every gesture-driven pan/zoom frame updated the provider state, which triggered ref.listen to call _mapController.move() redundantly. This caused flutter_map's TileLayer to cancel in-flight tile loads on every frame — producing partially-rendered maps and crashes when zooming out during tile loading. The ref.listen now only fires for programmatic changes (locateUser, zoomIn/zoomOut), which are infrequent single-shot moves. Co-Authored-By: Claude Opus 4.6 --- .../map/presentation/screens/map_screen.dart | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/mobile/lib/features/map/presentation/screens/map_screen.dart b/mobile/lib/features/map/presentation/screens/map_screen.dart index b6ea8a8..ec3d261 100644 --- a/mobile/lib/features/map/presentation/screens/map_screen.dart +++ b/mobile/lib/features/map/presentation/screens/map_screen.dart @@ -41,19 +41,13 @@ class _MapScreenState extends ConsumerState { final apiClient = ref.watch(apiClientProvider); final styleAsync = ref.watch(mapStyleProvider(apiClient.baseUrl)); - // Listen for zoom/center changes from the provider and move the map. - // Skip the move if the map controller is already at the target position - // (meaning the state change came from a user gesture via onPositionChanged). - // Without this guard, every gesture-driven pan calls _mapController.move() - // with the position the map is already at, which triggers a tile - // recalculation that cancels in-flight tile loads and produces a - // partially-rendered (diagonal) map. + // Move the map only for programmatic state changes (locateUser, + // zoomIn/zoomOut). Gesture-driven pans no longer call updateCamera(), + // so this listener only fires for intentional moves and won't interfere + // with the TileLayer's in-flight tile loads. ref.listen(mapProvider, (previous, next) { if (previous?.center != next.center || previous?.zoom != next.zoom) { - final camera = _mapController.camera; - if (camera.center != next.center || camera.zoom != next.zoom) { - _mapController.move(next.center, next.zoom); - } + _mapController.move(next.center, next.zoom); } }); @@ -69,12 +63,12 @@ class _MapScreenState extends ConsumerState { minZoom: 0, maxZoom: 18, onPositionChanged: (position, hasGesture) { - if (hasGesture) { - ref.read(mapProvider.notifier).updateCamera( - position.center, - position.zoom, - ); - } + // Intentionally not syncing gesture-driven position changes + // back to the provider. Doing so caused ref.listen to call + // _mapController.move() on every pan/zoom frame, which made + // flutter_map's TileLayer cancel in-flight tile loads — + // producing a partially-rendered (diagonal) map and crashes + // when zooming out during tile loading. }, onTap: (tapPosition, point) { ref.read(mapProvider.notifier).clearSelectedPlace(); -- 2.45.3