[google_maps_flutter] Clone cached elements in GoogleMap (#2076)
Create a clone of cached elements in GoogleMap (Polyline, Polygon, etc.)
to detect modifications if these objects are mutated instead of
modified by copy.
diff --git a/packages/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/CHANGELOG.md
index 56fbf0e..45ecfbe 100644
--- a/packages/google_maps_flutter/CHANGELOG.md
+++ b/packages/google_maps_flutter/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.5.21+7
+
+* Create a clone of cached elements in GoogleMap (Polyline, Polygon, etc.) to detect modifications
+ if these objects are mutated instead of modified by copy.
+
## 0.5.21+6
* Override a default method to work around flutter/flutter#40126.
diff --git a/packages/google_maps_flutter/lib/src/circle.dart b/packages/google_maps_flutter/lib/src/circle.dart
index eefb8c0..1ab966c 100644
--- a/packages/google_maps_flutter/lib/src/circle.dart
+++ b/packages/google_maps_flutter/lib/src/circle.dart
@@ -114,6 +114,9 @@
);
}
+ /// Creates a new [Circle] object whose values are the same as this instance.
+ Circle clone() => copyWith();
+
dynamic _toJson() {
final Map<String, dynamic> json = <String, dynamic>{};
@@ -161,8 +164,8 @@
if (circles == null) {
return <CircleId, Circle>{};
}
- return Map<CircleId, Circle>.fromEntries(circles.map(
- (Circle circle) => MapEntry<CircleId, Circle>(circle.circleId, circle)));
+ return Map<CircleId, Circle>.fromEntries(circles.map((Circle circle) =>
+ MapEntry<CircleId, Circle>(circle.circleId, circle.clone())));
}
List<Map<String, dynamic>> _serializeCircleSet(Set<Circle> circles) {
diff --git a/packages/google_maps_flutter/lib/src/marker.dart b/packages/google_maps_flutter/lib/src/marker.dart
index 4a087f7..5d4d4f3 100644
--- a/packages/google_maps_flutter/lib/src/marker.dart
+++ b/packages/google_maps_flutter/lib/src/marker.dart
@@ -254,6 +254,9 @@
);
}
+ /// Creates a new [Marker] object whose values are the same as this instance.
+ Marker clone() => copyWith();
+
Map<String, dynamic> _toJson() {
final Map<String, dynamic> json = <String, dynamic>{};
@@ -314,8 +317,8 @@
if (markers == null) {
return <MarkerId, Marker>{};
}
- return Map<MarkerId, Marker>.fromEntries(markers.map(
- (Marker marker) => MapEntry<MarkerId, Marker>(marker.markerId, marker)));
+ return Map<MarkerId, Marker>.fromEntries(markers.map((Marker marker) =>
+ MapEntry<MarkerId, Marker>(marker.markerId, marker.clone())));
}
List<Map<String, dynamic>> _serializeMarkerSet(Set<Marker> markers) {
diff --git a/packages/google_maps_flutter/lib/src/polygon.dart b/packages/google_maps_flutter/lib/src/polygon.dart
index 2230ae8..4aed3bd 100644
--- a/packages/google_maps_flutter/lib/src/polygon.dart
+++ b/packages/google_maps_flutter/lib/src/polygon.dart
@@ -120,6 +120,11 @@
);
}
+ /// Creates a new [Polygon] object whose values are the same as this instance.
+ Polygon clone() {
+ return copyWith(pointsParam: List<LatLng>.of(points));
+ }
+
dynamic _toJson() {
final Map<String, dynamic> json = <String, dynamic>{};
@@ -179,7 +184,7 @@
return <PolygonId, Polygon>{};
}
return Map<PolygonId, Polygon>.fromEntries(polygons.map((Polygon polygon) =>
- MapEntry<PolygonId, Polygon>(polygon.polygonId, polygon)));
+ MapEntry<PolygonId, Polygon>(polygon.polygonId, polygon.clone())));
}
List<Map<String, dynamic>> _serializePolygonSet(Set<Polygon> polygons) {
diff --git a/packages/google_maps_flutter/lib/src/polyline.dart b/packages/google_maps_flutter/lib/src/polyline.dart
index 7454711..1eac145 100644
--- a/packages/google_maps_flutter/lib/src/polyline.dart
+++ b/packages/google_maps_flutter/lib/src/polyline.dart
@@ -156,6 +156,15 @@
);
}
+ /// Creates a new [Polyline] object whose values are the same as this
+ /// instance.
+ Polyline clone() {
+ return copyWith(
+ patternsParam: List<PatternItem>.of(patterns),
+ pointsParam: List<LatLng>.of(points),
+ );
+ }
+
dynamic _toJson() {
final Map<String, dynamic> json = <String, dynamic>{};
@@ -234,8 +243,8 @@
return <PolylineId, Polyline>{};
}
return Map<PolylineId, Polyline>.fromEntries(polylines.map(
- (Polyline polyline) =>
- MapEntry<PolylineId, Polyline>(polyline.polylineId, polyline)));
+ (Polyline polyline) => MapEntry<PolylineId, Polyline>(
+ polyline.polylineId, polyline.clone())));
}
List<Map<String, dynamic>> _serializePolylineSet(Set<Polyline> polylines) {
diff --git a/packages/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/pubspec.yaml
index 71d870f..5e8e92e 100644
--- a/packages/google_maps_flutter/pubspec.yaml
+++ b/packages/google_maps_flutter/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter
-version: 0.5.21+6
+version: 0.5.21+7
dependencies:
flutter:
diff --git a/packages/google_maps_flutter/test/fake_maps_controllers.dart b/packages/google_maps_flutter/test/fake_maps_controllers.dart
index 0ae12c8..b92b841 100644
--- a/packages/google_maps_flutter/test/fake_maps_controllers.dart
+++ b/packages/google_maps_flutter/test/fake_maps_controllers.dart
@@ -195,17 +195,25 @@
final String polygonId = polygonData['polygonId'];
final bool visible = polygonData['visible'];
final bool geodesic = polygonData['geodesic'];
+ final List<LatLng> points = _deserializePoints(polygonData['points']);
result.add(Polygon(
polygonId: PolygonId(polygonId),
visible: visible,
geodesic: geodesic,
+ points: points,
));
}
return result;
}
+ List<LatLng> _deserializePoints(List<dynamic> points) {
+ return points.map<LatLng>((dynamic list) {
+ return LatLng(list[0], list[1]);
+ }).toList();
+ }
+
void updatePolylines(Map<dynamic, dynamic> polylineUpdates) {
if (polylineUpdates == null) {
return;
@@ -245,11 +253,13 @@
final String polylineId = polylineData['polylineId'];
final bool visible = polylineData['visible'];
final bool geodesic = polylineData['geodesic'];
+ final List<LatLng> points = _deserializePoints(polylineData['points']);
result.add(Polyline(
polylineId: PolylineId(polylineId),
visible: visible,
geodesic: geodesic,
+ points: points,
));
}
diff --git a/packages/google_maps_flutter/test/polygon_updates_test.dart b/packages/google_maps_flutter/test/polygon_updates_test.dart
index c666cb6..a884254 100644
--- a/packages/google_maps_flutter/test/polygon_updates_test.dart
+++ b/packages/google_maps_flutter/test/polygon_updates_test.dart
@@ -130,6 +130,25 @@
expect(update.geodesic, true);
});
+ testWidgets("Mutate a polygon", (WidgetTester tester) async {
+ final Polygon p1 = Polygon(
+ polygonId: PolygonId("polygon_1"),
+ points: <LatLng>[const LatLng(0.0, 0.0)],
+ );
+ await tester.pumpWidget(_mapWithPolygons(_toSet(p1: p1)));
+
+ p1.points.add(const LatLng(1.0, 1.0));
+ await tester.pumpWidget(_mapWithPolygons(_toSet(p1: p1)));
+
+ final FakePlatformGoogleMap platformGoogleMap =
+ fakePlatformViewsController.lastCreatedView;
+ expect(platformGoogleMap.polygonsToChange.length, 1);
+ expect(platformGoogleMap.polygonsToChange.first, equals(p1));
+
+ expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true);
+ expect(platformGoogleMap.polygonsToAdd.isEmpty, true);
+ });
+
testWidgets("Multi Update", (WidgetTester tester) async {
Polygon p1 = Polygon(polygonId: PolygonId("polygon_1"));
Polygon p2 = Polygon(polygonId: PolygonId("polygon_2"));
diff --git a/packages/google_maps_flutter/test/polyline_updates_test.dart b/packages/google_maps_flutter/test/polyline_updates_test.dart
index a0f3918..ddc64bf 100644
--- a/packages/google_maps_flutter/test/polyline_updates_test.dart
+++ b/packages/google_maps_flutter/test/polyline_updates_test.dart
@@ -130,6 +130,25 @@
expect(update.geodesic, true);
});
+ testWidgets("Mutate a polyline", (WidgetTester tester) async {
+ final Polyline p1 = Polyline(
+ polylineId: PolylineId("polyline_1"),
+ points: <LatLng>[const LatLng(0.0, 0.0)],
+ );
+ await tester.pumpWidget(_mapWithPolylines(_toSet(p1: p1)));
+
+ p1.points.add(const LatLng(1.0, 1.0));
+ await tester.pumpWidget(_mapWithPolylines(_toSet(p1: p1)));
+
+ final FakePlatformGoogleMap platformGoogleMap =
+ fakePlatformViewsController.lastCreatedView;
+ expect(platformGoogleMap.polylinesToChange.length, 1);
+ expect(platformGoogleMap.polylinesToChange.first, equals(p1));
+
+ expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true);
+ expect(platformGoogleMap.polylinesToAdd.isEmpty, true);
+ });
+
testWidgets("Multi Update", (WidgetTester tester) async {
Polyline p1 = Polyline(polylineId: PolylineId("polyline_1"));
Polyline p2 = Polyline(polylineId: PolylineId("polyline_2"));