Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.13.1

* Fixes exception when dispose is called while asynchronous update from
`didUpdateWidget` is executed.

## 2.13.0

* Adds support for camera control button on web.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,30 +448,38 @@ class _GoogleMapState extends State<GoogleMap> {
@override
void didUpdateWidget(GoogleMap oldWidget) {
super.didUpdateWidget(oldWidget);
_updateOptions();
_updateClusterManagers();
_updateMarkers();
_updatePolygons();
_updatePolylines();
_updateCircles();
_updateHeatmaps();
_updateTileOverlays();
_updateGroundOverlays();

_refreshStateFromWidget();
}

Future<void> _refreshStateFromWidget() async {
final GoogleMapController controller = await _controller.future;
if (!mounted) {
return;
}

_updateOptions(controller);
_updateClusterManagers(controller);
_updateMarkers(controller);
_updatePolygons(controller);
_updatePolylines(controller);
_updateCircles(controller);
_updateHeatmaps(controller);
_updateTileOverlays(controller);
_updateGroundOverlays(controller);
}

Future<void> _updateOptions() async {
void _updateOptions(GoogleMapController controller) {
final MapConfiguration newConfig = _configurationFromMapWidget(widget);
final MapConfiguration updates = newConfig.diffFrom(_mapConfiguration);
if (updates.isEmpty) {
return;
}
final GoogleMapController controller = await _controller.future;
unawaited(controller._updateMapConfiguration(updates));
_mapConfiguration = newConfig;
}

Future<void> _updateMarkers() async {
final GoogleMapController controller = await _controller.future;
void _updateMarkers(GoogleMapController controller) {
unawaited(
controller._updateMarkers(
MarkerUpdates.from(_markers.values.toSet(), widget.markers),
Expand All @@ -480,8 +488,7 @@ class _GoogleMapState extends State<GoogleMap> {
_markers = keyByMarkerId(widget.markers);
}

Future<void> _updateClusterManagers() async {
final GoogleMapController controller = await _controller.future;
void _updateClusterManagers(GoogleMapController controller) {
unawaited(
controller._updateClusterManagers(
ClusterManagerUpdates.from(
Expand All @@ -493,8 +500,7 @@ class _GoogleMapState extends State<GoogleMap> {
_clusterManagers = keyByClusterManagerId(widget.clusterManagers);
}

Future<void> _updateGroundOverlays() async {
final GoogleMapController controller = await _controller.future;
void _updateGroundOverlays(GoogleMapController controller) {
unawaited(
controller._updateGroundOverlays(
GroundOverlayUpdates.from(
Expand All @@ -506,8 +512,7 @@ class _GoogleMapState extends State<GoogleMap> {
_groundOverlays = keyByGroundOverlayId(widget.groundOverlays);
}

Future<void> _updatePolygons() async {
final GoogleMapController controller = await _controller.future;
void _updatePolygons(GoogleMapController controller) {
unawaited(
controller._updatePolygons(
PolygonUpdates.from(_polygons.values.toSet(), widget.polygons),
Expand All @@ -516,8 +521,7 @@ class _GoogleMapState extends State<GoogleMap> {
_polygons = keyByPolygonId(widget.polygons);
}

Future<void> _updatePolylines() async {
final GoogleMapController controller = await _controller.future;
void _updatePolylines(GoogleMapController controller) {
unawaited(
controller._updatePolylines(
PolylineUpdates.from(_polylines.values.toSet(), widget.polylines),
Expand All @@ -526,8 +530,7 @@ class _GoogleMapState extends State<GoogleMap> {
_polylines = keyByPolylineId(widget.polylines);
}

Future<void> _updateCircles() async {
final GoogleMapController controller = await _controller.future;
void _updateCircles(GoogleMapController controller) {
unawaited(
controller._updateCircles(
CircleUpdates.from(_circles.values.toSet(), widget.circles),
Expand All @@ -536,8 +539,7 @@ class _GoogleMapState extends State<GoogleMap> {
_circles = keyByCircleId(widget.circles);
}

Future<void> _updateHeatmaps() async {
final GoogleMapController controller = await _controller.future;
void _updateHeatmaps(GoogleMapController controller) {
unawaited(
controller._updateHeatmaps(
HeatmapUpdates.from(_heatmaps.values.toSet(), widget.heatmaps),
Expand All @@ -546,8 +548,7 @@ class _GoogleMapState extends State<GoogleMap> {
_heatmaps = keyByHeatmapId(widget.heatmaps);
}

Future<void> _updateTileOverlays() async {
final GoogleMapController controller = await _controller.future;
void _updateTileOverlays(GoogleMapController controller) {
unawaited(controller._updateTileOverlays(widget.tileOverlays));
}

Expand All @@ -558,10 +559,12 @@ class _GoogleMapState extends State<GoogleMap> {
this,
);
_controller.complete(controller);
unawaited(_updateTileOverlays());
final MapCreatedCallback? onMapCreated = widget.onMapCreated;
if (onMapCreated != null) {
onMapCreated(controller);
if (mounted) {
_updateTileOverlays(controller);
final MapCreatedCallback? onMapCreated = widget.onMapCreated;
if (onMapCreated != null) {
onMapCreated(controller);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: google_maps_flutter
description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
version: 2.13.0
version: 2.13.1

environment:
sdk: ^3.7.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform {
final StreamController<MapEvent<dynamic>> mapEventStreamController =
StreamController<MapEvent<dynamic>>.broadcast();

// Overrides completion of the init.
Completer<void>? initCompleter;

@override
Future<void> init(int mapId) async {}
Future<void> init(int mapId) async => initCompleter?.future;

@override
Future<void> updateMapConfiguration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
Expand Down Expand Up @@ -598,4 +600,63 @@ void main() {

expect(map.mapConfiguration.style, '');
});

testWidgets('Update state from widget only when mounted', (
WidgetTester tester,
) async {
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
child: GoogleMap(
initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)),
),
),
);

final State<StatefulWidget> googleMapState = tester.state(
find.byType(GoogleMap),
);

await tester.pumpWidget(Container());

// This is done to force the update path while the widget is not mounted.
// ignore:invalid_use_of_protected_member
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If something is needed for testing, it should be annotated as such, rather than having warnings suppressed at the usage site.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, I see why it has to be done this way.

googleMapState.didUpdateWidget(
GoogleMap(
initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
circles: <Circle>{const Circle(circleId: CircleId('circle'))},
),
);

await tester.pumpAndSettle();

final PlatformMapStateRecorder map = platform.lastCreatedMap;

expect(map.circleUpdates.length, 1);
});

testWidgets('Update state after map is initialized only when mounted', (
WidgetTester tester,
) async {
platform.initCompleter = Completer<void>();

await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
child: GoogleMap(
initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)),
),
),
);

await tester.pumpWidget(Container());

platform.initCompleter!.complete();

await tester.pumpAndSettle();

final PlatformMapStateRecorder map = platform.lastCreatedMap;

expect(map.tileOverlaySets.length, 1);
});
}