diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index e244734a438..6edda3e0804 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 2.3.1 +* Fixes a regression from 2.2.8 that could cause incorrect handling of a + rapid series of map object updates. * Fixes stale ignore: prefer_const_constructors. * Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart index e1b710c307a..08c2286527f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart @@ -360,41 +360,41 @@ class _GoogleMapState extends State { return; } final GoogleMapController controller = await _controller.future; - await controller._updateMapConfiguration(updates); + unawaited(controller._updateMapConfiguration(updates)); _mapConfiguration = newConfig; } Future _updateMarkers() async { final GoogleMapController controller = await _controller.future; - await controller._updateMarkers( - MarkerUpdates.from(_markers.values.toSet(), widget.markers)); + unawaited(controller._updateMarkers( + MarkerUpdates.from(_markers.values.toSet(), widget.markers))); _markers = keyByMarkerId(widget.markers); } Future _updatePolygons() async { final GoogleMapController controller = await _controller.future; - await controller._updatePolygons( - PolygonUpdates.from(_polygons.values.toSet(), widget.polygons)); + unawaited(controller._updatePolygons( + PolygonUpdates.from(_polygons.values.toSet(), widget.polygons))); _polygons = keyByPolygonId(widget.polygons); } Future _updatePolylines() async { final GoogleMapController controller = await _controller.future; - await controller._updatePolylines( - PolylineUpdates.from(_polylines.values.toSet(), widget.polylines)); + unawaited(controller._updatePolylines( + PolylineUpdates.from(_polylines.values.toSet(), widget.polylines))); _polylines = keyByPolylineId(widget.polylines); } Future _updateCircles() async { final GoogleMapController controller = await _controller.future; - await controller._updateCircles( - CircleUpdates.from(_circles.values.toSet(), widget.circles)); + unawaited(controller._updateCircles( + CircleUpdates.from(_circles.values.toSet(), widget.circles))); _circles = keyByCircleId(widget.circles); } Future _updateTileOverlays() async { final GoogleMapController controller = await _controller.future; - await controller._updateTileOverlays(widget.tileOverlays); + unawaited(controller._updateTileOverlays(widget.tileOverlays)); } Future onPlatformViewCreated(int id) async { diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 91bc476fda8..e56ff513e7a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/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. 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.3.0 +version: 2.3.1 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart index 459e16b60c4..f94caf1f583 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'fake_maps_controllers.dart'; +import 'fake_google_maps_flutter_platform.dart'; Widget _mapWithCircles(Set circles) { return Directionality( @@ -20,36 +20,24 @@ Widget _mapWithCircles(Set circles) { } void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final FakePlatformViewsController fakePlatformViewsController = - FakePlatformViewsController(); - - setUpAll(() { - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform_views, - fakePlatformViewsController.fakePlatformViewsMethodHandler, - ); - }); + late FakeGoogleMapsFlutterPlatform platform; setUp(() { - fakePlatformViewsController.reset(); + platform = FakeGoogleMapsFlutterPlatform(); + GoogleMapsFlutterPlatform.instance = platform; }); testWidgets('Initializing a circle', (WidgetTester tester) async { const Circle c1 = Circle(circleId: CircleId('circle_1')); await tester.pumpWidget(_mapWithCircles({c1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.circlesToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.circleUpdates.last.circlesToAdd.length, 1); - final Circle initializedCircle = platformGoogleMap.circlesToAdd.first; + final Circle initializedCircle = map.circleUpdates.last.circlesToAdd.first; expect(initializedCircle, equals(c1)); - expect(platformGoogleMap.circleIdsToRemove.isEmpty, true); - expect(platformGoogleMap.circlesToChange.isEmpty, true); + expect(map.circleUpdates.last.circleIdsToRemove.isEmpty, true); + expect(map.circleUpdates.last.circlesToChange.isEmpty, true); }); testWidgets('Adding a circle', (WidgetTester tester) async { @@ -59,16 +47,15 @@ void main() { await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({c1, c2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.circlesToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.circleUpdates.last.circlesToAdd.length, 1); - final Circle addedCircle = platformGoogleMap.circlesToAdd.first; + final Circle addedCircle = map.circleUpdates.last.circlesToAdd.first; expect(addedCircle, equals(c2)); - expect(platformGoogleMap.circleIdsToRemove.isEmpty, true); + expect(map.circleUpdates.last.circleIdsToRemove.isEmpty, true); - expect(platformGoogleMap.circlesToChange.isEmpty, true); + expect(map.circleUpdates.last.circlesToChange.isEmpty, true); }); testWidgets('Removing a circle', (WidgetTester tester) async { @@ -77,13 +64,12 @@ void main() { await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.circleIdsToRemove.length, 1); - expect(platformGoogleMap.circleIdsToRemove.first, equals(c1.circleId)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.circleUpdates.last.circleIdsToRemove.length, 1); + expect(map.circleUpdates.last.circleIdsToRemove.first, equals(c1.circleId)); - expect(platformGoogleMap.circlesToChange.isEmpty, true); - expect(platformGoogleMap.circlesToAdd.isEmpty, true); + expect(map.circleUpdates.last.circlesToChange.isEmpty, true); + expect(map.circleUpdates.last.circlesToAdd.isEmpty, true); }); testWidgets('Updating a circle', (WidgetTester tester) async { @@ -93,13 +79,12 @@ void main() { await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({c2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.circlesToChange.length, 1); - expect(platformGoogleMap.circlesToChange.first, equals(c2)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.circleUpdates.last.circlesToChange.length, 1); + expect(map.circleUpdates.last.circlesToChange.first, equals(c2)); - expect(platformGoogleMap.circleIdsToRemove.isEmpty, true); - expect(platformGoogleMap.circlesToAdd.isEmpty, true); + expect(map.circleUpdates.last.circleIdsToRemove.isEmpty, true); + expect(map.circleUpdates.last.circlesToAdd.isEmpty, true); }); testWidgets('Updating a circle', (WidgetTester tester) async { @@ -109,11 +94,10 @@ void main() { await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({c2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.circlesToChange.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.circleUpdates.last.circlesToChange.length, 1); - final Circle update = platformGoogleMap.circlesToChange.first; + final Circle update = map.circleUpdates.last.circlesToChange.first; expect(update, equals(c2)); expect(update.radius, 10); }); @@ -129,12 +113,11 @@ void main() { await tester.pumpWidget(_mapWithCircles(prev)); await tester.pumpWidget(_mapWithCircles(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.circlesToChange, cur); - expect(platformGoogleMap.circleIdsToRemove.isEmpty, true); - expect(platformGoogleMap.circlesToAdd.isEmpty, true); + expect(map.circleUpdates.last.circlesToChange, cur); + expect(map.circleUpdates.last.circleIdsToRemove.isEmpty, true); + expect(map.circleUpdates.last.circlesToAdd.isEmpty, true); }); testWidgets('Multi Update', (WidgetTester tester) async { @@ -150,16 +133,15 @@ void main() { await tester.pumpWidget(_mapWithCircles(prev)); await tester.pumpWidget(_mapWithCircles(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.circlesToChange.length, 1); - expect(platformGoogleMap.circlesToAdd.length, 1); - expect(platformGoogleMap.circleIdsToRemove.length, 1); + expect(map.circleUpdates.last.circlesToChange.length, 1); + expect(map.circleUpdates.last.circlesToAdd.length, 1); + expect(map.circleUpdates.last.circleIdsToRemove.length, 1); - expect(platformGoogleMap.circlesToChange.first, equals(c2)); - expect(platformGoogleMap.circlesToAdd.first, equals(c1)); - expect(platformGoogleMap.circleIdsToRemove.first, equals(c3.circleId)); + expect(map.circleUpdates.last.circlesToChange.first, equals(c2)); + expect(map.circleUpdates.last.circlesToAdd.first, equals(c1)); + expect(map.circleUpdates.last.circleIdsToRemove.first, equals(c3.circleId)); }); testWidgets('Partial Update', (WidgetTester tester) async { @@ -173,12 +155,11 @@ void main() { await tester.pumpWidget(_mapWithCircles(prev)); await tester.pumpWidget(_mapWithCircles(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.circlesToChange, {c3}); - expect(platformGoogleMap.circleIdsToRemove.isEmpty, true); - expect(platformGoogleMap.circlesToAdd.isEmpty, true); + expect(map.circleUpdates.last.circlesToChange, {c3}); + expect(map.circleUpdates.last.circleIdsToRemove.isEmpty, true); + expect(map.circleUpdates.last.circlesToAdd.isEmpty, true); }); testWidgets('Update non platform related attr', (WidgetTester tester) async { @@ -190,17 +171,42 @@ void main() { await tester.pumpWidget(_mapWithCircles(prev)); await tester.pumpWidget(_mapWithCircles(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.circlesToChange.isEmpty, true); - expect(platformGoogleMap.circleIdsToRemove.isEmpty, true); - expect(platformGoogleMap.circlesToAdd.isEmpty, true); + expect(map.circleUpdates.last.circlesToChange.isEmpty, true); + expect(map.circleUpdates.last.circleIdsToRemove.isEmpty, true); + expect(map.circleUpdates.last.circlesToAdd.isEmpty, true); }); -} -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; + testWidgets('multi-update with delays', (WidgetTester tester) async { + platform.simulatePlatformDelay = true; + + const Circle c1 = Circle(circleId: CircleId('circle_1')); + const Circle c2 = Circle(circleId: CircleId('circle_2')); + const Circle c3 = Circle(circleId: CircleId('circle_3'), radius: 1); + const Circle c3updated = Circle(circleId: CircleId('circle_3'), radius: 10); + + // First remove one and add another, then update the new one. + await tester.pumpWidget(_mapWithCircles({c1, c2})); + await tester.pumpWidget(_mapWithCircles({c1, c3})); + await tester.pumpWidget(_mapWithCircles({c1, c3updated})); + + final PlatformMapStateRecorder map = platform.lastCreatedMap; + + expect(map.circleUpdates.length, 3); + + expect(map.circleUpdates[0].circlesToChange.isEmpty, true); + expect(map.circleUpdates[0].circlesToAdd, {c1, c2}); + expect(map.circleUpdates[0].circleIdsToRemove.isEmpty, true); + + expect(map.circleUpdates[1].circlesToChange.isEmpty, true); + expect(map.circleUpdates[1].circlesToAdd, {c3}); + expect(map.circleUpdates[1].circleIdsToRemove, {c2.circleId}); + + expect(map.circleUpdates[2].circlesToChange, {c3updated}); + expect(map.circleUpdates[2].circlesToAdd.isEmpty, true); + expect(map.circleUpdates[2].circleIdsToRemove.isEmpty, true); + + await tester.pumpAndSettle(); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter/test/fake_google_maps_flutter_platform.dart new file mode 100644 index 00000000000..22447ba5eca --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/test/fake_google_maps_flutter_platform.dart @@ -0,0 +1,303 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// 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/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:stream_transform/stream_transform.dart'; + +// A dummy implementation of the platform interface for tests. +class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { + FakeGoogleMapsFlutterPlatform(); + + /// The IDs passed to each call to buildView, in call order. + List createdIds = []; + + /// A map of creation IDs to fake map instances. + Map mapInstances = + {}; + + PlatformMapStateRecorder get lastCreatedMap => mapInstances[createdIds.last]!; + + /// Whether to add a small delay to async calls to simulate more realistic + /// async behavior (simulating the platform channel calls most + /// implementations will do). + /// + /// When true, requires tests to `pumpAndSettle` at the end of the test + /// to avoid exceptions. + bool simulatePlatformDelay = false; + + /// Whether `dispose` has been called. + bool disposed = false; + + /// Stream controller to inject events for testing. + final StreamController> mapEventStreamController = + StreamController>.broadcast(); + + @override + Future init(int mapId) async {} + + @override + Future updateMapConfiguration( + MapConfiguration update, { + required int mapId, + }) async { + mapInstances[mapId]?.mapConfiguration = update; + await _fakeDelay(); + } + + @override + Future updateMarkers( + MarkerUpdates markerUpdates, { + required int mapId, + }) async { + mapInstances[mapId]?.markerUpdates.add(markerUpdates); + await _fakeDelay(); + } + + @override + Future updatePolygons( + PolygonUpdates polygonUpdates, { + required int mapId, + }) async { + mapInstances[mapId]?.polygonUpdates.add(polygonUpdates); + await _fakeDelay(); + } + + @override + Future updatePolylines( + PolylineUpdates polylineUpdates, { + required int mapId, + }) async { + mapInstances[mapId]?.polylineUpdates.add(polylineUpdates); + await _fakeDelay(); + } + + @override + Future updateCircles( + CircleUpdates circleUpdates, { + required int mapId, + }) async { + mapInstances[mapId]?.circleUpdates.add(circleUpdates); + await _fakeDelay(); + } + + @override + Future updateTileOverlays({ + required Set newTileOverlays, + required int mapId, + }) async { + mapInstances[mapId]?.tileOverlaySets.add(newTileOverlays); + await _fakeDelay(); + } + + @override + Future clearTileCache( + TileOverlayId tileOverlayId, { + required int mapId, + }) async {} + + @override + Future animateCamera( + CameraUpdate cameraUpdate, { + required int mapId, + }) async {} + + @override + Future moveCamera( + CameraUpdate cameraUpdate, { + required int mapId, + }) async {} + + @override + Future setMapStyle( + String? mapStyle, { + required int mapId, + }) async {} + + @override + Future getVisibleRegion({ + required int mapId, + }) async { + return LatLngBounds( + southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)); + } + + @override + Future getScreenCoordinate( + LatLng latLng, { + required int mapId, + }) async { + return const ScreenCoordinate(x: 0, y: 0); + } + + @override + Future getLatLng( + ScreenCoordinate screenCoordinate, { + required int mapId, + }) async { + return const LatLng(0, 0); + } + + @override + Future showMarkerInfoWindow( + MarkerId markerId, { + required int mapId, + }) async {} + + @override + Future hideMarkerInfoWindow( + MarkerId markerId, { + required int mapId, + }) async {} + + @override + Future isMarkerInfoWindowShown( + MarkerId markerId, { + required int mapId, + }) async { + return false; + } + + @override + Future getZoomLevel({ + required int mapId, + }) async { + return 0.0; + } + + @override + Future takeSnapshot({ + required int mapId, + }) async { + return null; + } + + @override + Stream onCameraMoveStarted({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onCameraMove({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onCameraIdle({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onMarkerTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onInfoWindowTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onMarkerDragStart({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onMarkerDrag({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onMarkerDragEnd({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onPolylineTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onPolygonTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onCircleTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + Stream onLongPress({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + + @override + void dispose({required int mapId}) { + disposed = true; + } + + @override + Widget buildViewWithConfiguration( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required MapWidgetConfiguration widgetConfiguration, + MapObjects mapObjects = const MapObjects(), + MapConfiguration mapConfiguration = const MapConfiguration(), + }) { + final PlatformMapStateRecorder? instance = mapInstances[creationId]; + if (instance == null) { + createdIds.add(creationId); + mapInstances[creationId] = PlatformMapStateRecorder( + widgetConfiguration: widgetConfiguration, + mapConfiguration: mapConfiguration, + mapObjects: mapObjects); + onPlatformViewCreated(creationId); + } + return Container(); + } + + Future _fakeDelay() async { + if (!simulatePlatformDelay) { + return; + } + return Future.delayed(const Duration(microseconds: 1)); + } +} + +/// A fake implementation of a native map, which stores all the updates it is +/// sent for inspection in tests. +class PlatformMapStateRecorder { + PlatformMapStateRecorder({ + required this.widgetConfiguration, + this.mapObjects = const MapObjects(), + this.mapConfiguration = const MapConfiguration(), + }) { + markerUpdates.add(MarkerUpdates.from(const {}, mapObjects.markers)); + polygonUpdates + .add(PolygonUpdates.from(const {}, mapObjects.polygons)); + polylineUpdates + .add(PolylineUpdates.from(const {}, mapObjects.polylines)); + circleUpdates.add(CircleUpdates.from(const {}, mapObjects.circles)); + tileOverlaySets.add(mapObjects.tileOverlays); + } + + MapWidgetConfiguration widgetConfiguration; + MapObjects mapObjects; + MapConfiguration mapConfiguration; + + final List markerUpdates = []; + final List polygonUpdates = []; + final List polylineUpdates = []; + final List circleUpdates = []; + final List> tileOverlaySets = >[]; +} diff --git a/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart b/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart deleted file mode 100644 index c28ff1f4f55..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart +++ /dev/null @@ -1,485 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; - -class FakePlatformGoogleMap { - FakePlatformGoogleMap(int id, Map params) - : cameraPosition = - CameraPosition.fromMap(params['initialCameraPosition']), - channel = MethodChannel('plugins.flutter.io/google_maps_$id') { - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger - .setMockMethodCallHandler(channel, onMethodCall); - updateOptions(params['options'] as Map); - updateMarkers(params); - updatePolygons(params); - updatePolylines(params); - updateCircles(params); - updateTileOverlays(Map.castFrom(params)); - } - - MethodChannel channel; - - CameraPosition? cameraPosition; - - bool? compassEnabled; - - bool? mapToolbarEnabled; - - CameraTargetBounds? cameraTargetBounds; - - MapType? mapType; - - MinMaxZoomPreference? minMaxZoomPreference; - - bool? rotateGesturesEnabled; - - bool? scrollGesturesEnabled; - - bool? tiltGesturesEnabled; - - bool? zoomGesturesEnabled; - - bool? zoomControlsEnabled; - - bool? liteModeEnabled; - - bool? trackCameraPosition; - - bool? myLocationEnabled; - - bool? trafficEnabled; - - bool? buildingsEnabled; - - bool? myLocationButtonEnabled; - - List? padding; - - Set markerIdsToRemove = {}; - - Set markersToAdd = {}; - - Set markersToChange = {}; - - Set polygonIdsToRemove = {}; - - Set polygonsToAdd = {}; - - Set polygonsToChange = {}; - - Set polylineIdsToRemove = {}; - - Set polylinesToAdd = {}; - - Set polylinesToChange = {}; - - Set circleIdsToRemove = {}; - - Set circlesToAdd = {}; - - Set circlesToChange = {}; - - Set tileOverlayIdsToRemove = {}; - - Set tileOverlaysToAdd = {}; - - Set tileOverlaysToChange = {}; - - Future onMethodCall(MethodCall call) { - switch (call.method) { - case 'map#update': - final Map arguments = - (call.arguments as Map).cast(); - updateOptions(arguments['options']! as Map); - return Future.sync(() {}); - case 'markers#update': - updateMarkers(call.arguments as Map?); - return Future.sync(() {}); - case 'polygons#update': - updatePolygons(call.arguments as Map?); - return Future.sync(() {}); - case 'polylines#update': - updatePolylines(call.arguments as Map?); - return Future.sync(() {}); - case 'tileOverlays#update': - updateTileOverlays(Map.castFrom( - call.arguments as Map)); - return Future.sync(() {}); - case 'circles#update': - updateCircles(call.arguments as Map?); - return Future.sync(() {}); - default: - return Future.sync(() {}); - } - } - - void updateMarkers(Map? markerUpdates) { - if (markerUpdates == null) { - return; - } - markersToAdd = _deserializeMarkers(markerUpdates['markersToAdd']); - markerIdsToRemove = _deserializeMarkerIds( - markerUpdates['markerIdsToRemove'] as List?); - markersToChange = _deserializeMarkers(markerUpdates['markersToChange']); - } - - Set _deserializeMarkerIds(List? markerIds) { - if (markerIds == null) { - return {}; - } - return markerIds - .map((dynamic markerId) => MarkerId(markerId as String)) - .toSet(); - } - - Set _deserializeMarkers(dynamic markers) { - if (markers == null) { - return {}; - } - final List markersData = markers as List; - final Set result = {}; - for (final Map markerData - in markersData.cast>()) { - final String markerId = markerData['markerId'] as String; - final double alpha = markerData['alpha'] as double; - final bool draggable = markerData['draggable'] as bool; - final bool visible = markerData['visible'] as bool; - - final dynamic infoWindowData = markerData['infoWindow']; - InfoWindow infoWindow = InfoWindow.noText; - if (infoWindowData != null) { - final Map infoWindowMap = - infoWindowData as Map; - infoWindow = InfoWindow( - title: infoWindowMap['title'] as String?, - snippet: infoWindowMap['snippet'] as String?, - ); - } - - result.add(Marker( - markerId: MarkerId(markerId), - draggable: draggable, - visible: visible, - infoWindow: infoWindow, - alpha: alpha, - )); - } - - return result; - } - - void updatePolygons(Map? polygonUpdates) { - if (polygonUpdates == null) { - return; - } - polygonsToAdd = _deserializePolygons(polygonUpdates['polygonsToAdd']); - polygonIdsToRemove = _deserializePolygonIds( - polygonUpdates['polygonIdsToRemove'] as List?); - polygonsToChange = _deserializePolygons(polygonUpdates['polygonsToChange']); - } - - Set _deserializePolygonIds(List? polygonIds) { - if (polygonIds == null) { - return {}; - } - return polygonIds - .map((dynamic polygonId) => PolygonId(polygonId as String)) - .toSet(); - } - - Set _deserializePolygons(dynamic polygons) { - if (polygons == null) { - return {}; - } - final List polygonsData = polygons as List; - final Set result = {}; - for (final Map polygonData - in polygonsData.cast>()) { - final String polygonId = polygonData['polygonId'] as String; - final bool visible = polygonData['visible'] as bool; - final bool geodesic = polygonData['geodesic'] as bool; - final List points = - _deserializePoints(polygonData['points'] as List); - final List> holes = - _deserializeHoles(polygonData['holes'] as List); - - result.add(Polygon( - polygonId: PolygonId(polygonId), - visible: visible, - geodesic: geodesic, - points: points, - holes: holes, - )); - } - - return result; - } - - // Converts a list of points expressed as two-element lists of doubles into - // a list of `LatLng`s. All list items are assumed to be non-null. - List _deserializePoints(List points) { - return points.map((dynamic item) { - final List list = item as List; - return LatLng(list[0]! as double, list[1]! as double); - }).toList(); - } - - List> _deserializeHoles(List holes) { - return holes.map>((dynamic hole) { - return _deserializePoints(hole as List); - }).toList(); - } - - void updatePolylines(Map? polylineUpdates) { - if (polylineUpdates == null) { - return; - } - polylinesToAdd = _deserializePolylines(polylineUpdates['polylinesToAdd']); - polylineIdsToRemove = _deserializePolylineIds( - polylineUpdates['polylineIdsToRemove'] as List?); - polylinesToChange = - _deserializePolylines(polylineUpdates['polylinesToChange']); - } - - Set _deserializePolylineIds(List? polylineIds) { - if (polylineIds == null) { - return {}; - } - return polylineIds - .map((dynamic polylineId) => PolylineId(polylineId as String)) - .toSet(); - } - - Set _deserializePolylines(dynamic polylines) { - if (polylines == null) { - return {}; - } - final List polylinesData = polylines as List; - final Set result = {}; - for (final Map polylineData - in polylinesData.cast>()) { - final String polylineId = polylineData['polylineId'] as String; - final bool visible = polylineData['visible'] as bool; - final bool geodesic = polylineData['geodesic'] as bool; - final List points = - _deserializePoints(polylineData['points'] as List); - - result.add(Polyline( - polylineId: PolylineId(polylineId), - visible: visible, - geodesic: geodesic, - points: points, - )); - } - - return result; - } - - void updateCircles(Map? circleUpdates) { - if (circleUpdates == null) { - return; - } - circlesToAdd = _deserializeCircles(circleUpdates['circlesToAdd']); - circleIdsToRemove = _deserializeCircleIds( - circleUpdates['circleIdsToRemove'] as List?); - circlesToChange = _deserializeCircles(circleUpdates['circlesToChange']); - } - - void updateTileOverlays(Map updateTileOverlayUpdates) { - final List>? tileOverlaysToAddList = - updateTileOverlayUpdates['tileOverlaysToAdd'] != null - ? List.castFrom>( - updateTileOverlayUpdates['tileOverlaysToAdd'] as List) - : null; - final List? tileOverlayIdsToRemoveList = - updateTileOverlayUpdates['tileOverlayIdsToRemove'] != null - ? List.castFrom( - updateTileOverlayUpdates['tileOverlayIdsToRemove'] - as List) - : null; - final List>? tileOverlaysToChangeList = - updateTileOverlayUpdates['tileOverlaysToChange'] != null - ? List.castFrom>( - updateTileOverlayUpdates['tileOverlaysToChange'] - as List) - : null; - tileOverlaysToAdd = _deserializeTileOverlays(tileOverlaysToAddList); - tileOverlayIdsToRemove = - _deserializeTileOverlayIds(tileOverlayIdsToRemoveList); - tileOverlaysToChange = _deserializeTileOverlays(tileOverlaysToChangeList); - } - - Set _deserializeCircleIds(List? circleIds) { - if (circleIds == null) { - return {}; - } - return circleIds - .map((dynamic circleId) => CircleId(circleId as String)) - .toSet(); - } - - Set _deserializeCircles(dynamic circles) { - if (circles == null) { - return {}; - } - final List circlesData = circles as List; - final Set result = {}; - for (final Map circleData - in circlesData.cast>()) { - final String circleId = circleData['circleId'] as String; - final bool visible = circleData['visible'] as bool; - final double radius = circleData['radius'] as double; - - result.add(Circle( - circleId: CircleId(circleId), - visible: visible, - radius: radius, - )); - } - - return result; - } - - Set _deserializeTileOverlayIds(List? tileOverlayIds) { - if (tileOverlayIds == null || tileOverlayIds.isEmpty) { - return {}; - } - return tileOverlayIds - .map((String tileOverlayId) => TileOverlayId(tileOverlayId)) - .toSet(); - } - - Set _deserializeTileOverlays( - List>? tileOverlays) { - if (tileOverlays == null || tileOverlays.isEmpty) { - return {}; - } - final Set result = {}; - for (final Map tileOverlayData in tileOverlays) { - final String tileOverlayId = tileOverlayData['tileOverlayId'] as String; - final bool fadeIn = tileOverlayData['fadeIn'] as bool; - final double transparency = tileOverlayData['transparency'] as double; - final int zIndex = tileOverlayData['zIndex'] as int; - final bool visible = tileOverlayData['visible'] as bool; - - result.add(TileOverlay( - tileOverlayId: TileOverlayId(tileOverlayId), - fadeIn: fadeIn, - transparency: transparency, - zIndex: zIndex, - visible: visible, - )); - } - - return result; - } - - void updateOptions(Map options) { - if (options.containsKey('compassEnabled')) { - compassEnabled = options['compassEnabled'] as bool?; - } - if (options.containsKey('mapToolbarEnabled')) { - mapToolbarEnabled = options['mapToolbarEnabled'] as bool?; - } - if (options.containsKey('cameraTargetBounds')) { - final List boundsList = - options['cameraTargetBounds'] as List; - cameraTargetBounds = boundsList[0] == null - ? CameraTargetBounds.unbounded - : CameraTargetBounds(LatLngBounds.fromList(boundsList[0])); - } - if (options.containsKey('mapType')) { - mapType = MapType.values[options['mapType'] as int]; - } - if (options.containsKey('minMaxZoomPreference')) { - final List minMaxZoomList = - options['minMaxZoomPreference'] as List; - minMaxZoomPreference = MinMaxZoomPreference( - minMaxZoomList[0] as double?, minMaxZoomList[1] as double?); - } - if (options.containsKey('rotateGesturesEnabled')) { - rotateGesturesEnabled = options['rotateGesturesEnabled'] as bool?; - } - if (options.containsKey('scrollGesturesEnabled')) { - scrollGesturesEnabled = options['scrollGesturesEnabled'] as bool?; - } - if (options.containsKey('tiltGesturesEnabled')) { - tiltGesturesEnabled = options['tiltGesturesEnabled'] as bool?; - } - if (options.containsKey('trackCameraPosition')) { - trackCameraPosition = options['trackCameraPosition'] as bool?; - } - if (options.containsKey('zoomGesturesEnabled')) { - zoomGesturesEnabled = options['zoomGesturesEnabled'] as bool?; - } - if (options.containsKey('zoomControlsEnabled')) { - zoomControlsEnabled = options['zoomControlsEnabled'] as bool?; - } - if (options.containsKey('liteModeEnabled')) { - liteModeEnabled = options['liteModeEnabled'] as bool?; - } - if (options.containsKey('myLocationEnabled')) { - myLocationEnabled = options['myLocationEnabled'] as bool?; - } - if (options.containsKey('myLocationButtonEnabled')) { - myLocationButtonEnabled = options['myLocationButtonEnabled'] as bool?; - } - if (options.containsKey('trafficEnabled')) { - trafficEnabled = options['trafficEnabled'] as bool?; - } - if (options.containsKey('buildingsEnabled')) { - buildingsEnabled = options['buildingsEnabled'] as bool?; - } - if (options.containsKey('padding')) { - padding = options['padding'] as List?; - } - } -} - -class FakePlatformViewsController { - FakePlatformGoogleMap? lastCreatedView; - - Future fakePlatformViewsMethodHandler(MethodCall call) { - switch (call.method) { - case 'create': - final Map args = - call.arguments as Map; - final Map params = - _decodeParams(args['params'] as Uint8List)!; - lastCreatedView = FakePlatformGoogleMap( - args['id'] as int, - params, - ); - return Future.sync(() => 1); - default: - return Future.sync(() {}); - } - } - - void reset() { - lastCreatedView = null; - } -} - -Map? _decodeParams(Uint8List paramsMessage) { - final ByteBuffer buffer = paramsMessage.buffer; - final ByteData messageBytes = buffer.asByteData( - paramsMessage.offsetInBytes, - paramsMessage.lengthInBytes, - ); - return const StandardMessageCodec().decodeMessage(messageBytes) - as Map?; -} - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; diff --git a/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart index 99b12988f3b..7005a8d3ab6 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart @@ -2,30 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'fake_maps_controllers.dart'; +import 'fake_google_maps_flutter_platform.dart'; void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final FakePlatformViewsController fakePlatformViewsController = - FakePlatformViewsController(); - - setUpAll(() { - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform_views, - fakePlatformViewsController.fakePlatformViewsMethodHandler, - ); - }); + late FakeGoogleMapsFlutterPlatform platform; setUp(() { - fakePlatformViewsController.reset(); + platform = FakeGoogleMapsFlutterPlatform(); + GoogleMapsFlutterPlatform.instance = platform; }); testWidgets('Initial camera position', (WidgetTester tester) async { @@ -38,10 +27,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.cameraPosition, + expect(map.widgetConfiguration.initialCameraPosition, const CameraPosition(target: LatLng(10.0, 15.0))); }); @@ -65,10 +53,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.cameraPosition, + expect(map.widgetConfiguration.initialCameraPosition, const CameraPosition(target: LatLng(10.0, 15.0))); }); @@ -83,10 +70,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.compassEnabled, false); + expect(map.mapConfiguration.compassEnabled, false); await tester.pumpWidget( const Directionality( @@ -97,7 +83,7 @@ void main() { ), ); - expect(platformGoogleMap.compassEnabled, true); + expect(map.mapConfiguration.compassEnabled, true); }); testWidgets('Can update mapToolbarEnabled', (WidgetTester tester) async { @@ -111,10 +97,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.mapToolbarEnabled, false); + expect(map.mapConfiguration.mapToolbarEnabled, false); await tester.pumpWidget( const Directionality( @@ -125,7 +110,7 @@ void main() { ), ); - expect(platformGoogleMap.mapToolbarEnabled, true); + expect(map.mapConfiguration.mapToolbarEnabled, true); }); testWidgets('Can update cameraTargetBounds', (WidgetTester tester) async { @@ -145,11 +130,10 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; expect( - platformGoogleMap.cameraTargetBounds, + map.mapConfiguration.cameraTargetBounds, CameraTargetBounds( LatLngBounds( southwest: const LatLng(10.0, 20.0), @@ -174,7 +158,7 @@ void main() { ); expect( - platformGoogleMap.cameraTargetBounds, + map.mapConfiguration.cameraTargetBounds, CameraTargetBounds( LatLngBounds( southwest: const LatLng(16.0, 20.0), @@ -194,10 +178,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.mapType, MapType.hybrid); + expect(map.mapConfiguration.mapType, MapType.hybrid); await tester.pumpWidget( const Directionality( @@ -209,7 +192,7 @@ void main() { ), ); - expect(platformGoogleMap.mapType, MapType.satellite); + expect(map.mapConfiguration.mapType, MapType.satellite); }); testWidgets('Can update minMaxZoom', (WidgetTester tester) async { @@ -223,10 +206,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.minMaxZoomPreference, + expect(map.mapConfiguration.minMaxZoomPreference, const MinMaxZoomPreference(1.0, 3.0)); await tester.pumpWidget( @@ -238,8 +220,8 @@ void main() { ), ); - expect( - platformGoogleMap.minMaxZoomPreference, MinMaxZoomPreference.unbounded); + expect(map.mapConfiguration.minMaxZoomPreference, + MinMaxZoomPreference.unbounded); }); testWidgets('Can update rotateGesturesEnabled', (WidgetTester tester) async { @@ -253,10 +235,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.rotateGesturesEnabled, false); + expect(map.mapConfiguration.rotateGesturesEnabled, false); await tester.pumpWidget( const Directionality( @@ -267,7 +248,7 @@ void main() { ), ); - expect(platformGoogleMap.rotateGesturesEnabled, true); + expect(map.mapConfiguration.rotateGesturesEnabled, true); }); testWidgets('Can update scrollGesturesEnabled', (WidgetTester tester) async { @@ -281,10 +262,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.scrollGesturesEnabled, false); + expect(map.mapConfiguration.scrollGesturesEnabled, false); await tester.pumpWidget( const Directionality( @@ -295,7 +275,7 @@ void main() { ), ); - expect(platformGoogleMap.scrollGesturesEnabled, true); + expect(map.mapConfiguration.scrollGesturesEnabled, true); }); testWidgets('Can update tiltGesturesEnabled', (WidgetTester tester) async { @@ -309,10 +289,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.tiltGesturesEnabled, false); + expect(map.mapConfiguration.tiltGesturesEnabled, false); await tester.pumpWidget( const Directionality( @@ -323,7 +302,7 @@ void main() { ), ); - expect(platformGoogleMap.tiltGesturesEnabled, true); + expect(map.mapConfiguration.tiltGesturesEnabled, true); }); testWidgets('Can update trackCameraPosition', (WidgetTester tester) async { @@ -336,10 +315,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.trackCameraPosition, false); + expect(map.mapConfiguration.trackCameraPosition, false); await tester.pumpWidget( Directionality( @@ -352,7 +330,7 @@ void main() { ), ); - expect(platformGoogleMap.trackCameraPosition, true); + expect(map.mapConfiguration.trackCameraPosition, true); }); testWidgets('Can update zoomGesturesEnabled', (WidgetTester tester) async { @@ -366,10 +344,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.zoomGesturesEnabled, false); + expect(map.mapConfiguration.zoomGesturesEnabled, false); await tester.pumpWidget( const Directionality( @@ -380,7 +357,7 @@ void main() { ), ); - expect(platformGoogleMap.zoomGesturesEnabled, true); + expect(map.mapConfiguration.zoomGesturesEnabled, true); }); testWidgets('Can update zoomControlsEnabled', (WidgetTester tester) async { @@ -394,10 +371,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.zoomControlsEnabled, false); + expect(map.mapConfiguration.zoomControlsEnabled, false); await tester.pumpWidget( const Directionality( @@ -408,7 +384,7 @@ void main() { ), ); - expect(platformGoogleMap.zoomControlsEnabled, true); + expect(map.mapConfiguration.zoomControlsEnabled, true); }); testWidgets('Can update myLocationEnabled', (WidgetTester tester) async { @@ -421,10 +397,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.myLocationEnabled, false); + expect(map.mapConfiguration.myLocationEnabled, false); await tester.pumpWidget( const Directionality( @@ -436,7 +411,7 @@ void main() { ), ); - expect(platformGoogleMap.myLocationEnabled, true); + expect(map.mapConfiguration.myLocationEnabled, true); }); testWidgets('Can update myLocationButtonEnabled', @@ -450,10 +425,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.myLocationButtonEnabled, true); + expect(map.mapConfiguration.myLocationButtonEnabled, true); await tester.pumpWidget( const Directionality( @@ -465,7 +439,7 @@ void main() { ), ); - expect(platformGoogleMap.myLocationButtonEnabled, false); + expect(map.mapConfiguration.myLocationButtonEnabled, false); }); testWidgets('Is default padding 0', (WidgetTester tester) async { @@ -478,10 +452,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.padding, [0, 0, 0, 0]); + expect(map.mapConfiguration.padding, EdgeInsets.zero); }); testWidgets('Can update padding', (WidgetTester tester) async { @@ -494,10 +467,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.padding, [0, 0, 0, 0]); + expect(map.mapConfiguration.padding, EdgeInsets.zero); await tester.pumpWidget( const Directionality( @@ -509,7 +481,8 @@ void main() { ), ); - expect(platformGoogleMap.padding, [20, 10, 40, 30]); + expect(map.mapConfiguration.padding, + const EdgeInsets.fromLTRB(10, 20, 30, 40)); await tester.pumpWidget( const Directionality( @@ -521,7 +494,8 @@ void main() { ), ); - expect(platformGoogleMap.padding, [60, 50, 80, 70]); + expect(map.mapConfiguration.padding, + const EdgeInsets.fromLTRB(50, 60, 70, 80)); }); testWidgets('Can update traffic', (WidgetTester tester) async { @@ -534,10 +508,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.trafficEnabled, false); + expect(map.mapConfiguration.trafficEnabled, false); await tester.pumpWidget( const Directionality( @@ -549,7 +522,7 @@ void main() { ), ); - expect(platformGoogleMap.trafficEnabled, true); + expect(map.mapConfiguration.trafficEnabled, true); }); testWidgets('Can update buildings', (WidgetTester tester) async { @@ -563,10 +536,9 @@ void main() { ), ); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.buildingsEnabled, false); + expect(map.mapConfiguration.buildingsEnabled, false); await tester.pumpWidget( const Directionality( @@ -577,12 +549,6 @@ void main() { ), ); - expect(platformGoogleMap.buildingsEnabled, true); + expect(map.mapConfiguration.buildingsEnabled, true); }); } - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; diff --git a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart index 7f88b60ad6c..eb7e038c043 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart @@ -2,22 +2,20 @@ // 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/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'package:stream_transform/stream_transform.dart'; + +import 'fake_google_maps_flutter_platform.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late TestGoogleMapsFlutterPlatform platform; + late FakeGoogleMapsFlutterPlatform platform; setUp(() { // Use a mock platform so we never need to hit the MethodChannel code. - platform = TestGoogleMapsFlutterPlatform(); + platform = FakeGoogleMapsFlutterPlatform(); GoogleMapsFlutterPlatform.instance = platform; }); @@ -66,222 +64,3 @@ void main() { expect(platform.disposed, true); }); } - -// A dummy implementation of the platform interface for tests. -class TestGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { - TestGoogleMapsFlutterPlatform(); - - // The IDs passed to each call to buildView, in call order. - List createdIds = []; - - // Whether `dispose` has been called. - bool disposed = false; - - // Stream controller to inject events for testing. - final StreamController> mapEventStreamController = - StreamController>.broadcast(); - - @override - Future init(int mapId) async {} - - @override - Future updateMapConfiguration( - MapConfiguration update, { - required int mapId, - }) async {} - - @override - Future updateMarkers( - MarkerUpdates markerUpdates, { - required int mapId, - }) async {} - - @override - Future updatePolygons( - PolygonUpdates polygonUpdates, { - required int mapId, - }) async {} - - @override - Future updatePolylines( - PolylineUpdates polylineUpdates, { - required int mapId, - }) async {} - - @override - Future updateCircles( - CircleUpdates circleUpdates, { - required int mapId, - }) async {} - - @override - Future updateTileOverlays({ - required Set newTileOverlays, - required int mapId, - }) async {} - - @override - Future clearTileCache( - TileOverlayId tileOverlayId, { - required int mapId, - }) async {} - - @override - Future animateCamera( - CameraUpdate cameraUpdate, { - required int mapId, - }) async {} - - @override - Future moveCamera( - CameraUpdate cameraUpdate, { - required int mapId, - }) async {} - - @override - Future setMapStyle( - String? mapStyle, { - required int mapId, - }) async {} - - @override - Future getVisibleRegion({ - required int mapId, - }) async { - return LatLngBounds( - southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)); - } - - @override - Future getScreenCoordinate( - LatLng latLng, { - required int mapId, - }) async { - return const ScreenCoordinate(x: 0, y: 0); - } - - @override - Future getLatLng( - ScreenCoordinate screenCoordinate, { - required int mapId, - }) async { - return const LatLng(0, 0); - } - - @override - Future showMarkerInfoWindow( - MarkerId markerId, { - required int mapId, - }) async {} - - @override - Future hideMarkerInfoWindow( - MarkerId markerId, { - required int mapId, - }) async {} - - @override - Future isMarkerInfoWindowShown( - MarkerId markerId, { - required int mapId, - }) async { - return false; - } - - @override - Future getZoomLevel({ - required int mapId, - }) async { - return 0.0; - } - - @override - Future takeSnapshot({ - required int mapId, - }) async { - return null; - } - - @override - Stream onCameraMoveStarted({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onCameraMove({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onCameraIdle({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onMarkerTap({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onInfoWindowTap({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onMarkerDragStart({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onMarkerDrag({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onMarkerDragEnd({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onPolylineTap({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onPolygonTap({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onCircleTap({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onTap({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - Stream onLongPress({required int mapId}) { - return mapEventStreamController.stream.whereType(); - } - - @override - void dispose({required int mapId}) { - disposed = true; - } - - @override - Widget buildViewWithConfiguration( - int creationId, - PlatformViewCreatedCallback onPlatformViewCreated, { - required MapWidgetConfiguration widgetConfiguration, - MapObjects mapObjects = const MapObjects(), - MapConfiguration mapConfiguration = const MapConfiguration(), - }) { - onPlatformViewCreated(0); - createdIds.add(creationId); - return Container(); - } -} diff --git a/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart index 75a153e0eaa..9f65f5d3bf5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'fake_maps_controllers.dart'; +import 'fake_google_maps_flutter_platform.dart'; Widget _mapWithMarkers(Set markers) { return Directionality( @@ -20,36 +20,24 @@ Widget _mapWithMarkers(Set markers) { } void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final FakePlatformViewsController fakePlatformViewsController = - FakePlatformViewsController(); - - setUpAll(() { - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform_views, - fakePlatformViewsController.fakePlatformViewsMethodHandler, - ); - }); + late FakeGoogleMapsFlutterPlatform platform; setUp(() { - fakePlatformViewsController.reset(); + platform = FakeGoogleMapsFlutterPlatform(); + GoogleMapsFlutterPlatform.instance = platform; }); testWidgets('Initializing a marker', (WidgetTester tester) async { const Marker m1 = Marker(markerId: MarkerId('marker_1')); await tester.pumpWidget(_mapWithMarkers({m1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.markersToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.markerUpdates.last.markersToAdd.length, 1); - final Marker initializedMarker = platformGoogleMap.markersToAdd.first; + final Marker initializedMarker = map.markerUpdates.last.markersToAdd.first; expect(initializedMarker, equals(m1)); - expect(platformGoogleMap.markerIdsToRemove.isEmpty, true); - expect(platformGoogleMap.markersToChange.isEmpty, true); + expect(map.markerUpdates.last.markerIdsToRemove.isEmpty, true); + expect(map.markerUpdates.last.markersToChange.isEmpty, true); }); testWidgets('Adding a marker', (WidgetTester tester) async { @@ -59,16 +47,15 @@ void main() { await tester.pumpWidget(_mapWithMarkers({m1})); await tester.pumpWidget(_mapWithMarkers({m1, m2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.markersToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.markerUpdates.last.markersToAdd.length, 1); - final Marker addedMarker = platformGoogleMap.markersToAdd.first; + final Marker addedMarker = map.markerUpdates.last.markersToAdd.first; expect(addedMarker, equals(m2)); - expect(platformGoogleMap.markerIdsToRemove.isEmpty, true); + expect(map.markerUpdates.last.markerIdsToRemove.isEmpty, true); - expect(platformGoogleMap.markersToChange.isEmpty, true); + expect(map.markerUpdates.last.markersToChange.isEmpty, true); }); testWidgets('Removing a marker', (WidgetTester tester) async { @@ -77,13 +64,12 @@ void main() { await tester.pumpWidget(_mapWithMarkers({m1})); await tester.pumpWidget(_mapWithMarkers({})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.markerIdsToRemove.length, 1); - expect(platformGoogleMap.markerIdsToRemove.first, equals(m1.markerId)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.markerUpdates.last.markerIdsToRemove.length, 1); + expect(map.markerUpdates.last.markerIdsToRemove.first, equals(m1.markerId)); - expect(platformGoogleMap.markersToChange.isEmpty, true); - expect(platformGoogleMap.markersToAdd.isEmpty, true); + expect(map.markerUpdates.last.markersToChange.isEmpty, true); + expect(map.markerUpdates.last.markersToAdd.isEmpty, true); }); testWidgets('Updating a marker', (WidgetTester tester) async { @@ -93,13 +79,12 @@ void main() { await tester.pumpWidget(_mapWithMarkers({m1})); await tester.pumpWidget(_mapWithMarkers({m2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.markersToChange.length, 1); - expect(platformGoogleMap.markersToChange.first, equals(m2)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.markerUpdates.last.markersToChange.length, 1); + expect(map.markerUpdates.last.markersToChange.first, equals(m2)); - expect(platformGoogleMap.markerIdsToRemove.isEmpty, true); - expect(platformGoogleMap.markersToAdd.isEmpty, true); + expect(map.markerUpdates.last.markerIdsToRemove.isEmpty, true); + expect(map.markerUpdates.last.markersToAdd.isEmpty, true); }); testWidgets('Updating a marker', (WidgetTester tester) async { @@ -112,11 +97,10 @@ void main() { await tester.pumpWidget(_mapWithMarkers({m1})); await tester.pumpWidget(_mapWithMarkers({m2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.markersToChange.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.markerUpdates.last.markersToChange.length, 1); - final Marker update = platformGoogleMap.markersToChange.first; + final Marker update = map.markerUpdates.last.markersToChange.first; expect(update, equals(m2)); expect(update.infoWindow.snippet, 'changed'); }); @@ -132,12 +116,11 @@ void main() { await tester.pumpWidget(_mapWithMarkers(prev)); await tester.pumpWidget(_mapWithMarkers(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.markersToChange, cur); - expect(platformGoogleMap.markerIdsToRemove.isEmpty, true); - expect(platformGoogleMap.markersToAdd.isEmpty, true); + expect(map.markerUpdates.last.markersToChange, cur); + expect(map.markerUpdates.last.markerIdsToRemove.isEmpty, true); + expect(map.markerUpdates.last.markersToAdd.isEmpty, true); }); testWidgets('Multi Update', (WidgetTester tester) async { @@ -153,16 +136,15 @@ void main() { await tester.pumpWidget(_mapWithMarkers(prev)); await tester.pumpWidget(_mapWithMarkers(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.markersToChange.length, 1); - expect(platformGoogleMap.markersToAdd.length, 1); - expect(platformGoogleMap.markerIdsToRemove.length, 1); + expect(map.markerUpdates.last.markersToChange.length, 1); + expect(map.markerUpdates.last.markersToAdd.length, 1); + expect(map.markerUpdates.last.markerIdsToRemove.length, 1); - expect(platformGoogleMap.markersToChange.first, equals(m2)); - expect(platformGoogleMap.markersToAdd.first, equals(m1)); - expect(platformGoogleMap.markerIdsToRemove.first, equals(m3.markerId)); + expect(map.markerUpdates.last.markersToChange.first, equals(m2)); + expect(map.markerUpdates.last.markersToAdd.first, equals(m1)); + expect(map.markerUpdates.last.markerIdsToRemove.first, equals(m3.markerId)); }); testWidgets('Partial Update', (WidgetTester tester) async { @@ -176,12 +158,11 @@ void main() { await tester.pumpWidget(_mapWithMarkers(prev)); await tester.pumpWidget(_mapWithMarkers(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.markersToChange, {m3}); - expect(platformGoogleMap.markerIdsToRemove.isEmpty, true); - expect(platformGoogleMap.markersToAdd.isEmpty, true); + expect(map.markerUpdates.last.markersToChange, {m3}); + expect(map.markerUpdates.last.markerIdsToRemove.isEmpty, true); + expect(map.markerUpdates.last.markersToAdd.isEmpty, true); }); testWidgets('Update non platform related attr', (WidgetTester tester) async { @@ -196,17 +177,43 @@ void main() { await tester.pumpWidget(_mapWithMarkers(prev)); await tester.pumpWidget(_mapWithMarkers(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.markersToChange.isEmpty, true); - expect(platformGoogleMap.markerIdsToRemove.isEmpty, true); - expect(platformGoogleMap.markersToAdd.isEmpty, true); + expect(map.markerUpdates.last.markersToChange.isEmpty, true); + expect(map.markerUpdates.last.markerIdsToRemove.isEmpty, true); + expect(map.markerUpdates.last.markersToAdd.isEmpty, true); }); -} -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; + testWidgets('multi-update with delays', (WidgetTester tester) async { + platform.simulatePlatformDelay = true; + + const Marker m1 = Marker(markerId: MarkerId('marker_1')); + const Marker m2 = Marker(markerId: MarkerId('marker_2')); + const Marker m3 = Marker(markerId: MarkerId('marker_3')); + const Marker m3updated = + Marker(markerId: MarkerId('marker_3'), draggable: true); + + // First remove one and add another, then update the new one. + await tester.pumpWidget(_mapWithMarkers({m1, m2})); + await tester.pumpWidget(_mapWithMarkers({m1, m3})); + await tester.pumpWidget(_mapWithMarkers({m1, m3updated})); + + final PlatformMapStateRecorder map = platform.lastCreatedMap; + + expect(map.markerUpdates.length, 3); + + expect(map.markerUpdates[0].markersToChange.isEmpty, true); + expect(map.markerUpdates[0].markersToAdd, {m1, m2}); + expect(map.markerUpdates[0].markerIdsToRemove.isEmpty, true); + + expect(map.markerUpdates[1].markersToChange.isEmpty, true); + expect(map.markerUpdates[1].markersToAdd, {m3}); + expect(map.markerUpdates[1].markerIdsToRemove, {m2.markerId}); + + expect(map.markerUpdates[2].markersToChange, {m3updated}); + expect(map.markerUpdates[2].markersToAdd.isEmpty, true); + expect(map.markerUpdates[2].markerIdsToRemove.isEmpty, true); + + await tester.pumpAndSettle(); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart index 152cbddfc34..08910fa5ccb 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'fake_maps_controllers.dart'; +import 'fake_google_maps_flutter_platform.dart'; Widget _mapWithPolygons(Set polygons) { return Directionality( @@ -43,36 +43,25 @@ Polygon _polygonWithPointsAndHole(PolygonId polygonId) { } void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final FakePlatformViewsController fakePlatformViewsController = - FakePlatformViewsController(); - - setUpAll(() { - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform_views, - fakePlatformViewsController.fakePlatformViewsMethodHandler, - ); - }); + late FakeGoogleMapsFlutterPlatform platform; setUp(() { - fakePlatformViewsController.reset(); + platform = FakeGoogleMapsFlutterPlatform(); + GoogleMapsFlutterPlatform.instance = platform; }); testWidgets('Initializing a polygon', (WidgetTester tester) async { const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); await tester.pumpWidget(_mapWithPolygons({p1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToAdd.length, 1); - final Polygon initializedPolygon = platformGoogleMap.polygonsToAdd.first; + final Polygon initializedPolygon = + map.polygonUpdates.last.polygonsToAdd.first; expect(initializedPolygon, equals(p1)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToChange.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange.isEmpty, true); }); testWidgets('Adding a polygon', (WidgetTester tester) async { @@ -82,16 +71,15 @@ void main() { await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p1, p2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToAdd.length, 1); - final Polygon addedPolygon = platformGoogleMap.polygonsToAdd.first; + final Polygon addedPolygon = map.polygonUpdates.last.polygonsToAdd.first; expect(addedPolygon, equals(p2)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToChange.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange.isEmpty, true); }); testWidgets('Removing a polygon', (WidgetTester tester) async { @@ -100,13 +88,13 @@ void main() { await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonIdsToRemove.length, 1); - expect(platformGoogleMap.polygonIdsToRemove.first, equals(p1.polygonId)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonIdsToRemove.length, 1); + expect( + map.polygonUpdates.last.polygonIdsToRemove.first, equals(p1.polygonId)); - expect(platformGoogleMap.polygonsToChange.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Updating a polygon', (WidgetTester tester) async { @@ -117,13 +105,12 @@ void main() { await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToChange.length, 1); - expect(platformGoogleMap.polygonsToChange.first, equals(p2)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToChange.length, 1); + expect(map.polygonUpdates.last.polygonsToChange.first, equals(p2)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Mutate a polygon', (WidgetTester tester) async { @@ -137,13 +124,12 @@ void main() { p1.points.add(const LatLng(1.0, 1.0)); await tester.pumpWidget(_mapWithPolygons({p1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToChange.length, 1); - expect(platformGoogleMap.polygonsToChange.first, equals(p1)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToChange.length, 1); + expect(map.polygonUpdates.last.polygonsToChange.first, equals(p1)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Multi Update', (WidgetTester tester) async { @@ -157,12 +143,11 @@ void main() { await tester.pumpWidget(_mapWithPolygons(prev)); await tester.pumpWidget(_mapWithPolygons(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polygonsToChange, cur); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange, cur); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Multi Update', (WidgetTester tester) async { @@ -178,16 +163,16 @@ void main() { await tester.pumpWidget(_mapWithPolygons(prev)); await tester.pumpWidget(_mapWithPolygons(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polygonsToChange.length, 1); - expect(platformGoogleMap.polygonsToAdd.length, 1); - expect(platformGoogleMap.polygonIdsToRemove.length, 1); + expect(map.polygonUpdates.last.polygonsToChange.length, 1); + expect(map.polygonUpdates.last.polygonsToAdd.length, 1); + expect(map.polygonUpdates.last.polygonIdsToRemove.length, 1); - expect(platformGoogleMap.polygonsToChange.first, equals(p2)); - expect(platformGoogleMap.polygonsToAdd.first, equals(p1)); - expect(platformGoogleMap.polygonIdsToRemove.first, equals(p3.polygonId)); + expect(map.polygonUpdates.last.polygonsToChange.first, equals(p2)); + expect(map.polygonUpdates.last.polygonsToAdd.first, equals(p1)); + expect( + map.polygonUpdates.last.polygonIdsToRemove.first, equals(p3.polygonId)); }); testWidgets('Partial Update', (WidgetTester tester) async { @@ -201,12 +186,11 @@ void main() { await tester.pumpWidget(_mapWithPolygons(prev)); await tester.pumpWidget(_mapWithPolygons(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polygonsToChange, {p3}); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange, {p3}); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Update non platform related attr', (WidgetTester tester) async { @@ -218,12 +202,11 @@ void main() { await tester.pumpWidget(_mapWithPolygons(prev)); await tester.pumpWidget(_mapWithPolygons(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polygonsToChange.isEmpty, true); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Initializing a polygon with points and hole', @@ -231,14 +214,14 @@ void main() { final Polygon p1 = _polygonWithPointsAndHole(const PolygonId('polygon_1')); await tester.pumpWidget(_mapWithPolygons({p1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToAdd.length, 1); - final Polygon initializedPolygon = platformGoogleMap.polygonsToAdd.first; + final Polygon initializedPolygon = + map.polygonUpdates.last.polygonsToAdd.first; expect(initializedPolygon, equals(p1)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToChange.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange.isEmpty, true); }); testWidgets('Adding a polygon with points and hole', @@ -249,16 +232,15 @@ void main() { await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p1, p2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToAdd.length, 1); - final Polygon addedPolygon = platformGoogleMap.polygonsToAdd.first; + final Polygon addedPolygon = map.polygonUpdates.last.polygonsToAdd.first; expect(addedPolygon, equals(p2)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToChange.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange.isEmpty, true); }); testWidgets('Removing a polygon with points and hole', @@ -268,13 +250,13 @@ void main() { await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonIdsToRemove.length, 1); - expect(platformGoogleMap.polygonIdsToRemove.first, equals(p1.polygonId)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonIdsToRemove.length, 1); + expect( + map.polygonUpdates.last.polygonIdsToRemove.first, equals(p1.polygonId)); - expect(platformGoogleMap.polygonsToChange.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Updating a polygon by adding points and hole', @@ -285,13 +267,12 @@ void main() { await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToChange.length, 1); - expect(platformGoogleMap.polygonsToChange.first, equals(p2)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToChange.length, 1); + expect(map.polygonUpdates.last.polygonsToChange.first, equals(p2)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Mutate a polygon with points and holes', @@ -311,13 +292,12 @@ void main() { ..addAll(>[_rectPoints(size: 1)]); await tester.pumpWidget(_mapWithPolygons({p1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polygonsToChange.length, 1); - expect(platformGoogleMap.polygonsToChange.first, equals(p1)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polygonUpdates.last.polygonsToChange.length, 1); + expect(map.polygonUpdates.last.polygonsToChange.first, equals(p1)); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Multi Update polygons with points and hole', @@ -339,12 +319,11 @@ void main() { await tester.pumpWidget(_mapWithPolygons(prev)); await tester.pumpWidget(_mapWithPolygons(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polygonsToChange, cur); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange, cur); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); testWidgets('Multi Update polygons with points and hole', @@ -368,16 +347,16 @@ void main() { await tester.pumpWidget(_mapWithPolygons(prev)); await tester.pumpWidget(_mapWithPolygons(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polygonsToChange.length, 1); - expect(platformGoogleMap.polygonsToAdd.length, 1); - expect(platformGoogleMap.polygonIdsToRemove.length, 1); + expect(map.polygonUpdates.last.polygonsToChange.length, 1); + expect(map.polygonUpdates.last.polygonsToAdd.length, 1); + expect(map.polygonUpdates.last.polygonIdsToRemove.length, 1); - expect(platformGoogleMap.polygonsToChange.first, equals(p2)); - expect(platformGoogleMap.polygonsToAdd.first, equals(p1)); - expect(platformGoogleMap.polygonIdsToRemove.first, equals(p3.polygonId)); + expect(map.polygonUpdates.last.polygonsToChange.first, equals(p2)); + expect(map.polygonUpdates.last.polygonsToAdd.first, equals(p1)); + expect( + map.polygonUpdates.last.polygonIdsToRemove.first, equals(p3.polygonId)); }); testWidgets('Partial Update polygons with points and hole', @@ -399,17 +378,44 @@ void main() { await tester.pumpWidget(_mapWithPolygons(prev)); await tester.pumpWidget(_mapWithPolygons(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polygonsToChange, {p3}); - expect(platformGoogleMap.polygonIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToChange, {p3}); + expect(map.polygonUpdates.last.polygonIdsToRemove.isEmpty, true); + expect(map.polygonUpdates.last.polygonsToAdd.isEmpty, true); }); -} -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; + testWidgets('multi-update with delays', (WidgetTester tester) async { + platform.simulatePlatformDelay = true; + + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); + const Polygon p2 = Polygon(polygonId: PolygonId('polygon_2')); + const Polygon p3 = + Polygon(polygonId: PolygonId('polygon_3'), strokeWidth: 1); + const Polygon p3updated = + Polygon(polygonId: PolygonId('polygon_3'), strokeWidth: 2); + + // First remove one and add another, then update the new one. + await tester.pumpWidget(_mapWithPolygons({p1, p2})); + await tester.pumpWidget(_mapWithPolygons({p1, p3})); + await tester.pumpWidget(_mapWithPolygons({p1, p3updated})); + + final PlatformMapStateRecorder map = platform.lastCreatedMap; + + expect(map.polygonUpdates.length, 3); + + expect(map.polygonUpdates[0].polygonsToChange.isEmpty, true); + expect(map.polygonUpdates[0].polygonsToAdd, {p1, p2}); + expect(map.polygonUpdates[0].polygonIdsToRemove.isEmpty, true); + + expect(map.polygonUpdates[1].polygonsToChange.isEmpty, true); + expect(map.polygonUpdates[1].polygonsToAdd, {p3}); + expect(map.polygonUpdates[1].polygonIdsToRemove, {p2.polygonId}); + + expect(map.polygonUpdates[2].polygonsToChange, {p3updated}); + expect(map.polygonUpdates[2].polygonsToAdd.isEmpty, true); + expect(map.polygonUpdates[2].polygonIdsToRemove.isEmpty, true); + + await tester.pumpAndSettle(); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart index 03b6c620190..cac311f7f2e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'fake_maps_controllers.dart'; +import 'fake_google_maps_flutter_platform.dart'; Widget _mapWithPolylines(Set polylines) { return Directionality( @@ -20,36 +20,25 @@ Widget _mapWithPolylines(Set polylines) { } void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final FakePlatformViewsController fakePlatformViewsController = - FakePlatformViewsController(); - - setUpAll(() { - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform_views, - fakePlatformViewsController.fakePlatformViewsMethodHandler, - ); - }); + late FakeGoogleMapsFlutterPlatform platform; setUp(() { - fakePlatformViewsController.reset(); + platform = FakeGoogleMapsFlutterPlatform(); + GoogleMapsFlutterPlatform.instance = platform; }); testWidgets('Initializing a polyline', (WidgetTester tester) async { const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); await tester.pumpWidget(_mapWithPolylines({p1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polylinesToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polylineUpdates.last.polylinesToAdd.length, 1); - final Polyline initializedPolyline = platformGoogleMap.polylinesToAdd.first; + final Polyline initializedPolyline = + map.polylineUpdates.last.polylinesToAdd.first; expect(initializedPolyline, equals(p1)); - expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polylinesToChange.isEmpty, true); + expect(map.polylineUpdates.last.polylineIdsToRemove.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToChange.isEmpty, true); }); testWidgets('Adding a polyline', (WidgetTester tester) async { @@ -59,16 +48,16 @@ void main() { await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({p1, p2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polylinesToAdd.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polylineUpdates.last.polylinesToAdd.length, 1); - final Polyline addedPolyline = platformGoogleMap.polylinesToAdd.first; + final Polyline addedPolyline = + map.polylineUpdates.last.polylinesToAdd.first; expect(addedPolyline, equals(p2)); - expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true); + expect(map.polylineUpdates.last.polylineIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polylinesToChange.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToChange.isEmpty, true); }); testWidgets('Removing a polyline', (WidgetTester tester) async { @@ -77,13 +66,13 @@ void main() { await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polylineIdsToRemove.length, 1); - expect(platformGoogleMap.polylineIdsToRemove.first, equals(p1.polylineId)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polylineUpdates.last.polylineIdsToRemove.length, 1); + expect(map.polylineUpdates.last.polylineIdsToRemove.first, + equals(p1.polylineId)); - expect(platformGoogleMap.polylinesToChange.isEmpty, true); - expect(platformGoogleMap.polylinesToAdd.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToChange.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToAdd.isEmpty, true); }); testWidgets('Updating a polyline', (WidgetTester tester) async { @@ -94,13 +83,12 @@ void main() { await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({p2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polylinesToChange.length, 1); - expect(platformGoogleMap.polylinesToChange.first, equals(p2)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polylineUpdates.last.polylinesToChange.length, 1); + expect(map.polylineUpdates.last.polylinesToChange.first, equals(p2)); - expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polylinesToAdd.isEmpty, true); + expect(map.polylineUpdates.last.polylineIdsToRemove.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToAdd.isEmpty, true); }); testWidgets('Updating a polyline', (WidgetTester tester) async { @@ -111,11 +99,10 @@ void main() { await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({p2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polylinesToChange.length, 1); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polylineUpdates.last.polylinesToChange.length, 1); - final Polyline update = platformGoogleMap.polylinesToChange.first; + final Polyline update = map.polylineUpdates.last.polylinesToChange.first; expect(update, equals(p2)); expect(update.geodesic, true); }); @@ -131,13 +118,12 @@ void main() { p1.points.add(const LatLng(1.0, 1.0)); await tester.pumpWidget(_mapWithPolylines({p1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.polylinesToChange.length, 1); - expect(platformGoogleMap.polylinesToChange.first, equals(p1)); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.polylineUpdates.last.polylinesToChange.length, 1); + expect(map.polylineUpdates.last.polylinesToChange.first, equals(p1)); - expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polylinesToAdd.isEmpty, true); + expect(map.polylineUpdates.last.polylineIdsToRemove.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToAdd.isEmpty, true); }); testWidgets('Multi Update', (WidgetTester tester) async { @@ -151,12 +137,11 @@ void main() { await tester.pumpWidget(_mapWithPolylines(prev)); await tester.pumpWidget(_mapWithPolylines(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polylinesToChange, cur); - expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polylinesToAdd.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToChange, cur); + expect(map.polylineUpdates.last.polylineIdsToRemove.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToAdd.isEmpty, true); }); testWidgets('Multi Update', (WidgetTester tester) async { @@ -172,16 +157,16 @@ void main() { await tester.pumpWidget(_mapWithPolylines(prev)); await tester.pumpWidget(_mapWithPolylines(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polylinesToChange.length, 1); - expect(platformGoogleMap.polylinesToAdd.length, 1); - expect(platformGoogleMap.polylineIdsToRemove.length, 1); + expect(map.polylineUpdates.last.polylinesToChange.length, 1); + expect(map.polylineUpdates.last.polylinesToAdd.length, 1); + expect(map.polylineUpdates.last.polylineIdsToRemove.length, 1); - expect(platformGoogleMap.polylinesToChange.first, equals(p2)); - expect(platformGoogleMap.polylinesToAdd.first, equals(p1)); - expect(platformGoogleMap.polylineIdsToRemove.first, equals(p3.polylineId)); + expect(map.polylineUpdates.last.polylinesToChange.first, equals(p2)); + expect(map.polylineUpdates.last.polylinesToAdd.first, equals(p1)); + expect(map.polylineUpdates.last.polylineIdsToRemove.first, + equals(p3.polylineId)); }); testWidgets('Partial Update', (WidgetTester tester) async { @@ -195,12 +180,11 @@ void main() { await tester.pumpWidget(_mapWithPolylines(prev)); await tester.pumpWidget(_mapWithPolylines(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polylinesToChange, {p3}); - expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polylinesToAdd.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToChange, {p3}); + expect(map.polylineUpdates.last.polylineIdsToRemove.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToAdd.isEmpty, true); }); testWidgets('Update non platform related attr', (WidgetTester tester) async { @@ -212,17 +196,45 @@ void main() { await tester.pumpWidget(_mapWithPolylines(prev)); await tester.pumpWidget(_mapWithPolylines(cur)); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; + final PlatformMapStateRecorder map = platform.lastCreatedMap; - expect(platformGoogleMap.polylinesToChange.isEmpty, true); - expect(platformGoogleMap.polylineIdsToRemove.isEmpty, true); - expect(platformGoogleMap.polylinesToAdd.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToChange.isEmpty, true); + expect(map.polylineUpdates.last.polylineIdsToRemove.isEmpty, true); + expect(map.polylineUpdates.last.polylinesToAdd.isEmpty, true); }); -} -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; + testWidgets('multi-update with delays', (WidgetTester tester) async { + platform.simulatePlatformDelay = true; + + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); + const Polyline p2 = Polyline(polylineId: PolylineId('polyline_2')); + const Polyline p3 = + Polyline(polylineId: PolylineId('polyline_3'), width: 1); + const Polyline p3updated = + Polyline(polylineId: PolylineId('polyline_3'), width: 2); + + // First remove one and add another, then update the new one. + await tester.pumpWidget(_mapWithPolylines({p1, p2})); + await tester.pumpWidget(_mapWithPolylines({p1, p3})); + await tester.pumpWidget(_mapWithPolylines({p1, p3updated})); + + final PlatformMapStateRecorder map = platform.lastCreatedMap; + + expect(map.polylineUpdates.length, 3); + + expect(map.polylineUpdates[0].polylinesToChange.isEmpty, true); + expect(map.polylineUpdates[0].polylinesToAdd, {p1, p2}); + expect(map.polylineUpdates[0].polylineIdsToRemove.isEmpty, true); + + expect(map.polylineUpdates[1].polylinesToChange.isEmpty, true); + expect(map.polylineUpdates[1].polylinesToAdd, {p3}); + expect(map.polylineUpdates[1].polylineIdsToRemove, + {p2.polylineId}); + + expect(map.polylineUpdates[2].polylinesToChange, {p3updated}); + expect(map.polylineUpdates[2].polylinesToAdd.isEmpty, true); + expect(map.polylineUpdates[2].polylineIdsToRemove.isEmpty, true); + + await tester.pumpAndSettle(); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart index e4e4514dd50..c2faca593bc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'fake_maps_controllers.dart'; +import 'fake_google_maps_flutter_platform.dart'; Widget _mapWithTileOverlays(Set tileOverlays) { return Directionality( @@ -20,20 +20,11 @@ Widget _mapWithTileOverlays(Set tileOverlays) { } void main() { - final FakePlatformViewsController fakePlatformViewsController = - FakePlatformViewsController(); - - setUpAll(() { - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform_views, - fakePlatformViewsController.fakePlatformViewsMethodHandler, - ); - }); + late FakeGoogleMapsFlutterPlatform platform; setUp(() { - fakePlatformViewsController.reset(); + platform = FakeGoogleMapsFlutterPlatform(); + GoogleMapsFlutterPlatform.instance = platform; }); testWidgets('Initializing a tile overlay', (WidgetTester tester) async { @@ -41,15 +32,8 @@ void main() { TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); await tester.pumpWidget(_mapWithTileOverlays({t1})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.tileOverlaysToAdd.length, 1); - - final TileOverlay initializedTileOverlay = - platformGoogleMap.tileOverlaysToAdd.first; - expect(initializedTileOverlay, equals(t1)); - expect(platformGoogleMap.tileOverlayIdsToRemove.isEmpty, true); - expect(platformGoogleMap.tileOverlaysToChange.isEmpty, true); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.tileOverlaySets.last, equals({t1})); }); testWidgets('Adding a tile overlay', (WidgetTester tester) async { @@ -61,16 +45,8 @@ void main() { await tester.pumpWidget(_mapWithTileOverlays({t1})); await tester.pumpWidget(_mapWithTileOverlays({t1, t2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.tileOverlaysToAdd.length, 1); - - final TileOverlay addedTileOverlay = - platformGoogleMap.tileOverlaysToAdd.first; - expect(addedTileOverlay, equals(t2)); - expect(platformGoogleMap.tileOverlayIdsToRemove.isEmpty, true); - - expect(platformGoogleMap.tileOverlaysToChange.isEmpty, true); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.tileOverlaySets.last, equals({t1, t2})); }); testWidgets('Removing a tile overlay', (WidgetTester tester) async { @@ -80,32 +56,8 @@ void main() { await tester.pumpWidget(_mapWithTileOverlays({t1})); await tester.pumpWidget(_mapWithTileOverlays({})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.tileOverlayIdsToRemove.length, 1); - expect(platformGoogleMap.tileOverlayIdsToRemove.first, - equals(t1.tileOverlayId)); - - expect(platformGoogleMap.tileOverlaysToChange.isEmpty, true); - expect(platformGoogleMap.tileOverlaysToAdd.isEmpty, true); - }); - - testWidgets('Updating a tile overlay', (WidgetTester tester) async { - const TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); - const TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1'), zIndex: 10); - - await tester.pumpWidget(_mapWithTileOverlays({t1})); - await tester.pumpWidget(_mapWithTileOverlays({t2})); - - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.tileOverlaysToChange.length, 1); - expect(platformGoogleMap.tileOverlaysToChange.first, equals(t2)); - - expect(platformGoogleMap.tileOverlayIdsToRemove.isEmpty, true); - expect(platformGoogleMap.tileOverlaysToAdd.isEmpty, true); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.tileOverlaySets.last, equals({})); }); testWidgets('Updating a tile overlay', (WidgetTester tester) async { @@ -117,94 +69,7 @@ void main() { await tester.pumpWidget(_mapWithTileOverlays({t1})); await tester.pumpWidget(_mapWithTileOverlays({t2})); - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - expect(platformGoogleMap.tileOverlaysToChange.length, 1); - - final TileOverlay update = platformGoogleMap.tileOverlaysToChange.first; - expect(update, equals(t2)); - expect(update.zIndex, 10); - }); - - testWidgets('Multi Update', (WidgetTester tester) async { - TileOverlay t1 = - const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); - TileOverlay t2 = - const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_2')); - final Set prev = {t1, t2}; - t1 = const TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_1'), visible: false); - t2 = const TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_2'), zIndex: 10); - final Set cur = {t1, t2}; - - await tester.pumpWidget(_mapWithTileOverlays(prev)); - await tester.pumpWidget(_mapWithTileOverlays(cur)); - - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - - expect(platformGoogleMap.tileOverlaysToChange, cur); - expect(platformGoogleMap.tileOverlayIdsToRemove.isEmpty, true); - expect(platformGoogleMap.tileOverlaysToAdd.isEmpty, true); - }); - - testWidgets('Multi Update', (WidgetTester tester) async { - TileOverlay t2 = - const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_2')); - const TileOverlay t3 = - TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_3')); - final Set prev = {t2, t3}; - - // t1 is added, t2 is updated, t3 is removed. - const TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); - t2 = const TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_2'), zIndex: 10); - final Set cur = {t1, t2}; - - await tester.pumpWidget(_mapWithTileOverlays(prev)); - await tester.pumpWidget(_mapWithTileOverlays(cur)); - - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - - expect(platformGoogleMap.tileOverlaysToChange.length, 1); - expect(platformGoogleMap.tileOverlaysToAdd.length, 1); - expect(platformGoogleMap.tileOverlayIdsToRemove.length, 1); - - expect(platformGoogleMap.tileOverlaysToChange.first, equals(t2)); - expect(platformGoogleMap.tileOverlaysToAdd.first, equals(t1)); - expect(platformGoogleMap.tileOverlayIdsToRemove.first, - equals(t3.tileOverlayId)); - }); - - testWidgets('Partial Update', (WidgetTester tester) async { - const TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); - const TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_2')); - TileOverlay t3 = - const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_3')); - final Set prev = {t1, t2, t3}; - t3 = const TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_3'), zIndex: 10); - final Set cur = {t1, t2, t3}; - - await tester.pumpWidget(_mapWithTileOverlays(prev)); - await tester.pumpWidget(_mapWithTileOverlays(cur)); - - final FakePlatformGoogleMap platformGoogleMap = - fakePlatformViewsController.lastCreatedView!; - - expect(platformGoogleMap.tileOverlaysToChange, {t3}); - expect(platformGoogleMap.tileOverlayIdsToRemove.isEmpty, true); - expect(platformGoogleMap.tileOverlaysToAdd.isEmpty, true); + final PlatformMapStateRecorder map = platform.lastCreatedMap; + expect(map.tileOverlaySets.last, equals({t2})); }); } - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml index 525576f013c..dab6b69b98b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml @@ -25,3 +25,10 @@ dev_dependencies: integration_test: sdk: flutter mockito: 5.4.1 + +dependency_overrides: + # Override the google_maps_flutter dependency on google_maps_flutter_web. + # TODO(ditman): Unwind the circular dependency. This will create problems + # if we need to make a breaking change to google_maps_flutter_web. + google_maps_flutter_web: + path: ../