diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 3dcd5bd12f93..66c8c06ced8b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.18.0 + +* Adds support for tapping points of interest on the map. + ## 2.17.1 * Updates README to link to implementation packages for platform-specific diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index de3f13912cf7..dc330ed57fee 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -35,6 +35,7 @@ class _MapClickBodyState extends State<_MapClickBody> { GoogleMapController? mapController; LatLng? _lastTap; LatLng? _lastLongPress; + String? _lastPoiPlaceId; @override Widget build(BuildContext context) { @@ -51,6 +52,11 @@ class _MapClickBodyState extends State<_MapClickBody> { _lastLongPress = pos; }); }, + onPointOfInterestTap: (PointOfInterestId pointOfInterestId) { + setState(() { + _lastPoiPlaceId = pointOfInterestId.value; + }); + }, ); final columnChildren = [ @@ -73,6 +79,16 @@ class _MapClickBodyState extends State<_MapClickBody> { child: Text(_lastLongPress != null ? 'Long pressed' : '', textAlign: TextAlign.center), ), ); + final lastPoiTap = 'Point of interest place ID:\n${_lastPoiPlaceId ?? ""}\n'; + columnChildren.add(Center(child: Text(lastPoiTap, textAlign: TextAlign.center))); + columnChildren.add( + Center( + child: Text( + _lastPoiPlaceId != null ? 'Point of interest tapped' : '', + textAlign: TextAlign.center, + ), + ), + ); } return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren); } diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 931dd16a7211..37afda8be256 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -54,6 +54,7 @@ export 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf MinMaxZoomPreference, PatternItem, PinConfig, + PointOfInterestId, Polygon, PolygonId, Polyline, diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart index 32ed8a26671f..57a9c6a5b4d8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart @@ -59,6 +59,13 @@ class GoogleMapController { .listen((_) => _googleMapState.widget.onCameraIdle!()), ); } + _streamSubscriptions.add( + GoogleMapsFlutterPlatform.instance + .onPointOfInterestTap(mapId: mapId) + .listen( + (PointOfInterestTapEvent e) => _googleMapState.onPointOfInterestTap(e.value), + ), + ); _streamSubscriptions.add( GoogleMapsFlutterPlatform.instance .onMarkerTap(mapId: mapId) diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart index 3bd76b784571..d6d122812766 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart @@ -133,6 +133,7 @@ class GoogleMap extends StatefulWidget { this.onCameraIdle, this.onTap, this.onLongPress, + this.onPointOfInterestTap, this.markerType = GoogleMapMarkerType.marker, this.colorScheme, String? mapId, @@ -297,6 +298,11 @@ class GoogleMap extends StatefulWidget { /// Called every time a [GoogleMap] is long pressed. final ArgumentCallback? onLongPress; + /// Called when a point of interest on the map is tapped. + /// + /// May not be supported on all platforms. + final ArgumentCallback? onPointOfInterestTap; + /// True if a "My Location" layer should be shown on the map. /// /// This layer includes a location indicator at the current device location, @@ -685,6 +691,14 @@ class _GoogleMapState extends State { } } + void onPointOfInterestTap(PointOfInterestId pointOfInterestId) { + final ArgumentCallback? onPointOfInterestTap = + widget.onPointOfInterestTap; + if (onPointOfInterestTap != null) { + onPointOfInterestTap(pointOfInterestId); + } + } + void onTap(LatLng position) { final ArgumentCallback? onTap = widget.onTap; if (onTap != null) { diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 0342292634b8..31dd2343854e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.17.1 +version: 2.18.0 environment: sdk: ^3.10.0 diff --git a/packages/google_maps_flutter/google_maps_flutter/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter/test/fake_google_maps_flutter_platform.dart index fae706bdb4ed..ffb6303ee4d3 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/fake_google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/fake_google_maps_flutter_platform.dart @@ -227,6 +227,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/test/google_map_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart index 722a4390bb65..c5cbcd623ee8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart @@ -776,4 +776,29 @@ void main() { const GoogleMap(initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0))); }, returnsNormally); }); + + testWidgets('onPointOfInterestTap invokes callback', (WidgetTester tester) async { + final poiCompleter = Completer(); + final controllerCompleter = Completer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: GoogleMap( + initialCameraPosition: const CameraPosition(target: LatLng(0.0, 0.0)), + onMapCreated: controllerCompleter.complete, + onPointOfInterestTap: poiCompleter.complete, + ), + ), + ); + + await controllerCompleter.future; + final int mapId = platform.createdIds.first; + + platform.mapEventStreamController.add( + PointOfInterestTapEvent(mapId, const PointOfInterestId('place-123')), + ); + + expect(await poiCompleter.future, const PointOfInterestId('place-123')); + }); } diff --git a/packages/google_maps_flutter/google_maps_flutter/test/google_maps_flutter_export_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/google_maps_flutter_export_test.dart index decbeee5292c..c148561cd6ce 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/google_maps_flutter_export_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/google_maps_flutter_export_test.dart @@ -50,6 +50,7 @@ void main() { main_file.MinMaxZoomPreference; main_file.PatternItem; main_file.PinConfig; + main_file.PointOfInterestId; main_file.Polygon; main_file.PolygonId; main_file.Polyline; 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 c62c53095ef3..5da702010381 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.12 * Bumps the androidx group across 10 directories with 1 update. 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..05056d56a422 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 != null && 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 3d5d0190d6a1..e6d78829c242 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 @@ -4071,6 +4071,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..fad122f88af5 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,18 @@ 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 SetInitialHeatmaps() { GoogleMapController googleMapController = getGoogleMapControllerWithMockedDependencies(); 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 857e1aeb7337..f4b80671afc4 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 @@ -3436,6 +3436,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); @@ -3731,6 +3734,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 8d34180d23a9..7474c0db3346 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.12 +version: 2.20.0 environment: sdk: ^3.12.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..ed2af33a4199 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,22 @@ 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/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 37034b5bb674..27cf814895d7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.19.0 + +* Adds support for tapping points of interest on the map. + ## 2.18.3 * Updates README to include setup information. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m index ecda19a81474..7ba7ba7f8330 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m @@ -27,6 +27,35 @@ - (void)commit { @end +// Records messages sent through a FlutterBinaryMessenger. +@interface CapturingBinaryMessenger : NSObject +@property(nonatomic, copy, nullable) NSString *lastChannel; +@property(nonatomic, strong, nullable) NSData *lastMessage; +@end + +@implementation CapturingBinaryMessenger +- (void)sendOnChannel:(NSString *)channel message:(NSData *)message { + self.lastChannel = channel; + self.lastMessage = message; +} +- (void)sendOnChannel:(NSString *)channel + message:(NSData *)message + binaryReply:(FlutterBinaryReply)reply { + self.lastChannel = channel; + self.lastMessage = message; + if (reply) { + reply(nil); + } +} +- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection { +} +- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel + binaryMessageHandler: + (FlutterBinaryMessageHandler _Nullable)handler { + return 0; +} +@end + // No-op implementation of FlutterBinaryMessenger. @interface StubBinaryMessenger : NSObject @end @@ -189,6 +218,59 @@ - (void)testAnimateCameraWithUpdateAndDuration { [durationMilliseconds doubleValue] / 1000); } +- (void)testDidTapPOIForwardsPlaceId { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:@"place-123" + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertTrue([binaryMessenger.lastChannel containsString:@"onPointOfInterestTap"]); + NSObject *codec = FGMGetGoogleMapsFlutterPigeonMessagesCodec(); + NSArray *args = [codec decode:binaryMessenger.lastMessage]; + XCTAssertEqual(args.count, 1); + XCTAssertEqualObjects(args[0], @"place-123"); +} + +- (void)testDidTapPOINilPlaceIdDoesNotForward { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:nil + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertNil(binaryMessenger.lastChannel); + XCTAssertNil(binaryMessenger.lastMessage); +} + - (void)testInspectorAPICameraPosition { CGRect frame = CGRectMake(0, 0, 100, 100); GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/test/fake_google_maps_flutter_platform.dart index 3e8beefc78c4..f4bc1fd82c8f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/test/fake_google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/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/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/FGMGoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/FGMGoogleMapController.m index 5f609c9b00de..b5527b88870a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/FGMGoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/FGMGoogleMapController.m @@ -186,6 +186,12 @@ - (void)didTapCircleWithIdentifier:(NSString *)circleId { }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId { + [self.callbackHandler didTapPointOfInterestWithPlaceId:placeId + completion:^(FlutterError *_){ + }]; +} + - (void)didTapCluster:(FGMPlatformCluster *)cluster { [self.callbackHandler didTapCluster:cluster completion:^(FlutterError *_){ @@ -558,6 +564,15 @@ - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { } } +- (void)mapView:(GMSMapView *)mapView + didTapPOIWithPlaceID:(NSString *)placeID + name:(NSString *)name + location:(CLLocationCoordinate2D)location { + if (placeID != nil) { + [self.mapEventHandler didTapPointOfInterestWithPlaceId:placeID]; + } +} + - (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { [self.mapEventHandler didTapAtPosition:FGMGetPigeonLatLngForCoordinate(coordinate)]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m index 3302fd9e13b8..d14efbfba5d0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m @@ -3068,6 +3068,32 @@ - (void)didTapCircleWithIdentifier:(NSString *)arg_circleId } }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)arg_placeId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetGoogleMapsFlutterPigeonMessagesCodec()]; + [channel sendMessage:@[ arg_placeId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} - (void)didTapCluster:(FGMPlatformCluster *)arg_cluster completion:(void (^)(FlutterError *_Nullable))completion { NSString *channelName = [NSString diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h index f93f1adef252..0487246e0f34 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h @@ -30,6 +30,9 @@ NS_ASSUME_NONNULL_BEGIN /// Called when the map, not a specifc map object, is long pressed. - (void)didLongPressAtPosition:(FGMPlatformLatLng *)position; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId; + /// Called when a marker is tapped. - (void)didTapMarkerWithIdentifier:(NSString *)markerId; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h index 053f0f577650..e4c525f25a9f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h @@ -910,6 +910,9 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger /// Called when a circle is tapped. - (void)didTapCircleWithIdentifier:(NSString *)circleId completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId + completion:(void (^)(FlutterError *_Nullable))completion; /// Called when a marker cluster is tapped. - (void)didTapCluster:(FGMPlatformCluster *)cluster completion:(void (^)(FlutterError *_Nullable))completion; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index c0c8db027c8d..157bce4cbaf6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/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(); @@ -989,6 +994,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/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart index be134f058b12..db2d58fe4335 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart @@ -3355,6 +3355,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); @@ -3734,6 +3737,39 @@ 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 { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null.', + ); + final List args = (message as List?)!; + final String? arg_placeId = (args[0] as String?); + assert( + arg_placeId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null, expected non-null 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/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart index a04e857d05fc..1682bb4ef109 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart @@ -832,6 +832,10 @@ abstract class MapsCallbackApi { @ObjCSelector('didTapCircleWithIdentifier:') void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + @ObjCSelector('didTapPointOfInterestWithPlaceId:') + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. @ObjCSelector('didTapCluster:') void onClusterTap(PlatformCluster cluster); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index 46f21568a148..e8ba76189ab5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.18.3 +version: 2.19.0 environment: sdk: ^3.10.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart index da996d1b8e6e..a8b560a96ce7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart @@ -905,6 +905,22 @@ 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 = 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_sdk10/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/CHANGELOG.md index 4baa9e7a6eaa..fa356fc4d802 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.4 * Updates README to include setup information. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.m b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.m index efe09cb6ea33..958c0d1d4844 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/example/ios/RunnerTests/GoogleMapsTests.m @@ -27,6 +27,35 @@ - (void)commit { @end +// Records messages sent through a FlutterBinaryMessenger. +@interface CapturingBinaryMessenger : NSObject +@property(nonatomic, copy, nullable) NSString *lastChannel; +@property(nonatomic, strong, nullable) NSData *lastMessage; +@end + +@implementation CapturingBinaryMessenger +- (void)sendOnChannel:(NSString *)channel message:(NSData *)message { + self.lastChannel = channel; + self.lastMessage = message; +} +- (void)sendOnChannel:(NSString *)channel + message:(NSData *)message + binaryReply:(FlutterBinaryReply)reply { + self.lastChannel = channel; + self.lastMessage = message; + if (reply) { + reply(nil); + } +} +- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection { +} +- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel + binaryMessageHandler: + (FlutterBinaryMessageHandler _Nullable)handler { + return 0; +} +@end + // No-op implementation of FlutterBinaryMessenger. @interface StubBinaryMessenger : NSObject @end @@ -189,6 +218,59 @@ - (void)testAnimateCameraWithUpdateAndDuration { [durationMilliseconds doubleValue] / 1000); } +- (void)testDidTapPOIForwardsPlaceId { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:@"place-123" + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertTrue([binaryMessenger.lastChannel containsString:@"onPointOfInterestTap"]); + NSObject *codec = FGMGetGoogleMapsFlutterPigeonMessagesCodec(); + NSArray *args = [codec decode:binaryMessenger.lastMessage]; + XCTAssertEqual(args.count, 1); + XCTAssertEqualObjects(args[0], @"place-123"); +} + +- (void)testDidTapPOINilPlaceIdDoesNotForward { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:nil + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertNil(binaryMessenger.lastChannel); + XCTAssertNil(binaryMessenger.lastMessage); +} + - (void)testInspectorAPICameraPosition { CGRect frame = CGRectMake(0, 0, 100, 100); GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; 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/FGMGoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/FGMGoogleMapController.m index 5f609c9b00de..b5527b88870a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/FGMGoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/FGMGoogleMapController.m @@ -186,6 +186,12 @@ - (void)didTapCircleWithIdentifier:(NSString *)circleId { }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId { + [self.callbackHandler didTapPointOfInterestWithPlaceId:placeId + completion:^(FlutterError *_){ + }]; +} + - (void)didTapCluster:(FGMPlatformCluster *)cluster { [self.callbackHandler didTapCluster:cluster completion:^(FlutterError *_){ @@ -558,6 +564,15 @@ - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { } } +- (void)mapView:(GMSMapView *)mapView + didTapPOIWithPlaceID:(NSString *)placeID + name:(NSString *)name + location:(CLLocationCoordinate2D)location { + if (placeID != nil) { + [self.mapEventHandler didTapPointOfInterestWithPlaceId:placeID]; + } +} + - (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { [self.mapEventHandler didTapAtPosition:FGMGetPigeonLatLngForCoordinate(coordinate)]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.m index 3302fd9e13b8..d14efbfba5d0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.m @@ -3068,6 +3068,32 @@ - (void)didTapCircleWithIdentifier:(NSString *)arg_circleId } }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)arg_placeId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetGoogleMapsFlutterPigeonMessagesCodec()]; + [channel sendMessage:@[ arg_placeId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} - (void)didTapCluster:(FGMPlatformCluster *)arg_cluster completion:(void (^)(FlutterError *_Nullable))completion { NSString *channelName = [NSString diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/FGMMapEventDelegate.h b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/FGMMapEventDelegate.h index f93f1adef252..0487246e0f34 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/FGMMapEventDelegate.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/FGMMapEventDelegate.h @@ -30,6 +30,9 @@ NS_ASSUME_NONNULL_BEGIN /// Called when the map, not a specifc map object, is long pressed. - (void)didLongPressAtPosition:(FGMPlatformLatLng *)position; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId; + /// Called when a marker is tapped. - (void)didTapMarkerWithIdentifier:(NSString *)markerId; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.h index 053f0f577650..e4c525f25a9f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk10/ios/google_maps_flutter_ios_sdk10/Sources/google_maps_flutter_ios_sdk10/include/google_maps_flutter_ios_sdk10/google_maps_flutter_pigeon_messages.g.h @@ -910,6 +910,9 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger /// Called when a circle is tapped. - (void)didTapCircleWithIdentifier:(NSString *)circleId completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId + completion:(void (^)(FlutterError *_Nullable))completion; /// Called when a marker cluster is tapped. - (void)didTapCluster:(FGMPlatformCluster *)cluster completion:(void (^)(FlutterError *_Nullable))completion; 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 c0c8db027c8d..157bce4cbaf6 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(); @@ -989,6 +994,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 be134f058b12..db2d58fe4335 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 @@ -3355,6 +3355,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); @@ -3734,6 +3737,39 @@ 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 { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null.', + ); + final List args = (message as List?)!; + final String? arg_placeId = (args[0] as String?); + assert( + arg_placeId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null, expected non-null 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 51d82926642f..37eb13010c1f 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 @@ -832,6 +832,10 @@ abstract class MapsCallbackApi { @ObjCSelector('didTapCircleWithIdentifier:') void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + @ObjCSelector('didTapPointOfInterestWithPlaceId:') + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. @ObjCSelector('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 5ccd2b07d828..88d099f80a40 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.4 +version: 2.19.0 environment: sdk: ^3.10.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 da996d1b8e6e..a8b560a96ce7 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 @@ -905,6 +905,22 @@ 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 = 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 4baa9e7a6eaa..fa356fc4d802 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.4 * Updates README to include setup information. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.m b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.m index 3feab5b04da7..61544e919081 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/example/ios/RunnerTests/GoogleMapsTests.m @@ -27,6 +27,35 @@ - (void)commit { @end +// Records messages sent through a FlutterBinaryMessenger. +@interface CapturingBinaryMessenger : NSObject +@property(nonatomic, copy, nullable) NSString *lastChannel; +@property(nonatomic, strong, nullable) NSData *lastMessage; +@end + +@implementation CapturingBinaryMessenger +- (void)sendOnChannel:(NSString *)channel message:(NSData *)message { + self.lastChannel = channel; + self.lastMessage = message; +} +- (void)sendOnChannel:(NSString *)channel + message:(NSData *)message + binaryReply:(FlutterBinaryReply)reply { + self.lastChannel = channel; + self.lastMessage = message; + if (reply) { + reply(nil); + } +} +- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection { +} +- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel + binaryMessageHandler: + (FlutterBinaryMessageHandler _Nullable)handler { + return 0; +} +@end + // No-op implementation of FlutterBinaryMessenger. @interface StubBinaryMessenger : NSObject @end @@ -189,6 +218,59 @@ - (void)testAnimateCameraWithUpdateAndDuration { [durationMilliseconds doubleValue] / 1000); } +- (void)testDidTapPOIForwardsPlaceId { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:@"place-123" + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertTrue([binaryMessenger.lastChannel containsString:@"onPointOfInterestTap"]); + NSObject *codec = FGMGetGoogleMapsFlutterPigeonMessagesCodec(); + NSArray *args = [codec decode:binaryMessenger.lastMessage]; + XCTAssertEqual(args.count, 1); + XCTAssertEqualObjects(args[0], @"place-123"); +} + +- (void)testDidTapPOINilPlaceIdDoesNotForward { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:nil + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertNil(binaryMessenger.lastChannel); + XCTAssertNil(binaryMessenger.lastMessage); +} + - (void)testInspectorAPICameraPosition { CGRect frame = CGRectMake(0, 0, 100, 100); GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; 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/FGMGoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/FGMGoogleMapController.m index 5f609c9b00de..b5527b88870a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/FGMGoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/FGMGoogleMapController.m @@ -186,6 +186,12 @@ - (void)didTapCircleWithIdentifier:(NSString *)circleId { }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId { + [self.callbackHandler didTapPointOfInterestWithPlaceId:placeId + completion:^(FlutterError *_){ + }]; +} + - (void)didTapCluster:(FGMPlatformCluster *)cluster { [self.callbackHandler didTapCluster:cluster completion:^(FlutterError *_){ @@ -558,6 +564,15 @@ - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { } } +- (void)mapView:(GMSMapView *)mapView + didTapPOIWithPlaceID:(NSString *)placeID + name:(NSString *)name + location:(CLLocationCoordinate2D)location { + if (placeID != nil) { + [self.mapEventHandler didTapPointOfInterestWithPlaceId:placeID]; + } +} + - (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { [self.mapEventHandler didTapAtPosition:FGMGetPigeonLatLngForCoordinate(coordinate)]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.m index 3302fd9e13b8..d14efbfba5d0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.m @@ -3068,6 +3068,32 @@ - (void)didTapCircleWithIdentifier:(NSString *)arg_circleId } }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)arg_placeId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetGoogleMapsFlutterPigeonMessagesCodec()]; + [channel sendMessage:@[ arg_placeId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} - (void)didTapCluster:(FGMPlatformCluster *)arg_cluster completion:(void (^)(FlutterError *_Nullable))completion { NSString *channelName = [NSString diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/FGMMapEventDelegate.h b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/FGMMapEventDelegate.h index f93f1adef252..0487246e0f34 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/FGMMapEventDelegate.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/FGMMapEventDelegate.h @@ -30,6 +30,9 @@ NS_ASSUME_NONNULL_BEGIN /// Called when the map, not a specifc map object, is long pressed. - (void)didLongPressAtPosition:(FGMPlatformLatLng *)position; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId; + /// Called when a marker is tapped. - (void)didTapMarkerWithIdentifier:(NSString *)markerId; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.h index 053f0f577650..e4c525f25a9f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios_sdk9/ios/google_maps_flutter_ios_sdk9/Sources/google_maps_flutter_ios_sdk9/include/google_maps_flutter_ios_sdk9/google_maps_flutter_pigeon_messages.g.h @@ -910,6 +910,9 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger /// Called when a circle is tapped. - (void)didTapCircleWithIdentifier:(NSString *)circleId completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId + completion:(void (^)(FlutterError *_Nullable))completion; /// Called when a marker cluster is tapped. - (void)didTapCluster:(FGMPlatformCluster *)cluster completion:(void (^)(FlutterError *_Nullable))completion; 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 c0c8db027c8d..157bce4cbaf6 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(); @@ -989,6 +994,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 be134f058b12..db2d58fe4335 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 @@ -3355,6 +3355,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); @@ -3734,6 +3737,39 @@ 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 { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null.', + ); + final List args = (message as List?)!; + final String? arg_placeId = (args[0] as String?); + assert( + arg_placeId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null, expected non-null 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 3c69cc451040..d613d4780b90 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 @@ -832,6 +832,10 @@ abstract class MapsCallbackApi { @ObjCSelector('didTapCircleWithIdentifier:') void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + @ObjCSelector('didTapPointOfInterestWithPlaceId:') + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. @ObjCSelector('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 224c6615c645..a8eea2740140 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.4 +version: 2.19.0 environment: sdk: ^3.10.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 da996d1b8e6e..a8b560a96ce7 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 @@ -905,6 +905,22 @@ 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 = 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.m b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.m index ecda19a81474..7ba7ba7f8330 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/example/ios/RunnerTests/GoogleMapsTests.m @@ -27,6 +27,35 @@ - (void)commit { @end +// Records messages sent through a FlutterBinaryMessenger. +@interface CapturingBinaryMessenger : NSObject +@property(nonatomic, copy, nullable) NSString *lastChannel; +@property(nonatomic, strong, nullable) NSData *lastMessage; +@end + +@implementation CapturingBinaryMessenger +- (void)sendOnChannel:(NSString *)channel message:(NSData *)message { + self.lastChannel = channel; + self.lastMessage = message; +} +- (void)sendOnChannel:(NSString *)channel + message:(NSData *)message + binaryReply:(FlutterBinaryReply)reply { + self.lastChannel = channel; + self.lastMessage = message; + if (reply) { + reply(nil); + } +} +- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection { +} +- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel + binaryMessageHandler: + (FlutterBinaryMessageHandler _Nullable)handler { + return 0; +} +@end + // No-op implementation of FlutterBinaryMessenger. @interface StubBinaryMessenger : NSObject @end @@ -189,6 +218,59 @@ - (void)testAnimateCameraWithUpdateAndDuration { [durationMilliseconds doubleValue] / 1000); } +- (void)testDidTapPOIForwardsPlaceId { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:@"place-123" + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertTrue([binaryMessenger.lastChannel containsString:@"onPointOfInterestTap"]); + NSObject *codec = FGMGetGoogleMapsFlutterPigeonMessagesCodec(); + NSArray *args = [codec decode:binaryMessenger.lastMessage]; + XCTAssertEqual(args.count, 1); + XCTAssertEqualObjects(args[0], @"place-123"); +} + +- (void)testDidTapPOINilPlaceIdDoesNotForward { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + CapturingBinaryMessenger *binaryMessenger = [[CapturingBinaryMessenger alloc] init]; + FGMGoogleMapController *controller = + [[FGMGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:binaryMessenger]; + + [controller mapView:mapView + didTapPOIWithPlaceID:nil + name:@"Test Place" + location:CLLocationCoordinate2DMake(0, 0)]; + + XCTAssertNil(binaryMessenger.lastChannel); + XCTAssertNil(binaryMessenger.lastMessage); +} + - (void)testInspectorAPICameraPosition { CGRect frame = CGRectMake(0, 0, 100, 100); GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; 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/FGMGoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/FGMGoogleMapController.m index 5f609c9b00de..b5527b88870a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/FGMGoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/FGMGoogleMapController.m @@ -186,6 +186,12 @@ - (void)didTapCircleWithIdentifier:(NSString *)circleId { }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId { + [self.callbackHandler didTapPointOfInterestWithPlaceId:placeId + completion:^(FlutterError *_){ + }]; +} + - (void)didTapCluster:(FGMPlatformCluster *)cluster { [self.callbackHandler didTapCluster:cluster completion:^(FlutterError *_){ @@ -558,6 +564,15 @@ - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { } } +- (void)mapView:(GMSMapView *)mapView + didTapPOIWithPlaceID:(NSString *)placeID + name:(NSString *)name + location:(CLLocationCoordinate2D)location { + if (placeID != nil) { + [self.mapEventHandler didTapPointOfInterestWithPlaceId:placeID]; + } +} + - (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { [self.mapEventHandler didTapAtPosition:FGMGetPigeonLatLngForCoordinate(coordinate)]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m index 3302fd9e13b8..d14efbfba5d0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.m @@ -3068,6 +3068,32 @@ - (void)didTapCircleWithIdentifier:(NSString *)arg_circleId } }]; } +- (void)didTapPointOfInterestWithPlaceId:(NSString *)arg_placeId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetGoogleMapsFlutterPigeonMessagesCodec()]; + [channel sendMessage:@[ arg_placeId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} - (void)didTapCluster:(FGMPlatformCluster *)arg_cluster completion:(void (^)(FlutterError *_Nullable))completion { NSString *channelName = [NSString diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h index f93f1adef252..0487246e0f34 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/FGMMapEventDelegate.h @@ -30,6 +30,9 @@ NS_ASSUME_NONNULL_BEGIN /// Called when the map, not a specifc map object, is long pressed. - (void)didLongPressAtPosition:(FGMPlatformLatLng *)position; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId; + /// Called when a marker is tapped. - (void)didTapMarkerWithIdentifier:(NSString *)markerId; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h index 053f0f577650..e4c525f25a9f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios_shared_code/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/include/google_maps_flutter_ios/google_maps_flutter_pigeon_messages.g.h @@ -910,6 +910,9 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger /// Called when a circle is tapped. - (void)didTapCircleWithIdentifier:(NSString *)circleId completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterestWithPlaceId:(NSString *)placeId + completion:(void (^)(FlutterError *_Nullable))completion; /// Called when a marker cluster is tapped. - (void)didTapCluster:(FGMPlatformCluster *)cluster completion:(void (^)(FlutterError *_Nullable))completion; 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 c0c8db027c8d..157bce4cbaf6 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(); @@ -989,6 +994,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 be134f058b12..db2d58fe4335 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 @@ -3355,6 +3355,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); @@ -3734,6 +3737,39 @@ 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 { + assert( + message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null.', + ); + final List args = (message as List?)!; + final String? arg_placeId = (args[0] as String?); + assert( + arg_placeId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPointOfInterestTap was null, expected non-null 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 a04e857d05fc..1682bb4ef109 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 @@ -832,6 +832,10 @@ abstract class MapsCallbackApi { @ObjCSelector('didTapCircleWithIdentifier:') void onCircleTap(String circleId); + /// Called when a point of interest is tapped. + @ObjCSelector('didTapPointOfInterestWithPlaceId:') + void onPointOfInterestTap(String placeId); + /// Called when a marker cluster is tapped. @ObjCSelector('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 da996d1b8e6e..a8b560a96ce7 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 @@ -905,6 +905,22 @@ 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 = 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_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md index 1c2d3c2fb1ca..3eb15c4cda38 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.16.0 +* Adds support for tapping points of interest on the map. * Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. ## 2.15.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart index 0d3f47cde019..60d11d177ebc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart @@ -160,6 +160,15 @@ class GroundOverlayTapEvent extends MapEvent { GroundOverlayTapEvent(super.mapId, super.croundOverlayId); } +/// An event fired when a point of interest is tapped. +class PointOfInterestTapEvent extends MapEvent { + /// Build a PointOfInterestTap Event triggered from the map represented by `mapId`. + /// + /// The `value` of this event is a [PointOfInterestId] object that represents the + /// tapped point of interest. + PointOfInterestTapEvent(super.mapId, super.pointOfInterestId); +} + /// An event fired when a Map is tapped. class MapTapEvent extends _PositionedMapEvent { /// Build an MapTap Event triggered from the map represented by `mapId`. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart index 19ec79818ab9..86b9cb904f14 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart @@ -155,6 +155,11 @@ class MethodChannelGoogleMapsFlutter 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(); @@ -233,6 +238,14 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { _mapEventStreamController.add( CircleTapEvent(mapId, CircleId(arguments['circleId']! as String)), ); + case 'map#onPointOfInterestTap': + final Map arguments = _getArgumentDictionary(call); + _mapEventStreamController.add( + PointOfInterestTapEvent( + mapId, + PointOfInterestId(arguments['placeId']! as String), + ), + ); case 'map#onTap': final Map arguments = _getArgumentDictionary(call); _mapEventStreamController.add(MapTapEvent(mapId, LatLng.fromJson(arguments['position'])!)); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart index 4ca64f66a8c6..2e8b29fdc238 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart @@ -337,6 +337,11 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('onCircleTap() has not been implemented.'); } + /// A point of interest has been tapped. + Stream onPointOfInterestTap({required int mapId}) { + return const Stream.empty(); + } + /// A Map has been tapped at a certain [LatLng]. Stream onTap({required int mapId}) { throw UnimplementedError('onTap() has not been implemented.'); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/point_of_interest_id.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/point_of_interest_id.dart new file mode 100644 index 000000000000..419cd436b609 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/point_of_interest_id.dart @@ -0,0 +1,36 @@ +// 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. + +import 'package:flutter/foundation.dart' show immutable, objectRuntimeType; + +/// Uniquely identifies a point of interest on a [GoogleMap]. +/// +/// The [value] is the Google Maps place ID for the tapped point of interest. +@immutable +class PointOfInterestId { + /// Creates an immutable identifier for a point of interest. + const PointOfInterestId(this.value); + + /// The Google Maps place ID for the point of interest. + final String value; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is PointOfInterestId && value == other.value; + } + + @override + int get hashCode => value.hashCode; + + @override + String toString() { + return '${objectRuntimeType(this, 'PointOfInterestId')}($value)'; + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart index 80e380674337..673a8406ae6a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart @@ -28,6 +28,7 @@ export 'maps_object_updates.dart'; export 'marker.dart'; export 'marker_updates.dart'; export 'pattern_item.dart'; +export 'point_of_interest_id.dart'; export 'polygon.dart'; export 'polygon_updates.dart'; export 'polyline.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index 226c23f7e39b..deb836ce66b5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/google_maps_f issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.15.0 +version: 2.16.0 environment: sdk: ^3.10.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart index d181508fa569..51b6a2f01940 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart @@ -119,5 +119,22 @@ void main() { expect((await markerDragStream.next).value.value, equals('drag-marker')); expect((await markerDragEndStream.next).value.value, equals('drag-end-marker')); }); + + test('point of interest tap sends event to correct stream', () async { + const mapId = 1; + const placeId = 'place-123'; + final jsonEvent = {'placeId': placeId}; + + final maps = MethodChannelGoogleMapsFlutter(); + maps.ensureChannelInitialized(mapId); + + final stream = StreamQueue( + maps.onPointOfInterestTap(mapId: mapId), + ); + + await sendPlatformMessage(mapId, 'map#onPointOfInterestTap', jsonEvent); + + expect((await stream.next).value.value, equals(placeId)); + }); }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart index 2254a30780a9..446e1b88eabf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart @@ -95,6 +95,12 @@ void main() { ); }); + test('onPointOfInterestTap() returns empty stream', () async { + final Stream stream = BuildViewGoogleMapsFlutterPlatform() + .onPointOfInterestTap(mapId: 0); + expect(await stream.isEmpty, isTrue); + }); + test('default implementation of `getStyleError` returns null', () async { final GoogleMapsFlutterPlatform platform = BuildViewGoogleMapsFlutterPlatform(); expect(await platform.getStyleError(mapId: 0), null); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/point_of_interest_id_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/point_of_interest_id_test.dart new file mode 100644 index 000000000000..082ff61877c5 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/point_of_interest_id_test.dart @@ -0,0 +1,23 @@ +// 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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + test('PointOfInterestId equality', () { + const id1 = PointOfInterestId('place-123'); + const id2 = PointOfInterestId('place-123'); + const id3 = PointOfInterestId('place-456'); + + expect(id1, equals(id2)); + expect(id1, isNot(equals(id3))); + expect(id1.hashCode, equals(id2.hashCode)); + }); + + test('PointOfInterestId toString', () { + const id = PointOfInterestId('place-123'); + expect(id.toString(), contains('place-123')); + }); +} 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 c8657c54f8d8..753393eb59e3 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.3 + +* Adds support for tapping points of interest on the map. + ## 0.6.2+3 * Updates README to include setup information. 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 bf1a77889f2b..87471f1add1a 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,37 @@ 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..3c4fc7658fe8 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,18 @@ 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/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index 0413faaf104d..726a0908c839 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 @@ -7,6 +7,7 @@ library google_maps_flutter_web; import 'dart:async'; import 'dart:convert'; import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'dart:ui_web' as ui_web; import 'package:collection/collection.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..db56ff658ed1 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,15 @@ class GoogleMapController { _onClickSubscription = map.onClick.listen((gmaps.MapMouseEventOrIconMouseEvent event) { assert(event.latLng != null); if (!_streamController.isClosed) { + if (event.hasProperty('placeId'.toJS).toDart) { + final String? placeId = (event as gmaps.IconMouseEvent).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/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 9a19bffc4e88..c932c08469bd 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.2+3 +version: 0.6.3 environment: sdk: ^3.10.0