From 2592be41e21e338f3bf6530961b60112365672a3 Mon Sep 17 00:00:00 2001 From: tenninebt <5684363+tenninebt@users.noreply.github.com> Date: Fri, 18 Sep 2026 03:02:46 +0200 Subject: [PATCH 1/2] [google_maps_flutter] Add onPointOfInterestTap platform implementations Android, iOS (sdk9/sdk10), and web implementations for the published platform interface 2.17.0 stream. --- .../google_maps_flutter_android/CHANGELOG.md | 4 ++ .../googlemaps/GoogleMapController.java | 10 ++++ .../plugins/googlemaps/GoogleMapListener.java | 1 + .../io/flutter/plugins/googlemaps/Messages.kt | 19 +++++++ .../googlemaps/GoogleMapControllerTest.java | 23 +++++++++ .../example/pubspec.yaml | 2 +- .../fake_google_maps_flutter_platform.dart | 5 ++ .../lib/src/google_maps_flutter_android.dart | 10 ++++ .../lib/src/messages.g.dart | 28 ++++++++++ .../pigeons/messages.dart | 3 ++ .../google_maps_flutter_android/pubspec.yaml | 4 +- .../google_maps_flutter_android_test.dart | 14 +++++ .../CHANGELOG.md | 4 ++ .../ios/RunnerTests/GoogleMapsTests.swift | 51 +++++++++++++++++++ .../TestUtils/TestMapEventHandler.swift | 2 + .../example/pubspec.yaml | 2 +- .../fake_google_maps_flutter_platform.dart | 5 ++ .../GoogleMapController.swift | 49 +++++++++++++++--- .../Messages.g.swift | 25 +++++++++ .../lib/src/google_maps_flutter_ios.dart | 10 ++++ .../lib/src/messages.g.dart | 28 ++++++++++ .../pigeons/messages.dart | 4 ++ .../pubspec.yaml | 4 +- .../test/google_maps_flutter_ios_test.dart | 14 +++++ .../google_maps_flutter_ios_sdk9/CHANGELOG.md | 4 ++ .../ios/RunnerTests/GoogleMapsTests.swift | 51 +++++++++++++++++++ .../TestUtils/TestMapEventHandler.swift | 2 + .../example/pubspec.yaml | 2 +- .../fake_google_maps_flutter_platform.dart | 5 ++ .../GoogleMapController.swift | 49 +++++++++++++++--- .../Messages.g.swift | 25 +++++++++ .../lib/src/google_maps_flutter_ios.dart | 10 ++++ .../lib/src/messages.g.dart | 28 ++++++++++ .../pigeons/messages.dart | 4 ++ .../google_maps_flutter_ios_sdk9/pubspec.yaml | 4 +- .../test/google_maps_flutter_ios_test.dart | 14 +++++ .../ios/RunnerTests/GoogleMapsTests.swift | 51 +++++++++++++++++++ .../TestUtils/TestMapEventHandler.swift | 2 + .../fake_google_maps_flutter_platform.dart | 5 ++ .../GoogleMapController.swift | 49 +++++++++++++++--- .../google_maps_flutter_ios/Messages.g.swift | 25 +++++++++ .../lib/src/google_maps_flutter_ios.dart | 10 ++++ .../lib/src/messages.g.dart | 28 ++++++++++ .../pigeons/messages.dart | 4 ++ .../test/google_maps_flutter_ios_test.dart | 14 +++++ .../google_maps_flutter_web/CHANGELOG.md | 4 ++ .../example/3-64/pubspec.yaml | 2 +- .../google_maps_controller_test.dart | 30 +++++++++++ .../google_maps_plugin_test.dart | 7 +++ .../example/latest/pubspec.yaml | 2 +- .../lib/google_maps_flutter_web.dart | 1 + .../lib/src/google_maps_controller.dart | 5 ++ .../lib/src/google_maps_flutter_web.dart | 5 ++ .../lib/src/map_mouse_event_extension.dart | 22 ++++++++ .../google_maps_flutter_web/pubspec.yaml | 4 +- 55 files changed, 752 insertions(+), 37 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md index f0bb268895f8..49dd6718698c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.20.0 + +* Adds support for tapping points of interest on the map. + ## 2.19.13 * Updates pigeon dev_dependency to ^27.3.2 for analyzer 14 compatibility. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java index 273e0989cbfe..56a4716b88fd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java @@ -38,6 +38,7 @@ import com.google.android.gms.maps.model.MapCapabilities; import com.google.android.gms.maps.model.MapStyleOptions; import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.PointOfInterest; import com.google.android.gms.maps.model.Polygon; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.TileOverlay; @@ -385,6 +386,14 @@ public void onCircleClick(Circle circle) { circlesController.onCircleTap(circle.getId()); } + @Override + public void onPoiClick(PointOfInterest pointOfInterest) { + if (pointOfInterest.placeId != null) { + flutterApi.onPointOfInterestTap( + pointOfInterest.placeId, (Result result) -> Unit.INSTANCE); + } + } + @Override public void onGroundOverlayClick(@NonNull GroundOverlay groundOverlay) { groundOverlaysController.onGroundOverlayTap(groundOverlay.getId()); @@ -421,6 +430,7 @@ private void setGoogleMapListener(@Nullable GoogleMapListener listener) { googleMap.setOnPolygonClickListener(listener); googleMap.setOnPolylineClickListener(listener); googleMap.setOnCircleClickListener(listener); + googleMap.setOnPoiClickListener(listener); googleMap.setOnMapClickListener(listener); googleMap.setOnMapLongClickListener(listener); googleMap.setOnGroundOverlayClickListener(listener); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java index a9a5ae7f86ba..2816c5ebe124 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java @@ -15,6 +15,7 @@ interface GoogleMapListener GoogleMap.OnPolygonClickListener, GoogleMap.OnPolylineClickListener, GoogleMap.OnCircleClickListener, + GoogleMap.OnPoiClickListener, GoogleMap.OnMapClickListener, GoogleMap.OnMapLongClickListener, GoogleMap.OnMarkerDragListener, diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/kotlin/io/flutter/plugins/googlemaps/Messages.kt b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/kotlin/io/flutter/plugins/googlemaps/Messages.kt index 71c270f71682..c1b95e5c1588 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/kotlin/io/flutter/plugins/googlemaps/Messages.kt +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/kotlin/io/flutter/plugins/googlemaps/Messages.kt @@ -4247,6 +4247,25 @@ class MapsCallbackApi( } } } + /** Called when a point of interest is tapped. */ + fun onPointOfInterestTap(placeIdArg: String, callback: (Result) -> Unit) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = + "dev.flutter.pigeon.google_maps_flutter_android.MapsCallbackApi.onPointOfInterestTap$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(placeIdArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(MessagesPigeonUtils.createConnectionError(channelName))) + } + } + } /** Called when a marker cluster is tapped. */ fun onClusterTap(clusterArg: PlatformCluster, callback: (Result) -> Unit) { val separatedMessageChannelSuffix = diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java index 38c95e18f3f3..c3fabeb7476e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java @@ -27,6 +27,7 @@ import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MapCapabilities; import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.PointOfInterest; import com.google.maps.android.clustering.ClusterManager; import io.flutter.plugin.common.BinaryMessenger; import java.util.ArrayList; @@ -258,6 +259,28 @@ public void OnClusterItemInfoWindowClickCallsMarkersController() { verify(mockMarkersController, times(1)).onClusterItemInfoWindowTap(markerBuilder.markerId()); } + @Test + public void OnPoiClickCallsFlutterApi() { + GoogleMapController googleMapController = getGoogleMapControllerWithMockedDependencies(); + googleMapController.onMapReady(mockGoogleMap); + + PointOfInterest pointOfInterest = + new PointOfInterest(new LatLng(0, 0), "place-123", "Test Place"); + googleMapController.onPoiClick(pointOfInterest); + + verify(flutterApi, times(1)).onPointOfInterestTap(eq("place-123"), any()); + } + + @Test + public void OnPoiClickNullPlaceIdDoesNotCallFlutterApi() { + GoogleMapController googleMapController = getGoogleMapControllerWithMockedDependencies(); + googleMapController.onMapReady(mockGoogleMap); + + googleMapController.onPoiClick(new PointOfInterest(new LatLng(0, 0), null, "Test Place")); + + verify(flutterApi, times(0)).onPointOfInterestTap(any(), any()); + } + @Test public void SetInitialHeatmaps() { GoogleMapController googleMapController = getGoogleMapControllerWithMockedDependencies(); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml index 9a3889d52d48..c9f28ea3c66a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - google_maps_flutter_platform_interface: ^2.13.0 + google_maps_flutter_platform_interface: ^2.17.0 dev_dependencies: build_runner: ^2.1.10 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/test/fake_google_maps_flutter_platform.dart index 654c0b80a4e4..29b422cc29ec 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/test/fake_google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/test/fake_google_maps_flutter_platform.dart @@ -206,6 +206,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override Stream onGroundOverlayTap({required int mapId}) { return mapEventStreamController.stream.whereType(); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart index 9403484d2289..c5a87ef525fb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart @@ -205,6 +205,11 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Stream onGroundOverlayTap({required int mapId}) { return _events(mapId).whereType(); @@ -1079,6 +1084,11 @@ class HostMapMessageHandler implements MapsCallbackApi { streamController.add(CircleTapEvent(mapId, CircleId(circleId))); } + @override + void onPointOfInterestTap(String placeId) { + streamController.add(PointOfInterestTapEvent(mapId, PointOfInterestId(placeId))); + } + @override void onClusterTap(PlatformCluster cluster) { streamController.add( diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart index 7c66f0601f3c..269946503c9c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart @@ -3656,6 +3656,9 @@ abstract class MapsCallbackApi { /// Called when a circle is tapped. void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. void onClusterTap(PlatformCluster cluster); @@ -3951,6 +3954,31 @@ abstract class MapsCallbackApi { }); } } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_maps_flutter_android.MapsCallbackApi.onPointOfInterestTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + final List args = message! as List; + final String arg_placeId = args[0]! as String; + try { + api.onPointOfInterestTap(arg_placeId); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } { final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.google_maps_flutter_android.MapsCallbackApi.onClusterTap$messageChannelSuffix', diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart index 2b14821fb396..24bbebe55438 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart @@ -831,6 +831,9 @@ abstract class MapsCallbackApi { /// Called when a circle is tapped. void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. void onClusterTap(PlatformCluster cluster); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml index 7a6b7d395c07..ed87d07f165e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_android description: Android implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.19.13 +version: 2.20.0 environment: sdk: ^3.12.0 @@ -21,7 +21,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - google_maps_flutter_platform_interface: ^2.13.0 + google_maps_flutter_platform_interface: ^2.17.0 meta: ^1.10.0 stream_transform: ^2.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart index 968558a9d0c0..63395b99517b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart @@ -978,6 +978,20 @@ void main() { expect((await stream.next).value.value, equals(objectId)); }); + test('points of interest send tap events to correct stream', () async { + const mapId = 1; + const placeId = 'place-123'; + + final maps = GoogleMapsFlutterAndroid(); + final HostMapMessageHandler callbackHandler = maps.ensureHandlerInitialized(mapId); + + final stream = StreamQueue(maps.onPointOfInterestTap(mapId: mapId)); + + callbackHandler.onPointOfInterestTap(placeId); + + expect((await stream.next).value.value, equals(placeId)); + }); + test('clusters send tap events to correct stream', () async { const mapId = 1; const managerId = 'manager-id'; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/CHANGELOG.md index dd1560e59d0a..74c22c66e382 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.19.0 + +* Adds support for tapping points of interest on the map. + ## 2.18.13 * Adopts new Pigeon async Swift support. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift index 2a0b6cebfa84..787c7788f3a7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift @@ -38,6 +38,27 @@ class StubBinaryMessenger: NSObject, FlutterBinaryMessenger { } } +/// Records POI tap callbacks for unit tests. +class MockMapEventHandler: TestMapEventHandler { + var lastTappedPointOfInterestPlaceIdentifier: String? + private var pointOfInterestTapContinuation: CheckedContinuation? + + func waitForPointOfInterestTap() async { + if lastTappedPointOfInterestPlaceIdentifier != nil { + return + } + await withCheckedContinuation { continuation in + pointOfInterestTapContinuation = continuation + } + } + + override func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws { + lastTappedPointOfInterestPlaceIdentifier = placeIdArg + pointOfInterestTapContinuation?.resume() + pointOfInterestTapContinuation = nil + } +} + class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { var viewController: UIViewController? { nil } func publish(_ value: NSObject) {} @@ -167,6 +188,36 @@ class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { #expect(mockTransactionWrapper.animationDuration == Double(durationMilliseconds) / 1000) } + @Test func didTapPOIForwardsPlaceIdentifierToCallbackApi() async { + let frame = CGRect(x: 0, y: 0, width: 100, height: 100) + let mapViewOptions = GMSMapViewOptions() + mapViewOptions.frame = frame + mapViewOptions.camera = GMSCameraPosition(latitude: 0, longitude: 0, zoom: 0) + + let mapView = PartiallyMockedMapView(options: mapViewOptions) + + let mapEventHandler = MockMapEventHandler() + let controller = GoogleMapController( + mapView: mapView, + viewIdentifier: 0, + creationParameters: emptyCreationParameters(), + assetProvider: TestAssetProvider(), + binaryMessenger: StubBinaryMessenger(), + callbackHandler: mapEventHandler + ) + + async let poiTapReceived: Void = mapEventHandler.waitForPointOfInterestTap() + controller.mapView( + mapView, + didTapPOIWithPlaceID: "place-123", + name: "Test Place", + location: CLLocationCoordinate2DMake(0, 0) + ) + await poiTapReceived + + #expect(mapEventHandler.lastTappedPointOfInterestPlaceIdentifier == "place-123") + } + @Test func inspectorAPICameraPosition() throws { let frame = CGRect(x: 0, y: 0, width: 100, height: 100) let mapViewOptions = GMSMapViewOptions() diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift index 83e76fdf5b80..20feceab0707 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift @@ -39,6 +39,8 @@ class TestMapEventHandler: MapEventDelegate { func didTapCircle(withIdentifier circleIdArg: String) async throws {} + func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws {} + func didTapCluster(_ clusterArg: PlatformCluster) async throws {} func didTapPolygon(withIdentifier polygonIdArg: String) async throws {} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/pubspec.yaml index 7d916be0452d..5eed7f7f69be 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - google_maps_flutter_platform_interface: ^2.14.1 + google_maps_flutter_platform_interface: ^2.17.0 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/test/fake_google_maps_flutter_platform.dart index 3e8beefc78c4..f4bc1fd82c8f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/test/fake_google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/test/fake_google_maps_flutter_platform.dart @@ -206,6 +206,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override Stream onTap({required int mapId}) { return mapEventStreamController.stream.whereType(); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift index cc99b83f3ccd..53557b1ab7b7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift @@ -59,7 +59,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV /// The Google Maps SDK map view managed by this controller. let mapView: GMSMapView /// The Pigeon callback API implementation, used to send events to the Dart side. - let dartCallbackHandler: MapsCallbackApi + let dartCallbackHandler: MapEventDelegate /// The main Pigeon API implementation, separate to avoid lifetime extension. let callHandler: MapCallHandler /// The inspector API implementation, separate to avoid lifetime extension. @@ -101,21 +101,47 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV options.mapID = GMSMapID(identifier: mapId) } + let binaryMessenger = registrar.messenger() self.init( mapView: GMSMapView(options: options), viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: DefaultAssetProvider(registrar: registrar), - binaryMessenger: registrar.messenger() + binaryMessenger: binaryMessenger, + callbackHandler: MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) ) } - init( + convenience init( mapView: GMSMapView, viewIdentifier viewId: Int64, creationParameters: PlatformMapViewCreationParams, assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger + ) { + self.init( + mapView: mapView, + viewIdentifier: viewId, + creationParameters: creationParameters, + assetProvider: assetProvider, + binaryMessenger: binaryMessenger, + callbackHandler: MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) + ) + } + + init( + mapView: GMSMapView, + viewIdentifier viewId: Int64, + creationParameters: PlatformMapViewCreationParams, + assetProvider: AssetProvider, + binaryMessenger: FlutterBinaryMessenger, + callbackHandler: MapEventDelegate ) { self.mapView = mapView mapView.accessibilityElementsHidden = false @@ -134,10 +160,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV // End duplicate code. let pigeonSuffix = String(format: "%lld", viewId) - dartCallbackHandler = MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: pigeonSuffix - ) + dartCallbackHandler = callbackHandler let markerType = creationParameters.mapConfiguration.markerType @@ -167,7 +190,8 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV heatmapsController = HeatmapsController(mapView: mapView) tileOverlaysController = TileOverlaysController( mapView: mapView, - tileProvider: dartCallbackHandler + tileProvider: (callbackHandler as? MapsCallbackApi) + ?? MapsCallbackApi(binaryMessenger: binaryMessenger, messageChannelSuffix: pigeonSuffix) ) groundOverlaysController = GroundOverlaysController( mapView: mapView, @@ -396,6 +420,15 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV } } + public func mapView( + _ mapView: GMSMapView, didTapPOIWithPlaceID placeID: String, name: String, + location: CLLocationCoordinate2D + ) { + Task { + try await dartCallbackHandler.didTapPointOfInterest(withPlaceIdentifier: placeID) + } + } + func interpretMapConfiguration(_ config: PlatformMapConfiguration) { // Any changes here must also be made to the `init` method above. See the comment there for // details. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/Messages.g.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/Messages.g.swift index 3689467b7fea..0439edf54b0e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/Messages.g.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/Messages.g.swift @@ -3455,6 +3455,8 @@ protocol MapsCallbackApiProtocol { @MainActor func didTapInfoWindowOfMarker(withIdentifier markerIdArg: String) async throws /// Called when a circle is tapped. @MainActor func didTapCircle(withIdentifier circleIdArg: String) async throws + /// Called when a point of interest is tapped. + @MainActor func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws /// Called when a marker cluster is tapped. @MainActor func didTapCluster(_ clusterArg: PlatformCluster) async throws /// Called when a polygon is tapped. @@ -3738,6 +3740,29 @@ class MapsCallbackApi: MapsCallbackApiProtocol { } } } + /// Called when a point of interest is tapped. + @MainActor func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws { + return try await withCheckedThrowingContinuation { continuation in + let channelName: String = + "dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([placeIdArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + continuation.resume(throwing: createConnectionError(withChannelName: channelName)) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + continuation.resume(throwing: PigeonError(code: code, message: message, details: details)) + } else { + continuation.resume() + } + } + } + } /// Called when a marker cluster is tapped. @MainActor func didTapCluster(_ clusterArg: PlatformCluster) async throws { return try await withCheckedThrowingContinuation { continuation in diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/google_maps_flutter_ios.dart index 11df18180a50..252912f722cd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/google_maps_flutter_ios.dart @@ -182,6 +182,11 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Stream onGroundOverlayTap({required int mapId}) { return _events(mapId).whereType(); @@ -957,6 +962,11 @@ class HostMapMessageHandler implements MapsCallbackApi { streamController.add(CircleTapEvent(mapId, CircleId(circleId))); } + @override + void onPointOfInterestTap(String placeId) { + streamController.add(PointOfInterestTapEvent(mapId, PointOfInterestId(placeId))); + } + @override void onClusterTap(PlatformCluster cluster) { streamController.add( diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/messages.g.dart index d5c89d021360..76c32ea35680 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/lib/src/messages.g.dart @@ -3456,6 +3456,9 @@ abstract class MapsCallbackApi { /// Called when a circle is tapped. void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. void onClusterTap(PlatformCluster cluster); @@ -3751,6 +3754,31 @@ abstract class MapsCallbackApi { }); } } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + final List args = message! as List; + final String arg_placeId = args[0]! as String; + try { + api.onPointOfInterestTap(arg_placeId); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } { final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap$messageChannelSuffix', diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pigeons/messages.dart index 3f620b692269..673502373e3a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pigeons/messages.dart @@ -803,6 +803,10 @@ abstract class MapsCallbackApi { @SwiftFunction('didTapCircle(withIdentifier:)') void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + @SwiftFunction('didTapPointOfInterest(withPlaceIdentifier:)') + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. @SwiftFunction('didTapCluster(_:)') void onClusterTap(PlatformCluster cluster); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pubspec.yaml index f069ebac61bd..1dbb90cdf9f6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios_sdk10 description: iOS implementation of the google_maps_flutter plugin using Google Maps SDK 10. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios_sdk10 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.18.13 +version: 2.19.0 environment: sdk: ^3.10.0 @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.14.2 + google_maps_flutter_platform_interface: ^2.17.0 meta: ^1.10.0 stream_transform: ^2.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/test/google_maps_flutter_ios_test.dart index 6d21a3e81c1f..978a022a4194 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/test/google_maps_flutter_ios_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/test/google_maps_flutter_ios_test.dart @@ -903,6 +903,20 @@ void main() { expect((await stream.next).value.value, equals(objectId)); }); + test('points of interest send tap events to correct stream', () async { + const mapId = 1; + const placeId = 'place-id'; + + final maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = maps.ensureHandlerInitialized(mapId); + + final stream = StreamQueue(maps.onPointOfInterestTap(mapId: mapId)); + + callbackHandler.onPointOfInterestTap(placeId); + + expect((await stream.next).value.value, equals(placeId)); + }); + test('clusters send tap events to correct stream', () async { const mapId = 1; const managerId = 'manager-id'; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/CHANGELOG.md index da0ffcd8f6da..7987544d27e7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.19.0 + +* Adds support for tapping points of interest on the map. + ## 2.18.14 * Adopts new Pigeon async Swift support. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift index 189a00f20440..9b87d2afc3de 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift @@ -38,6 +38,27 @@ class StubBinaryMessenger: NSObject, FlutterBinaryMessenger { } } +/// Records POI tap callbacks for unit tests. +class MockMapEventHandler: TestMapEventHandler { + var lastTappedPointOfInterestPlaceIdentifier: String? + private var pointOfInterestTapContinuation: CheckedContinuation? + + func waitForPointOfInterestTap() async { + if lastTappedPointOfInterestPlaceIdentifier != nil { + return + } + await withCheckedContinuation { continuation in + pointOfInterestTapContinuation = continuation + } + } + + override func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws { + lastTappedPointOfInterestPlaceIdentifier = placeIdArg + pointOfInterestTapContinuation?.resume() + pointOfInterestTapContinuation = nil + } +} + class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { var viewController: UIViewController? { nil } func publish(_ value: NSObject) {} @@ -167,6 +188,36 @@ class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { #expect(mockTransactionWrapper.animationDuration == Double(durationMilliseconds) / 1000) } + @Test func didTapPOIForwardsPlaceIdentifierToCallbackApi() async { + let frame = CGRect(x: 0, y: 0, width: 100, height: 100) + let mapViewOptions = GMSMapViewOptions() + mapViewOptions.frame = frame + mapViewOptions.camera = GMSCameraPosition(latitude: 0, longitude: 0, zoom: 0) + + let mapView = PartiallyMockedMapView(options: mapViewOptions) + + let mapEventHandler = MockMapEventHandler() + let controller = GoogleMapController( + mapView: mapView, + viewIdentifier: 0, + creationParameters: emptyCreationParameters(), + assetProvider: TestAssetProvider(), + binaryMessenger: StubBinaryMessenger(), + callbackHandler: mapEventHandler + ) + + async let poiTapReceived: Void = mapEventHandler.waitForPointOfInterestTap() + controller.mapView( + mapView, + didTapPOIWithPlaceID: "place-123", + name: "Test Place", + location: CLLocationCoordinate2DMake(0, 0) + ) + await poiTapReceived + + #expect(mapEventHandler.lastTappedPointOfInterestPlaceIdentifier == "place-123") + } + @Test func inspectorAPICameraPosition() throws { let frame = CGRect(x: 0, y: 0, width: 100, height: 100) let mapViewOptions = GMSMapViewOptions() diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift index 2c72dffe0570..d96ab18767d9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift @@ -39,6 +39,8 @@ class TestMapEventHandler: MapEventDelegate { func didTapCircle(withIdentifier circleIdArg: String) async throws {} + func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws {} + func didTapCluster(_ clusterArg: PlatformCluster) async throws {} func didTapPolygon(withIdentifier polygonIdArg: String) async throws {} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/pubspec.yaml index d041adc00616..336b6a750a50 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - google_maps_flutter_platform_interface: ^2.14.1 + google_maps_flutter_platform_interface: ^2.17.0 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/test/fake_google_maps_flutter_platform.dart index 3e8beefc78c4..f4bc1fd82c8f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/test/fake_google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/test/fake_google_maps_flutter_platform.dart @@ -206,6 +206,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override Stream onTap({required int mapId}) { return mapEventStreamController.stream.whereType(); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift index cc99b83f3ccd..53557b1ab7b7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift @@ -59,7 +59,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV /// The Google Maps SDK map view managed by this controller. let mapView: GMSMapView /// The Pigeon callback API implementation, used to send events to the Dart side. - let dartCallbackHandler: MapsCallbackApi + let dartCallbackHandler: MapEventDelegate /// The main Pigeon API implementation, separate to avoid lifetime extension. let callHandler: MapCallHandler /// The inspector API implementation, separate to avoid lifetime extension. @@ -101,21 +101,47 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV options.mapID = GMSMapID(identifier: mapId) } + let binaryMessenger = registrar.messenger() self.init( mapView: GMSMapView(options: options), viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: DefaultAssetProvider(registrar: registrar), - binaryMessenger: registrar.messenger() + binaryMessenger: binaryMessenger, + callbackHandler: MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) ) } - init( + convenience init( mapView: GMSMapView, viewIdentifier viewId: Int64, creationParameters: PlatformMapViewCreationParams, assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger + ) { + self.init( + mapView: mapView, + viewIdentifier: viewId, + creationParameters: creationParameters, + assetProvider: assetProvider, + binaryMessenger: binaryMessenger, + callbackHandler: MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) + ) + } + + init( + mapView: GMSMapView, + viewIdentifier viewId: Int64, + creationParameters: PlatformMapViewCreationParams, + assetProvider: AssetProvider, + binaryMessenger: FlutterBinaryMessenger, + callbackHandler: MapEventDelegate ) { self.mapView = mapView mapView.accessibilityElementsHidden = false @@ -134,10 +160,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV // End duplicate code. let pigeonSuffix = String(format: "%lld", viewId) - dartCallbackHandler = MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: pigeonSuffix - ) + dartCallbackHandler = callbackHandler let markerType = creationParameters.mapConfiguration.markerType @@ -167,7 +190,8 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV heatmapsController = HeatmapsController(mapView: mapView) tileOverlaysController = TileOverlaysController( mapView: mapView, - tileProvider: dartCallbackHandler + tileProvider: (callbackHandler as? MapsCallbackApi) + ?? MapsCallbackApi(binaryMessenger: binaryMessenger, messageChannelSuffix: pigeonSuffix) ) groundOverlaysController = GroundOverlaysController( mapView: mapView, @@ -396,6 +420,15 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV } } + public func mapView( + _ mapView: GMSMapView, didTapPOIWithPlaceID placeID: String, name: String, + location: CLLocationCoordinate2D + ) { + Task { + try await dartCallbackHandler.didTapPointOfInterest(withPlaceIdentifier: placeID) + } + } + func interpretMapConfiguration(_ config: PlatformMapConfiguration) { // Any changes here must also be made to the `init` method above. See the comment there for // details. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/Messages.g.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/Messages.g.swift index 3689467b7fea..0439edf54b0e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/Messages.g.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/Messages.g.swift @@ -3455,6 +3455,8 @@ protocol MapsCallbackApiProtocol { @MainActor func didTapInfoWindowOfMarker(withIdentifier markerIdArg: String) async throws /// Called when a circle is tapped. @MainActor func didTapCircle(withIdentifier circleIdArg: String) async throws + /// Called when a point of interest is tapped. + @MainActor func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws /// Called when a marker cluster is tapped. @MainActor func didTapCluster(_ clusterArg: PlatformCluster) async throws /// Called when a polygon is tapped. @@ -3738,6 +3740,29 @@ class MapsCallbackApi: MapsCallbackApiProtocol { } } } + /// Called when a point of interest is tapped. + @MainActor func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws { + return try await withCheckedThrowingContinuation { continuation in + let channelName: String = + "dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([placeIdArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + continuation.resume(throwing: createConnectionError(withChannelName: channelName)) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + continuation.resume(throwing: PigeonError(code: code, message: message, details: details)) + } else { + continuation.resume() + } + } + } + } /// Called when a marker cluster is tapped. @MainActor func didTapCluster(_ clusterArg: PlatformCluster) async throws { return try await withCheckedThrowingContinuation { continuation in diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/google_maps_flutter_ios.dart index 11df18180a50..252912f722cd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/google_maps_flutter_ios.dart @@ -182,6 +182,11 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Stream onGroundOverlayTap({required int mapId}) { return _events(mapId).whereType(); @@ -957,6 +962,11 @@ class HostMapMessageHandler implements MapsCallbackApi { streamController.add(CircleTapEvent(mapId, CircleId(circleId))); } + @override + void onPointOfInterestTap(String placeId) { + streamController.add(PointOfInterestTapEvent(mapId, PointOfInterestId(placeId))); + } + @override void onClusterTap(PlatformCluster cluster) { streamController.add( diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/messages.g.dart index d5c89d021360..76c32ea35680 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/lib/src/messages.g.dart @@ -3456,6 +3456,9 @@ abstract class MapsCallbackApi { /// Called when a circle is tapped. void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. void onClusterTap(PlatformCluster cluster); @@ -3751,6 +3754,31 @@ abstract class MapsCallbackApi { }); } } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + final List args = message! as List; + final String arg_placeId = args[0]! as String; + try { + api.onPointOfInterestTap(arg_placeId); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } { final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap$messageChannelSuffix', diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pigeons/messages.dart index a9f2eae70806..a39bf066c24b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pigeons/messages.dart @@ -803,6 +803,10 @@ abstract class MapsCallbackApi { @SwiftFunction('didTapCircle(withIdentifier:)') void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + @SwiftFunction('didTapPointOfInterest(withPlaceIdentifier:)') + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. @SwiftFunction('didTapCluster(_:)') void onClusterTap(PlatformCluster cluster); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pubspec.yaml index 4fa42bc6f481..d05a9ac0b688 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios_sdk9 description: iOS implementation of the google_maps_flutter plugin using Google Maps SDK 9. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios_sdk9 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.18.14 +version: 2.19.0 environment: sdk: ^3.10.0 @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.14.2 + google_maps_flutter_platform_interface: ^2.17.0 meta: ^1.10.0 stream_transform: ^2.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/test/google_maps_flutter_ios_test.dart index 6d21a3e81c1f..978a022a4194 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/test/google_maps_flutter_ios_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/test/google_maps_flutter_ios_test.dart @@ -903,6 +903,20 @@ void main() { expect((await stream.next).value.value, equals(objectId)); }); + test('points of interest send tap events to correct stream', () async { + const mapId = 1; + const placeId = 'place-id'; + + final maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = maps.ensureHandlerInitialized(mapId); + + final stream = StreamQueue(maps.onPointOfInterestTap(mapId: mapId)); + + callbackHandler.onPointOfInterestTap(placeId); + + expect((await stream.next).value.value, equals(placeId)); + }); + test('clusters send tap events to correct stream', () async { const mapId = 1; const managerId = 'manager-id'; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift index e918732d5b1d..28d5c6378dee 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift @@ -38,6 +38,27 @@ class StubBinaryMessenger: NSObject, FlutterBinaryMessenger { } } +/// Records POI tap callbacks for unit tests. +class MockMapEventHandler: TestMapEventHandler { + var lastTappedPointOfInterestPlaceIdentifier: String? + private var pointOfInterestTapContinuation: CheckedContinuation? + + func waitForPointOfInterestTap() async { + if lastTappedPointOfInterestPlaceIdentifier != nil { + return + } + await withCheckedContinuation { continuation in + pointOfInterestTapContinuation = continuation + } + } + + override func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws { + lastTappedPointOfInterestPlaceIdentifier = placeIdArg + pointOfInterestTapContinuation?.resume() + pointOfInterestTapContinuation = nil + } +} + class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { var viewController: UIViewController? { nil } func publish(_ value: NSObject) {} @@ -167,6 +188,36 @@ class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { #expect(mockTransactionWrapper.animationDuration == Double(durationMilliseconds) / 1000) } + @Test func didTapPOIForwardsPlaceIdentifierToCallbackApi() async { + let frame = CGRect(x: 0, y: 0, width: 100, height: 100) + let mapViewOptions = GMSMapViewOptions() + mapViewOptions.frame = frame + mapViewOptions.camera = GMSCameraPosition(latitude: 0, longitude: 0, zoom: 0) + + let mapView = PartiallyMockedMapView(options: mapViewOptions) + + let mapEventHandler = MockMapEventHandler() + let controller = GoogleMapController( + mapView: mapView, + viewIdentifier: 0, + creationParameters: emptyCreationParameters(), + assetProvider: TestAssetProvider(), + binaryMessenger: StubBinaryMessenger(), + callbackHandler: mapEventHandler + ) + + async let poiTapReceived: Void = mapEventHandler.waitForPointOfInterestTap() + controller.mapView( + mapView, + didTapPOIWithPlaceID: "place-123", + name: "Test Place", + location: CLLocationCoordinate2DMake(0, 0) + ) + await poiTapReceived + + #expect(mapEventHandler.lastTappedPointOfInterestPlaceIdentifier == "place-123") + } + @Test func inspectorAPICameraPosition() throws { let frame = CGRect(x: 0, y: 0, width: 100, height: 100) let mapViewOptions = GMSMapViewOptions() diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift index be9370c2f997..c7ef526286cf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift @@ -39,6 +39,8 @@ class TestMapEventHandler: MapEventDelegate { func didTapCircle(withIdentifier circleIdArg: String) async throws {} + func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws {} + func didTapCluster(_ clusterArg: PlatformCluster) async throws {} func didTapPolygon(withIdentifier polygonIdArg: String) async throws {} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/test/fake_google_maps_flutter_platform.dart index 3e8beefc78c4..f4bc1fd82c8f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/test/fake_google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/test/fake_google_maps_flutter_platform.dart @@ -206,6 +206,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override Stream onTap({required int mapId}) { return mapEventStreamController.stream.whereType(); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift index cc99b83f3ccd..53557b1ab7b7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift @@ -59,7 +59,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV /// The Google Maps SDK map view managed by this controller. let mapView: GMSMapView /// The Pigeon callback API implementation, used to send events to the Dart side. - let dartCallbackHandler: MapsCallbackApi + let dartCallbackHandler: MapEventDelegate /// The main Pigeon API implementation, separate to avoid lifetime extension. let callHandler: MapCallHandler /// The inspector API implementation, separate to avoid lifetime extension. @@ -101,21 +101,47 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV options.mapID = GMSMapID(identifier: mapId) } + let binaryMessenger = registrar.messenger() self.init( mapView: GMSMapView(options: options), viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: DefaultAssetProvider(registrar: registrar), - binaryMessenger: registrar.messenger() + binaryMessenger: binaryMessenger, + callbackHandler: MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) ) } - init( + convenience init( mapView: GMSMapView, viewIdentifier viewId: Int64, creationParameters: PlatformMapViewCreationParams, assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger + ) { + self.init( + mapView: mapView, + viewIdentifier: viewId, + creationParameters: creationParameters, + assetProvider: assetProvider, + binaryMessenger: binaryMessenger, + callbackHandler: MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) + ) + } + + init( + mapView: GMSMapView, + viewIdentifier viewId: Int64, + creationParameters: PlatformMapViewCreationParams, + assetProvider: AssetProvider, + binaryMessenger: FlutterBinaryMessenger, + callbackHandler: MapEventDelegate ) { self.mapView = mapView mapView.accessibilityElementsHidden = false @@ -134,10 +160,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV // End duplicate code. let pigeonSuffix = String(format: "%lld", viewId) - dartCallbackHandler = MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: pigeonSuffix - ) + dartCallbackHandler = callbackHandler let markerType = creationParameters.mapConfiguration.markerType @@ -167,7 +190,8 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV heatmapsController = HeatmapsController(mapView: mapView) tileOverlaysController = TileOverlaysController( mapView: mapView, - tileProvider: dartCallbackHandler + tileProvider: (callbackHandler as? MapsCallbackApi) + ?? MapsCallbackApi(binaryMessenger: binaryMessenger, messageChannelSuffix: pigeonSuffix) ) groundOverlaysController = GroundOverlaysController( mapView: mapView, @@ -396,6 +420,15 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV } } + public func mapView( + _ mapView: GMSMapView, didTapPOIWithPlaceID placeID: String, name: String, + location: CLLocationCoordinate2D + ) { + Task { + try await dartCallbackHandler.didTapPointOfInterest(withPlaceIdentifier: placeID) + } + } + func interpretMapConfiguration(_ config: PlatformMapConfiguration) { // Any changes here must also be made to the `init` method above. See the comment there for // details. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/Messages.g.swift b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/Messages.g.swift index 3689467b7fea..0439edf54b0e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/Messages.g.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/Messages.g.swift @@ -3455,6 +3455,8 @@ protocol MapsCallbackApiProtocol { @MainActor func didTapInfoWindowOfMarker(withIdentifier markerIdArg: String) async throws /// Called when a circle is tapped. @MainActor func didTapCircle(withIdentifier circleIdArg: String) async throws + /// Called when a point of interest is tapped. + @MainActor func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws /// Called when a marker cluster is tapped. @MainActor func didTapCluster(_ clusterArg: PlatformCluster) async throws /// Called when a polygon is tapped. @@ -3738,6 +3740,29 @@ class MapsCallbackApi: MapsCallbackApiProtocol { } } } + /// Called when a point of interest is tapped. + @MainActor func didTapPointOfInterest(withPlaceIdentifier placeIdArg: String) async throws { + return try await withCheckedThrowingContinuation { continuation in + let channelName: String = + "dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([placeIdArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + continuation.resume(throwing: createConnectionError(withChannelName: channelName)) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + continuation.resume(throwing: PigeonError(code: code, message: message, details: details)) + } else { + continuation.resume() + } + } + } + } /// Called when a marker cluster is tapped. @MainActor func didTapCluster(_ clusterArg: PlatformCluster) async throws { return try await withCheckedThrowingContinuation { continuation in diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/google_maps_flutter_ios.dart index 11df18180a50..252912f722cd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/google_maps_flutter_ios.dart @@ -182,6 +182,11 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Stream onGroundOverlayTap({required int mapId}) { return _events(mapId).whereType(); @@ -957,6 +962,11 @@ class HostMapMessageHandler implements MapsCallbackApi { streamController.add(CircleTapEvent(mapId, CircleId(circleId))); } + @override + void onPointOfInterestTap(String placeId) { + streamController.add(PointOfInterestTapEvent(mapId, PointOfInterestId(placeId))); + } + @override void onClusterTap(PlatformCluster cluster) { streamController.add( diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/messages.g.dart index d5c89d021360..76c32ea35680 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/lib/src/messages.g.dart @@ -3456,6 +3456,9 @@ abstract class MapsCallbackApi { /// Called when a circle is tapped. void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. void onClusterTap(PlatformCluster cluster); @@ -3751,6 +3754,31 @@ abstract class MapsCallbackApi { }); } } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + final List args = message! as List; + final String arg_placeId = args[0]! as String; + try { + api.onPointOfInterestTap(arg_placeId); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } { final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap$messageChannelSuffix', diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/pigeons/messages.dart index 5a87863f002a..81f43e8718e3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/pigeons/messages.dart @@ -803,6 +803,10 @@ abstract class MapsCallbackApi { @SwiftFunction('didTapCircle(withIdentifier:)') void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + @SwiftFunction('didTapPointOfInterest(withPlaceIdentifier:)') + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. @SwiftFunction('didTapCluster(_:)') void onClusterTap(PlatformCluster cluster); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/test/google_maps_flutter_ios_test.dart index 6d21a3e81c1f..978a022a4194 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/test/google_maps_flutter_ios_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/test/google_maps_flutter_ios_test.dart @@ -903,6 +903,20 @@ void main() { expect((await stream.next).value.value, equals(objectId)); }); + test('points of interest send tap events to correct stream', () async { + const mapId = 1; + const placeId = 'place-id'; + + final maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = maps.ensureHandlerInitialized(mapId); + + final stream = StreamQueue(maps.onPointOfInterestTap(mapId: mapId)); + + callbackHandler.onPointOfInterestTap(placeId); + + expect((await stream.next).value.value, equals(placeId)); + }); + test('clusters send tap events to correct stream', () async { const mapId = 1; const managerId = 'manager-id'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index a65222c7bffd..a359e89a20e0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.4 + +* Adds support for tapping points of interest on the map. + ## 0.6.3+1 * Fixes flickering of byte-backed advanced markers when updating their position on web. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/3-64/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/3-64/pubspec.yaml index 320ad0bf1c51..67402690c4c9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/3-64/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/3-64/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.16.0 + google_maps_flutter_platform_interface: ^2.17.0 google_maps_flutter_web: path: ../.. web: ^1.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart index 1efafae752fc..6008918c23d9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_controller_test.dart @@ -248,6 +248,36 @@ void main() { expect(events[4], isA()); }); + testWidgets('emits point of interest tap when click has placeId', ( + WidgetTester tester, + ) async { + controller = createController() + ..debugSetOverrides( + createMap: (_, _) => map, + circles: circles, + heatmaps: heatmaps, + markers: markers, + polygons: polygons, + polylines: polylines, + groundOverlays: groundOverlays, + ) + ..init(); + + final Stream> capturedEvents = stream.stream.take(1); + + gmaps.event.trigger( + map, + 'click', + gmaps.IconMouseEvent(placeId: 'place-123')..latLng = gmaps.LatLng(0, 0), + ); + + final List> events = await capturedEvents.toList(); + + expect(events, hasLength(1)); + expect(events[0], isA()); + expect((events[0] as PointOfInterestTapEvent).value, const PointOfInterestId('place-123')); + }); + testWidgets('stops listening to map events once disposed', (WidgetTester tester) async { controller = createController() ..debugSetOverrides( diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart index ca1cde5334d9..da40728234f6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/integration_test/google_maps_plugin_test.dart @@ -467,6 +467,13 @@ void main() { await testStreamFiltering(stream, event); }); + testWidgets('onPointOfInterestTap', (WidgetTester tester) async { + final event = PointOfInterestTapEvent(mapId, const PointOfInterestId('place-123')); + + final Stream stream = plugin.onPointOfInterestTap(mapId: mapId); + + await testStreamFiltering(stream, event); + }); // Map taps testWidgets('onTap', (WidgetTester tester) async { final event = MapTapEvent(mapId, const LatLng(43.3597, -5.8458)); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml index 3c6b3674fd44..bd72568ae37b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/latest/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.16.0 + google_maps_flutter_platform_interface: ^2.17.0 google_maps_flutter_web: path: ../.. web: ^1.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index 0413faaf104d..266bebf47050 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -24,6 +24,7 @@ import 'package:web/web.dart' as web; import 'src/dom_window_extension.dart'; import 'src/google_maps_inspector_web.dart'; +import 'src/map_mouse_event_extension.dart'; import 'src/map_styler.dart'; import 'src/marker_clustering.dart'; import 'src/third_party/to_screen_location/to_screen_location.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 13e9054d9c7c..dd11bb2d7c65 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -289,6 +289,11 @@ class GoogleMapController { _onClickSubscription = map.onClick.listen((gmaps.MapMouseEventOrIconMouseEvent event) { assert(event.latLng != null); if (!_streamController.isClosed) { + final String? placeId = event.placeId; + if (placeId != null) { + _streamController.add(PointOfInterestTapEvent(_mapId, PointOfInterestId(placeId))); + return; + } _streamController.add(MapTapEvent(_mapId, gmLatLngToLatLng(event.latLng!))); } }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 9e1ce02285d9..9c21fb1af051 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -244,6 +244,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPointOfInterestTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Stream onTap({required int mapId}) { return _events(mapId).whereType(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart new file mode 100644 index 000000000000..870972a9c83e --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@JS() +library; + +import 'dart:js_interop'; + +import 'package:google_maps/google_maps.dart' as gmaps; + +/// Exposes the `placeId` property on map click events. +/// +/// [gmaps.MapMouseEventOrIconMouseEvent] only binds `latLng` upstream. POI +/// clicks are [gmaps.IconMouseEvent]s and carry a `placeId`; this extension +/// reads that property without `dart:js_interop_unsafe`. Prefer adding +/// `placeId` to the upstream binding when possible. +extension PlaceIdExtension on gmaps.MapMouseEventOrIconMouseEvent { + /// The place ID of a tapped point of interest, if this event is an icon + /// mouse event. Otherwise `null`. + external String? placeId; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 4da4852af280..46c529966de9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.6.3+1 +version: 0.6.4 environment: sdk: ^3.10.0 @@ -23,7 +23,7 @@ dependencies: flutter_web_plugins: sdk: flutter google_maps: ^8.1.0 - google_maps_flutter_platform_interface: ^2.16.0 + google_maps_flutter_platform_interface: ^2.17.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 web: ^1.0.0 From 0dad398f44e73b60e5145b515234cc9df1c750b4 Mon Sep 17 00:00:00 2001 From: tenninebt <5684363+tenninebt@users.noreply.github.com> Date: Thu, 24 Sep 2026 09:07:11 +0200 Subject: [PATCH 2/2] [google_maps_flutter] Address review feedback for POI tap DI and placeId Make placeId a read-only JS interop getter, and take tileProvider as an explicit GoogleMapController init parameter instead of casting or creating a second MapsCallbackApi. --- .../ios/RunnerTests/GoogleMapsTests.swift | 3 ++- .../TestUtils/TestMapEventHandler.swift | 4 +-- .../GoogleMapController.swift | 26 +++++++++++-------- .../ios/RunnerTests/GoogleMapsTests.swift | 3 ++- .../TestUtils/TestMapEventHandler.swift | 4 +-- .../GoogleMapController.swift | 26 +++++++++++-------- .../ios/RunnerTests/GoogleMapsTests.swift | 3 ++- .../TestUtils/TestMapEventHandler.swift | 4 +-- .../GoogleMapController.swift | 26 +++++++++++-------- .../lib/src/map_mouse_event_extension.dart | 2 +- 10 files changed, 58 insertions(+), 43 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift index 787c7788f3a7..2b5662e45f05 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.swift @@ -203,7 +203,8 @@ class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { creationParameters: emptyCreationParameters(), assetProvider: TestAssetProvider(), binaryMessenger: StubBinaryMessenger(), - callbackHandler: mapEventHandler + callbackHandler: mapEventHandler, + tileProvider: mapEventHandler ) async let poiTapReceived: Void = mapEventHandler.waitForPointOfInterestTap() diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift index 20feceab0707..fb49a27c8f74 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift @@ -6,8 +6,8 @@ import Foundation @testable import google_maps_flutter_ios_sdk10 -/// Fake implementation of MapEventDelegate for unit tests. -class TestMapEventHandler: MapEventDelegate { +/// Fake implementation of MapEventDelegate (and TileProviderDelegate) for unit tests. +class TestMapEventHandler: MapEventDelegate, TileProviderDelegate { func didStartCameraMove() async throws {} func didMoveCamera(to cameraPositionArg: PlatformCameraPosition) async throws {} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift index 53557b1ab7b7..6e023617ce5f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/GoogleMapController.swift @@ -102,16 +102,18 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV } let binaryMessenger = registrar.messenger() + let callbackHandler = MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) self.init( mapView: GMSMapView(options: options), viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: DefaultAssetProvider(registrar: registrar), binaryMessenger: binaryMessenger, - callbackHandler: MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: String(format: "%lld", viewId) - ) + callbackHandler: callbackHandler, + tileProvider: callbackHandler ) } @@ -122,16 +124,18 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger ) { + let callbackHandler = MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) self.init( mapView: mapView, viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: assetProvider, binaryMessenger: binaryMessenger, - callbackHandler: MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: String(format: "%lld", viewId) - ) + callbackHandler: callbackHandler, + tileProvider: callbackHandler ) } @@ -141,7 +145,8 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV creationParameters: PlatformMapViewCreationParams, assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger, - callbackHandler: MapEventDelegate + callbackHandler: MapEventDelegate, + tileProvider: TileProviderDelegate ) { self.mapView = mapView mapView.accessibilityElementsHidden = false @@ -190,8 +195,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV heatmapsController = HeatmapsController(mapView: mapView) tileOverlaysController = TileOverlaysController( mapView: mapView, - tileProvider: (callbackHandler as? MapsCallbackApi) - ?? MapsCallbackApi(binaryMessenger: binaryMessenger, messageChannelSuffix: pigeonSuffix) + tileProvider: tileProvider ) groundOverlaysController = GroundOverlaysController( mapView: mapView, diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift index 9b87d2afc3de..e52c3c3afe6e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.swift @@ -203,7 +203,8 @@ class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { creationParameters: emptyCreationParameters(), assetProvider: TestAssetProvider(), binaryMessenger: StubBinaryMessenger(), - callbackHandler: mapEventHandler + callbackHandler: mapEventHandler, + tileProvider: mapEventHandler ) async let poiTapReceived: Void = mapEventHandler.waitForPointOfInterestTap() diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift index d96ab18767d9..e76c32ab01a9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift @@ -6,8 +6,8 @@ import Foundation @testable import google_maps_flutter_ios_sdk9 -/// Fake implementation of MapEventDelegate for unit tests. -class TestMapEventHandler: MapEventDelegate { +/// Fake implementation of MapEventDelegate (and TileProviderDelegate) for unit tests. +class TestMapEventHandler: MapEventDelegate, TileProviderDelegate { func didStartCameraMove() async throws {} func didMoveCamera(to cameraPositionArg: PlatformCameraPosition) async throws {} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift index 53557b1ab7b7..6e023617ce5f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/GoogleMapController.swift @@ -102,16 +102,18 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV } let binaryMessenger = registrar.messenger() + let callbackHandler = MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) self.init( mapView: GMSMapView(options: options), viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: DefaultAssetProvider(registrar: registrar), binaryMessenger: binaryMessenger, - callbackHandler: MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: String(format: "%lld", viewId) - ) + callbackHandler: callbackHandler, + tileProvider: callbackHandler ) } @@ -122,16 +124,18 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger ) { + let callbackHandler = MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) self.init( mapView: mapView, viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: assetProvider, binaryMessenger: binaryMessenger, - callbackHandler: MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: String(format: "%lld", viewId) - ) + callbackHandler: callbackHandler, + tileProvider: callbackHandler ) } @@ -141,7 +145,8 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV creationParameters: PlatformMapViewCreationParams, assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger, - callbackHandler: MapEventDelegate + callbackHandler: MapEventDelegate, + tileProvider: TileProviderDelegate ) { self.mapView = mapView mapView.accessibilityElementsHidden = false @@ -190,8 +195,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV heatmapsController = HeatmapsController(mapView: mapView) tileOverlaysController = TileOverlaysController( mapView: mapView, - tileProvider: (callbackHandler as? MapsCallbackApi) - ?? MapsCallbackApi(binaryMessenger: binaryMessenger, messageChannelSuffix: pigeonSuffix) + tileProvider: tileProvider ) groundOverlaysController = GroundOverlaysController( mapView: mapView, diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift index 28d5c6378dee..e63025ef02f7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.swift @@ -203,7 +203,8 @@ class StubPluginRegistrar: NSObject, FlutterPluginRegistrar { creationParameters: emptyCreationParameters(), assetProvider: TestAssetProvider(), binaryMessenger: StubBinaryMessenger(), - callbackHandler: mapEventHandler + callbackHandler: mapEventHandler, + tileProvider: mapEventHandler ) async let poiTapReceived: Void = mapEventHandler.waitForPointOfInterestTap() diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift index c7ef526286cf..316b6b829bfd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/TestUtils/TestMapEventHandler.swift @@ -6,8 +6,8 @@ import Foundation @testable import google_maps_flutter_ios -/// Fake implementation of MapEventDelegate for unit tests. -class TestMapEventHandler: MapEventDelegate { +/// Fake implementation of MapEventDelegate (and TileProviderDelegate) for unit tests. +class TestMapEventHandler: MapEventDelegate, TileProviderDelegate { func didStartCameraMove() async throws {} func didMoveCamera(to cameraPositionArg: PlatformCameraPosition) async throws {} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift index 53557b1ab7b7..6e023617ce5f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.swift @@ -102,16 +102,18 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV } let binaryMessenger = registrar.messenger() + let callbackHandler = MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) self.init( mapView: GMSMapView(options: options), viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: DefaultAssetProvider(registrar: registrar), binaryMessenger: binaryMessenger, - callbackHandler: MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: String(format: "%lld", viewId) - ) + callbackHandler: callbackHandler, + tileProvider: callbackHandler ) } @@ -122,16 +124,18 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger ) { + let callbackHandler = MapsCallbackApi( + binaryMessenger: binaryMessenger, + messageChannelSuffix: String(format: "%lld", viewId) + ) self.init( mapView: mapView, viewIdentifier: viewId, creationParameters: creationParameters, assetProvider: assetProvider, binaryMessenger: binaryMessenger, - callbackHandler: MapsCallbackApi( - binaryMessenger: binaryMessenger, - messageChannelSuffix: String(format: "%lld", viewId) - ) + callbackHandler: callbackHandler, + tileProvider: callbackHandler ) } @@ -141,7 +145,8 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV creationParameters: PlatformMapViewCreationParams, assetProvider: AssetProvider, binaryMessenger: FlutterBinaryMessenger, - callbackHandler: MapEventDelegate + callbackHandler: MapEventDelegate, + tileProvider: TileProviderDelegate ) { self.mapView = mapView mapView.accessibilityElementsHidden = false @@ -190,8 +195,7 @@ public class GoogleMapController: NSObject, GMSMapViewDelegate, FlutterPlatformV heatmapsController = HeatmapsController(mapView: mapView) tileOverlaysController = TileOverlaysController( mapView: mapView, - tileProvider: (callbackHandler as? MapsCallbackApi) - ?? MapsCallbackApi(binaryMessenger: binaryMessenger, messageChannelSuffix: pigeonSuffix) + tileProvider: tileProvider ) groundOverlaysController = GroundOverlaysController( mapView: mapView, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart index 870972a9c83e..66584d0f1743 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_mouse_event_extension.dart @@ -18,5 +18,5 @@ import 'package:google_maps/google_maps.dart' as gmaps; extension PlaceIdExtension on gmaps.MapMouseEventOrIconMouseEvent { /// The place ID of a tapped point of interest, if this event is an icon /// mouse event. Otherwise `null`. - external String? placeId; + external String? get placeId; }