From c853830e31f21450610af42f6b499cbe9364efde Mon Sep 17 00:00:00 2001 From: James McIntosh Date: Mon, 12 May 2025 23:48:10 +1200 Subject: [PATCH 1/2] Stop sending map events once stream controller is closed --- .../google_maps_controller_test.dart | 38 +++++++++++++++++++ .../lib/src/google_maps_controller.dart | 36 ++++++++++++------ 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart index dcf5345ae95a..ad0e059d2fa2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart @@ -307,6 +307,44 @@ void main() { expect(events[4], isA()); }); + testWidgets('stops listening to map events once disposed', + (WidgetTester tester) async { + controller = createController() + ..debugSetOverrides( + createMap: (_, __) => map, + circles: circles, + heatmaps: heatmaps, + markers: markers, + polygons: polygons, + polylines: polylines, + groundOverlays: groundOverlays, + ) + ..init(); + + controller.dispose(); + + // Trigger events on the map, and verify they've been broadcast to the stream + final Stream> capturedEvents = stream.stream.take(5); + + gmaps.event.trigger( + map, + 'click', + gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0), + ); + gmaps.event.trigger( + map, + 'rightclick', + gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0), + ); + // The following line causes 2 events + gmaps.event.trigger(map, 'bounds_changed'); + gmaps.event.trigger(map, 'idle'); + + final List> events = await capturedEvents.toList(); + + expect(events, isEmpty); + }); + testWidgets("binds geometry controllers to map's", (WidgetTester tester) async { controller = createController() diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 4588c4717ba0..64e16daf40f0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -243,32 +243,44 @@ class GoogleMapController { void _attachMapEvents(gmaps.Map map) { map.onTilesloaded.first.then((void _) { // Report the map as ready to go the first time the tiles load - _streamController.add(WebMapReadyEvent(_mapId)); + if (!_streamController.isClosed) { + _streamController.add(WebMapReadyEvent(_mapId)); + } }); map.onClick.listen((gmaps.MapMouseEventOrIconMouseEvent event) { assert(event.latLng != null); - _streamController.add( - MapTapEvent(_mapId, gmLatLngToLatLng(event.latLng!)), - ); + if (!_streamController.isClosed) { + _streamController.add( + MapTapEvent(_mapId, gmLatLngToLatLng(event.latLng!)), + ); + } }); map.onRightclick.listen((gmaps.MapMouseEvent event) { assert(event.latLng != null); - _streamController.add( - MapLongPressEvent(_mapId, gmLatLngToLatLng(event.latLng!)), - ); + if (!_streamController.isClosed) { + _streamController.add( + MapLongPressEvent(_mapId, gmLatLngToLatLng(event.latLng!)), + ); + } }); map.onBoundsChanged.listen((void _) { if (!_mapIsMoving) { _mapIsMoving = true; - _streamController.add(CameraMoveStartedEvent(_mapId)); + if (!_streamController.isClosed) { + _streamController.add(CameraMoveStartedEvent(_mapId)); + } + } + if (!_streamController.isClosed) { + _streamController.add( + CameraMoveEvent(_mapId, _gmViewportToCameraPosition(map)), + ); } - _streamController.add( - CameraMoveEvent(_mapId, _gmViewportToCameraPosition(map)), - ); }); map.onIdle.listen((void _) { _mapIsMoving = false; - _streamController.add(CameraIdleEvent(_mapId)); + if (!_streamController.isClosed) { + _streamController.add(CameraIdleEvent(_mapId)); + } }); } From ecc91ce4f1f5151a1ad038c08f6a6632fb8412fc Mon Sep 17 00:00:00 2001 From: James McIntosh Date: Tue, 13 May 2025 00:06:53 +1200 Subject: [PATCH 2/2] Cancel map stream subscriptions on dispose --- .../google_maps_flutter_web/CHANGELOG.md | 4 ++++ .../lib/src/google_maps_controller.dart | 23 +++++++++++++++---- .../google_maps_flutter_web/pubspec.yaml | 2 +- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 0fe686f03cdc..3ac3965fe7fe 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.13 + +* Stop processing events and cancel subscriptions when controller is disposed. + ## 0.5.12 * Adds support for ground overlay. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 64e16daf40f0..e675676c368d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -137,6 +137,11 @@ class GoogleMapController { TileOverlaysController? _tileOverlaysController; GroundOverlaysController? _groundOverlaysController; + StreamSubscription? _onClickSubscription; + StreamSubscription? _onRightClickSubscription; + StreamSubscription? _onBoundsChangedSubscription; + StreamSubscription? _onIdleSubscription; + // Keeps track if _attachGeometryControllers has been called or not. bool _controllersBoundToMap = false; @@ -247,7 +252,8 @@ class GoogleMapController { _streamController.add(WebMapReadyEvent(_mapId)); } }); - map.onClick.listen((gmaps.MapMouseEventOrIconMouseEvent event) { + _onClickSubscription = + map.onClick.listen((gmaps.MapMouseEventOrIconMouseEvent event) { assert(event.latLng != null); if (!_streamController.isClosed) { _streamController.add( @@ -255,7 +261,8 @@ class GoogleMapController { ); } }); - map.onRightclick.listen((gmaps.MapMouseEvent event) { + _onRightClickSubscription = + map.onRightclick.listen((gmaps.MapMouseEvent event) { assert(event.latLng != null); if (!_streamController.isClosed) { _streamController.add( @@ -263,7 +270,7 @@ class GoogleMapController { ); } }); - map.onBoundsChanged.listen((void _) { + _onBoundsChangedSubscription = map.onBoundsChanged.listen((void _) { if (!_mapIsMoving) { _mapIsMoving = true; if (!_streamController.isClosed) { @@ -276,7 +283,7 @@ class GoogleMapController { ); } }); - map.onIdle.listen((void _) { + _onIdleSubscription = map.onIdle.listen((void _) { _mapIsMoving = false; if (!_streamController.isClosed) { _streamController.add(CameraIdleEvent(_mapId)); @@ -600,6 +607,14 @@ class GoogleMapController { _clusterManagersController = null; _tileOverlaysController = null; _groundOverlaysController = null; + _onClickSubscription?.cancel(); + _onClickSubscription = null; + _onRightClickSubscription?.cancel(); + _onRightClickSubscription = null; + _onBoundsChangedSubscription?.cancel(); + _onBoundsChangedSubscription = null; + _onIdleSubscription?.cancel(); + _onIdleSubscription = null; _streamController.close(); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index de53daa7817f..d07c8f95fa0d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.5.12 +version: 0.5.13 environment: sdk: ^3.4.0