Skip to content

Commit c3eac1e

Browse files
committed
Re-apply changes
1 parent e4d93da commit c3eac1e

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ class GoogleMapController {
264264
/// in-memory cache of tiles. If you want to cache tiles for longer, you
265265
/// should implement an on-disk cache.
266266
Future<void> clearTileCache(TileOverlayId tileOverlayId) async {
267+
_checkWidgetMountedOrThrow();
267268
return GoogleMapsFlutterPlatform.instance.clearTileCache(
268269
tileOverlayId,
269270
mapId: mapId,
@@ -278,6 +279,7 @@ class GoogleMapController {
278279
/// The returned [Future] completes after the change has been started on the
279280
/// platform side.
280281
Future<void> animateCamera(CameraUpdate cameraUpdate, {Duration? duration}) {
282+
_checkWidgetMountedOrThrow();
281283
return GoogleMapsFlutterPlatform.instance.animateCameraWithConfiguration(
282284
cameraUpdate,
283285
CameraUpdateAnimationConfiguration(duration: duration),
@@ -290,6 +292,7 @@ class GoogleMapController {
290292
/// The returned [Future] completes after the change has been made on the
291293
/// platform side.
292294
Future<void> moveCamera(CameraUpdate cameraUpdate) {
295+
_checkWidgetMountedOrThrow();
293296
return GoogleMapsFlutterPlatform.instance.moveCamera(
294297
cameraUpdate,
295298
mapId: mapId,
@@ -311,6 +314,7 @@ class GoogleMapController {
311314
/// style reference for more information regarding the supported styles.
312315
@Deprecated('Use GoogleMap.style instead.')
313316
Future<void> setMapStyle(String? mapStyle) {
317+
_checkWidgetMountedOrThrow();
314318
return GoogleMapsFlutterPlatform.instance.setMapStyle(
315319
mapStyle,
316320
mapId: mapId,
@@ -319,11 +323,13 @@ class GoogleMapController {
319323

320324
/// Returns the last style error, if any.
321325
Future<String?> getStyleError() {
326+
_checkWidgetMountedOrThrow();
322327
return GoogleMapsFlutterPlatform.instance.getStyleError(mapId: mapId);
323328
}
324329

325330
/// Return [LatLngBounds] defining the region that is visible in a map.
326331
Future<LatLngBounds> getVisibleRegion() {
332+
_checkWidgetMountedOrThrow();
327333
return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId);
328334
}
329335

@@ -333,6 +339,7 @@ class GoogleMapController {
333339
/// Screen location is in screen pixels (not display pixels) with respect to the top left corner
334340
/// of the map, not necessarily of the whole screen.
335341
Future<ScreenCoordinate> getScreenCoordinate(LatLng latLng) {
342+
_checkWidgetMountedOrThrow();
336343
return GoogleMapsFlutterPlatform.instance.getScreenCoordinate(
337344
latLng,
338345
mapId: mapId,
@@ -344,6 +351,7 @@ class GoogleMapController {
344351
/// Returned [LatLng] corresponds to a screen location. The screen location is specified in screen
345352
/// pixels (not display pixels) relative to the top left of the map, not top left of the whole screen.
346353
Future<LatLng> getLatLng(ScreenCoordinate screenCoordinate) {
354+
_checkWidgetMountedOrThrow();
347355
return GoogleMapsFlutterPlatform.instance.getLatLng(
348356
screenCoordinate,
349357
mapId: mapId,
@@ -359,6 +367,7 @@ class GoogleMapController {
359367
/// * [hideMarkerInfoWindow] to hide the Info Window.
360368
/// * [isMarkerInfoWindowShown] to check if the Info Window is showing.
361369
Future<void> showMarkerInfoWindow(MarkerId markerId) {
370+
_checkWidgetMountedOrThrow();
362371
return GoogleMapsFlutterPlatform.instance.showMarkerInfoWindow(
363372
markerId,
364373
mapId: mapId,
@@ -374,6 +383,7 @@ class GoogleMapController {
374383
/// * [showMarkerInfoWindow] to show the Info Window.
375384
/// * [isMarkerInfoWindowShown] to check if the Info Window is showing.
376385
Future<void> hideMarkerInfoWindow(MarkerId markerId) {
386+
_checkWidgetMountedOrThrow();
377387
return GoogleMapsFlutterPlatform.instance.hideMarkerInfoWindow(
378388
markerId,
379389
mapId: mapId,
@@ -389,6 +399,7 @@ class GoogleMapController {
389399
/// * [showMarkerInfoWindow] to show the Info Window.
390400
/// * [hideMarkerInfoWindow] to hide the Info Window.
391401
Future<bool> isMarkerInfoWindowShown(MarkerId markerId) {
402+
_checkWidgetMountedOrThrow();
392403
return GoogleMapsFlutterPlatform.instance.isMarkerInfoWindowShown(
393404
markerId,
394405
mapId: mapId,
@@ -397,11 +408,13 @@ class GoogleMapController {
397408

398409
/// Returns the current zoom level of the map
399410
Future<double> getZoomLevel() {
411+
_checkWidgetMountedOrThrow();
400412
return GoogleMapsFlutterPlatform.instance.getZoomLevel(mapId: mapId);
401413
}
402414

403415
/// Returns the image bytes of the map
404416
Future<Uint8List?> takeSnapshot() {
417+
_checkWidgetMountedOrThrow();
405418
return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId);
406419
}
407420

@@ -414,4 +427,40 @@ class GoogleMapController {
414427
_streamSubscriptions.clear();
415428
GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId);
416429
}
430+
431+
/// It is relatively easy to mistakenly call a method on the controller
432+
/// after the [GoogleMap] widget has already been disposed.
433+
/// Historically, this led to Platform-side errors such as
434+
/// `MissingPluginException` or `Unable to establish connection on channel`
435+
/// errors.
436+
///
437+
/// To facilitate debugging, this guard function
438+
/// raises [MapUsedAfterWidgetDisposedError].
439+
void _checkWidgetMountedOrThrow() {
440+
if (!_googleMapState.mounted) {
441+
throw MapUsedAfterWidgetDisposedError(mapId: mapId);
442+
}
443+
}
444+
}
445+
446+
/// Error thrown when any [GoogleMapController] method is called after
447+
/// its associated [GoogleMap] widget has been disposed.
448+
///
449+
/// To avoid this error:
450+
///
451+
/// 1. Set the map controller field to `null` in your widget state's
452+
/// `dispose()` method, or
453+
/// 2. Check the [State.mounted] state before each use of the controller.
454+
class MapUsedAfterWidgetDisposedError extends Error {
455+
/// Creates the use-after-disposed error for the provided [mapId].
456+
MapUsedAfterWidgetDisposedError({required this.mapId});
457+
458+
/// The map ID of the map for which this error is being raised.
459+
final int mapId;
460+
461+
@override
462+
String toString() {
463+
return 'GoogleMapController for map ID $mapId was used after '
464+
'the associated GoogleMap widget had already been disposed.';
465+
}
417466
}

0 commit comments

Comments
 (0)