diff --git a/packages/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/CHANGELOG.md index 56fbf0ee4a6e..45ecfbeb3e3d 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 eefb8c021fa6..1ab966cc6957 100644 --- a/packages/google_maps_flutter/lib/src/circle.dart +++ b/packages/google_maps_flutter/lib/src/circle.dart @@ -114,6 +114,9 @@ class Circle { ); } + /// Creates a new [Circle] object whose values are the same as this instance. + Circle clone() => copyWith(); + dynamic _toJson() { final Map json = {}; @@ -161,8 +164,8 @@ Map _keyByCircleId(Iterable circles) { if (circles == null) { return {}; } - return Map.fromEntries(circles.map( - (Circle circle) => MapEntry(circle.circleId, circle))); + return Map.fromEntries(circles.map((Circle circle) => + MapEntry(circle.circleId, circle.clone()))); } List> _serializeCircleSet(Set circles) { diff --git a/packages/google_maps_flutter/lib/src/marker.dart b/packages/google_maps_flutter/lib/src/marker.dart index 4a087f797cdf..5d4d4f393c33 100644 --- a/packages/google_maps_flutter/lib/src/marker.dart +++ b/packages/google_maps_flutter/lib/src/marker.dart @@ -254,6 +254,9 @@ class Marker { ); } + /// Creates a new [Marker] object whose values are the same as this instance. + Marker clone() => copyWith(); + Map _toJson() { final Map json = {}; @@ -314,8 +317,8 @@ Map _keyByMarkerId(Iterable markers) { if (markers == null) { return {}; } - return Map.fromEntries(markers.map( - (Marker marker) => MapEntry(marker.markerId, marker))); + return Map.fromEntries(markers.map((Marker marker) => + MapEntry(marker.markerId, marker.clone()))); } List> _serializeMarkerSet(Set markers) { diff --git a/packages/google_maps_flutter/lib/src/polygon.dart b/packages/google_maps_flutter/lib/src/polygon.dart index 2230ae81afaf..4aed3bd74bdd 100644 --- a/packages/google_maps_flutter/lib/src/polygon.dart +++ b/packages/google_maps_flutter/lib/src/polygon.dart @@ -120,6 +120,11 @@ class Polygon { ); } + /// Creates a new [Polygon] object whose values are the same as this instance. + Polygon clone() { + return copyWith(pointsParam: List.of(points)); + } + dynamic _toJson() { final Map json = {}; @@ -179,7 +184,7 @@ Map _keyByPolygonId(Iterable polygons) { return {}; } return Map.fromEntries(polygons.map((Polygon polygon) => - MapEntry(polygon.polygonId, polygon))); + MapEntry(polygon.polygonId, polygon.clone()))); } List> _serializePolygonSet(Set polygons) { diff --git a/packages/google_maps_flutter/lib/src/polyline.dart b/packages/google_maps_flutter/lib/src/polyline.dart index 7454711dd6b1..1eac1458fa7b 100644 --- a/packages/google_maps_flutter/lib/src/polyline.dart +++ b/packages/google_maps_flutter/lib/src/polyline.dart @@ -156,6 +156,15 @@ class Polyline { ); } + /// Creates a new [Polyline] object whose values are the same as this + /// instance. + Polyline clone() { + return copyWith( + patternsParam: List.of(patterns), + pointsParam: List.of(points), + ); + } + dynamic _toJson() { final Map json = {}; @@ -234,8 +243,8 @@ Map _keyByPolylineId(Iterable polylines) { return {}; } return Map.fromEntries(polylines.map( - (Polyline polyline) => - MapEntry(polyline.polylineId, polyline))); + (Polyline polyline) => MapEntry( + polyline.polylineId, polyline.clone()))); } List> _serializePolylineSet(Set polylines) { diff --git a/packages/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/pubspec.yaml index 71d870f619a2..5e8e92eb6740 100644 --- a/packages/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. author: Flutter Team 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 0ae12c815ff3..b92b841497aa 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 @@ class FakePlatformGoogleMap { final String polygonId = polygonData['polygonId']; final bool visible = polygonData['visible']; final bool geodesic = polygonData['geodesic']; + final List points = _deserializePoints(polygonData['points']); result.add(Polygon( polygonId: PolygonId(polygonId), visible: visible, geodesic: geodesic, + points: points, )); } return result; } + List _deserializePoints(List points) { + return points.map((dynamic list) { + return LatLng(list[0], list[1]); + }).toList(); + } + void updatePolylines(Map polylineUpdates) { if (polylineUpdates == null) { return; @@ -245,11 +253,13 @@ class FakePlatformGoogleMap { final String polylineId = polylineData['polylineId']; final bool visible = polylineData['visible']; final bool geodesic = polylineData['geodesic']; + final List 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 c666cb687fee..a884254f0587 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 @@ void main() { expect(update.geodesic, true); }); + testWidgets("Mutate a polygon", (WidgetTester tester) async { + final Polygon p1 = Polygon( + polygonId: PolygonId("polygon_1"), + points: [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 a0f39185d472..ddc64bf6158b 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 @@ void main() { expect(update.geodesic, true); }); + testWidgets("Mutate a polyline", (WidgetTester tester) async { + final Polyline p1 = Polyline( + polylineId: PolylineId("polyline_1"), + points: [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"));