diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b2c1be73c130..6579ae788e94 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.15.0 + +* Adds `onPoiTap` support to the `GoogleMap` widget to handle taps on base map landmarks and businesses. +* Adds a "Place POI" example to the example app. + ## 2.14.1 * Replaces internal use of deprecated methods. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index 2cee263eeba3..d47efa87a2bc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -23,6 +23,7 @@ import 'padding.dart'; import 'page.dart'; import 'place_circle.dart'; import 'place_marker.dart'; +import 'place_poi.dart'; import 'place_polygon.dart'; import 'place_polyline.dart'; import 'scrolling_map.dart'; @@ -41,6 +42,7 @@ final List _allPages = [ const PlacePolylinePage(), const PlacePolygonPage(), const PlaceCirclePage(), + const PlacePoiPage(), const PaddingPage(), const SnapshotPage(), const LiteModePage(), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_poi.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_poi.dart new file mode 100644 index 000000000000..89cf8cc5e6e9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_poi.dart @@ -0,0 +1,95 @@ +// 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/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +import 'page.dart'; + +/// Page for demonstrating Point of Interest (POI) tapping. +class PlacePoiPage extends GoogleMapExampleAppPage { + /// Default constructor. + const PlacePoiPage({super.key}) + : super(const Icon(Icons.business), 'Place POI'); + + @override + Widget build(BuildContext context) { + return const PlacePoiBody(); + } +} + +/// Body of the POI page. +class PlacePoiBody extends StatefulWidget { + /// Default constructor. + const PlacePoiBody({super.key}); + + @override + State createState() => PlacePoiBodyState(); +} + +/// State for [PlacePoiBody]. +class PlacePoiBodyState extends State { + /// The controller for the map. + /// + /// This is public to match the example pattern, but marked with a doc comment. + GoogleMapController? controller; + PointOfInterest? _lastPoi; + + final CameraPosition _kKolkata = const CameraPosition( + target: LatLng(22.54222641620606, 88.34560669761545), + zoom: 16.0, + ); + + // ignore: use_setters_to_change_properties + void _onMapCreated(GoogleMapController controller) { + this.controller = controller; + } + + void _onPoiTap(PointOfInterest poi) { + setState(() { + _lastPoi = poi; + }); + + controller?.animateCamera(CameraUpdate.newLatLng(poi.position)); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + child: GoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: _kKolkata, + onPoiTap: _onPoiTap, + myLocationButtonEnabled: false, + ), + ), + Container( + color: Colors.white, + width: double.infinity, + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Last Tapped POI:', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox(height: 8), + if (_lastPoi != null) ...[ + Text('Place ID: ${_lastPoi!.placeId}'), + Text( + 'Lat/Lng: ${_lastPoi!.position.latitude.toStringAsFixed(5)}, ${_lastPoi!.position.longitude.toStringAsFixed(5)}', + ), + ] else + const Text('Tap on a business or landmark icon...'), + ], + ), + ), + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml index 0e002e4f535a..1ab05458f51f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -18,8 +18,8 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - google_maps_flutter_android: ^2.16.1 - google_maps_flutter_platform_interface: ^2.14.0 + google_maps_flutter_android: ^2.19.0 + google_maps_flutter_platform_interface: ^2.14.1 dev_dependencies: build_runner: ^2.1.10 @@ -33,3 +33,13 @@ flutter: uses-material-design: true assets: - assets/ + +dependency_overrides: + google_maps_flutter_android: + path: ../../google_maps_flutter_android + google_maps_flutter_ios: + path: ../../google_maps_flutter_ios + google_maps_flutter_platform_interface: + path: ../../google_maps_flutter_platform_interface + google_maps_flutter_web: + path: ../../google_maps_flutter_web 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 7ca650dceb75..470469d73a68 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 @@ -47,6 +47,7 @@ export 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf MarkerId, MinMaxZoomPreference, PatternItem, + PointOfInterest, 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 80f019d5a006..64aaec30660e 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 @@ -99,6 +99,11 @@ class GoogleMapController { (InfoWindowTapEvent e) => _googleMapState.onInfoWindowTap(e.value), ), ); + _streamSubscriptions.add( + GoogleMapsFlutterPlatform.instance + .onPoiTap(mapId: mapId) + .listen((MapPoiTapEvent e) => _googleMapState.onPoiTap(e.value)), + ); _streamSubscriptions.add( GoogleMapsFlutterPlatform.instance .onPolylineTap(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 627efae290de..458f802e770a 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.onPoiTap, this.cloudMapId, }); @@ -290,6 +291,9 @@ class GoogleMap extends StatefulWidget { /// Called every time a [GoogleMap] is long pressed. final ArgumentCallback? onLongPress; + /// Called when a [PointOfInterest] is tapped. + final ArgumentCallback? onPoiTap; + /// True if a "My Location" layer should be shown on the map. /// /// This layer includes a location indicator at the current device location, @@ -612,6 +616,10 @@ class _GoogleMapState extends State { } } + void onPoiTap(PointOfInterest poi) { + widget.onPoiTap?.call(poi); + } + void onPolygonTap(PolygonId polygonId) { final Polygon? polygon = _polygons[polygonId]; if (polygon == null) { diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index d928fb3b21f5..0a3dff7ffea3 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.14.1 +version: 2.15.0 environment: sdk: ^3.8.0 @@ -21,10 +21,10 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_android: ^2.16.1 - google_maps_flutter_ios: ^2.15.4 - google_maps_flutter_platform_interface: ^2.14.0 - google_maps_flutter_web: ^0.5.14 + google_maps_flutter_android: ^2.19.0 + google_maps_flutter_ios: ^2.18.0 + google_maps_flutter_platform_interface: ^2.15.0 + google_maps_flutter_web: ^0.5.15 dev_dependencies: flutter_test: @@ -41,3 +41,13 @@ topics: # The example deliberately includes limited-use secrets. false_secrets: - /example/web/index.html + +dependency_overrides: + google_maps_flutter_android: + path: ../google_maps_flutter_android + google_maps_flutter_ios: + path: ../google_maps_flutter_ios + google_maps_flutter_platform_interface: + path: ../google_maps_flutter_platform_interface + google_maps_flutter_web: + path: ../google_maps_flutter_web 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 fe0439d6c69d..e1c8b29b46a6 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 @@ -280,6 +280,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onPoiTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override Stream onLongPress({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 4aedfdbb78f8..4dec896132ca 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 @@ -659,4 +659,39 @@ void main() { expect(map.tileOverlaySets.length, 1); }); + + testWidgets('onPoiTap callback is called', (WidgetTester tester) async { + PointOfInterest? tappedPoi; + final mapCreatedCompleter = Completer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: GoogleMap( + initialCameraPosition: const CameraPosition(target: LatLng(0, 0)), + onPoiTap: (PointOfInterest poi) => tappedPoi = poi, + onMapCreated: (_) => mapCreatedCompleter.complete(), + ), + ), + ); + + // Wait for the map to be fully initialized in the fake platform. + await mapCreatedCompleter.future; + + // Use the top-level 'platform' variable instead of redeclaring it. + const poi = PointOfInterest( + position: LatLng(10.0, 10.0), + placeId: 'test_id_123', + ); + + // Inject the event into the fake platform's stream. + // mapId 0 is used as it's the first map created in the test. + platform.mapEventStreamController.add(MapPoiTapEvent(0, poi)); + + // Allow the stream and callback to process. + await tester.pump(); + + expect(tappedPoi, poi); + expect(tappedPoi!.placeId, 'test_id_123'); + }); } 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 21b41d813f9b..ad76f7545c97 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.19.0 + +* Implements `onPoiTap` support for Android using `OnPoiClickListener`. + ## 2.18.12 * Bumps com.google.maps.android:android-maps-utils from 3.20.1 to 4.0.0. 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 bd87c923ccea..d52546981c3c 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 @@ -37,6 +37,7 @@ import com.google.android.gms.maps.model.LatLngBounds; 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; @@ -67,6 +68,7 @@ class GoogleMapController MapsApi, MapsInspectorApi, OnMapReadyCallback, + GoogleMap.OnPoiClickListener, PlatformView { private static final String TAG = "GoogleMapController"; @@ -200,6 +202,7 @@ public void onMapReady(@NonNull GoogleMap googleMap) { this.googleMap.setIndoorEnabled(this.indoorEnabled); this.googleMap.setTrafficEnabled(this.trafficEnabled); this.googleMap.setBuildingsEnabled(this.buildingsEnabled); + this.googleMap.setOnPoiClickListener(this); installInvalidator(); if (mapReadyResult != null) { mapReadyResult.success(); @@ -363,6 +366,17 @@ public void onMarkerDragEnd(Marker marker) { markersController.onMarkerDragEnd(marker.getId(), marker.getPosition()); } + @Override + public void onPoiClick(PointOfInterest poi) { + Messages.PlatformPointOfInterest platformPoi = + new Messages.PlatformPointOfInterest.Builder() + .setPosition(Convert.latLngToPigeon(poi.latLng)) + .setPlaceId(poi.placeId) + .build(); + + flutterApi.onPoiTap(platformPoi, new NoOpVoidResult()); + } + @Override public void onPolygonClick(Polygon polygon) { polygonsController.onPolygonTap(polygon.getId()); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java index f8d44e8c4bf9..77282fd0e0cb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v26.1.5), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.googlemaps; @@ -2510,6 +2510,102 @@ ArrayList toList() { } } + /** + * Pigeon equivalent of the Point of Interest class. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformPointOfInterest { + private @NonNull PlatformLatLng position; + + public @NonNull PlatformLatLng getPosition() { + return position; + } + + public void setPosition(@NonNull PlatformLatLng setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"position\" is null."); + } + this.position = setterArg; + } + + private @NonNull String placeId; + + public @NonNull String getPlaceId() { + return placeId; + } + + public void setPlaceId(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"placeId\" is null."); + } + this.placeId = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformPointOfInterest() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PlatformPointOfInterest that = (PlatformPointOfInterest) o; + return position.equals(that.position) && placeId.equals(that.placeId); + } + + @Override + public int hashCode() { + return Objects.hash(position, placeId); + } + + public static final class Builder { + + private @Nullable PlatformLatLng position; + + @CanIgnoreReturnValue + public @NonNull Builder setPosition(@NonNull PlatformLatLng setterArg) { + this.position = setterArg; + return this; + } + + private @Nullable String placeId; + + @CanIgnoreReturnValue + public @NonNull Builder setPlaceId(@NonNull String setterArg) { + this.placeId = setterArg; + return this; + } + + public @NonNull PlatformPointOfInterest build() { + PlatformPointOfInterest pigeonReturn = new PlatformPointOfInterest(); + pigeonReturn.setPosition(position); + pigeonReturn.setPlaceId(placeId); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(position); + toListResult.add(placeId); + return toListResult; + } + + static @NonNull PlatformPointOfInterest fromList(@NonNull ArrayList pigeonVar_list) { + PlatformPointOfInterest pigeonResult = new PlatformPointOfInterest(); + Object position = pigeonVar_list.get(0); + pigeonResult.setPosition((PlatformLatLng) position); + Object placeId = pigeonVar_list.get(1); + pigeonResult.setPlaceId((String) placeId); + return pigeonResult; + } + } + /** * Pigeon equivalent of the Polygon class. * @@ -6700,52 +6796,54 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { case (byte) 153: return PlatformMarker.fromList((ArrayList) readValue(buffer)); case (byte) 154: - return PlatformPolygon.fromList((ArrayList) readValue(buffer)); + return PlatformPointOfInterest.fromList((ArrayList) readValue(buffer)); case (byte) 155: - return PlatformPolyline.fromList((ArrayList) readValue(buffer)); + return PlatformPolygon.fromList((ArrayList) readValue(buffer)); case (byte) 156: - return PlatformCap.fromList((ArrayList) readValue(buffer)); + return PlatformPolyline.fromList((ArrayList) readValue(buffer)); case (byte) 157: - return PlatformPatternItem.fromList((ArrayList) readValue(buffer)); + return PlatformCap.fromList((ArrayList) readValue(buffer)); case (byte) 158: - return PlatformTile.fromList((ArrayList) readValue(buffer)); + return PlatformPatternItem.fromList((ArrayList) readValue(buffer)); case (byte) 159: - return PlatformTileOverlay.fromList((ArrayList) readValue(buffer)); + return PlatformTile.fromList((ArrayList) readValue(buffer)); case (byte) 160: - return PlatformEdgeInsets.fromList((ArrayList) readValue(buffer)); + return PlatformTileOverlay.fromList((ArrayList) readValue(buffer)); case (byte) 161: - return PlatformLatLng.fromList((ArrayList) readValue(buffer)); + return PlatformEdgeInsets.fromList((ArrayList) readValue(buffer)); case (byte) 162: - return PlatformLatLngBounds.fromList((ArrayList) readValue(buffer)); + return PlatformLatLng.fromList((ArrayList) readValue(buffer)); case (byte) 163: - return PlatformCluster.fromList((ArrayList) readValue(buffer)); + return PlatformLatLngBounds.fromList((ArrayList) readValue(buffer)); case (byte) 164: - return PlatformGroundOverlay.fromList((ArrayList) readValue(buffer)); + return PlatformCluster.fromList((ArrayList) readValue(buffer)); case (byte) 165: - return PlatformCameraTargetBounds.fromList((ArrayList) readValue(buffer)); + return PlatformGroundOverlay.fromList((ArrayList) readValue(buffer)); case (byte) 166: - return PlatformMapViewCreationParams.fromList((ArrayList) readValue(buffer)); + return PlatformCameraTargetBounds.fromList((ArrayList) readValue(buffer)); case (byte) 167: - return PlatformMapConfiguration.fromList((ArrayList) readValue(buffer)); + return PlatformMapViewCreationParams.fromList((ArrayList) readValue(buffer)); case (byte) 168: - return PlatformPoint.fromList((ArrayList) readValue(buffer)); + return PlatformMapConfiguration.fromList((ArrayList) readValue(buffer)); case (byte) 169: - return PlatformTileLayer.fromList((ArrayList) readValue(buffer)); + return PlatformPoint.fromList((ArrayList) readValue(buffer)); case (byte) 170: - return PlatformZoomRange.fromList((ArrayList) readValue(buffer)); + return PlatformTileLayer.fromList((ArrayList) readValue(buffer)); case (byte) 171: - return PlatformBitmap.fromList((ArrayList) readValue(buffer)); + return PlatformZoomRange.fromList((ArrayList) readValue(buffer)); case (byte) 172: - return PlatformBitmapDefaultMarker.fromList((ArrayList) readValue(buffer)); + return PlatformBitmap.fromList((ArrayList) readValue(buffer)); case (byte) 173: - return PlatformBitmapBytes.fromList((ArrayList) readValue(buffer)); + return PlatformBitmapDefaultMarker.fromList((ArrayList) readValue(buffer)); case (byte) 174: - return PlatformBitmapAsset.fromList((ArrayList) readValue(buffer)); + return PlatformBitmapBytes.fromList((ArrayList) readValue(buffer)); case (byte) 175: - return PlatformBitmapAssetImage.fromList((ArrayList) readValue(buffer)); + return PlatformBitmapAsset.fromList((ArrayList) readValue(buffer)); case (byte) 176: - return PlatformBitmapAssetMap.fromList((ArrayList) readValue(buffer)); + return PlatformBitmapAssetImage.fromList((ArrayList) readValue(buffer)); case (byte) 177: + return PlatformBitmapAssetMap.fromList((ArrayList) readValue(buffer)); + case (byte) 178: return PlatformBitmapBytesMap.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); @@ -6829,77 +6927,80 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } else if (value instanceof PlatformMarker) { stream.write(153); writeValue(stream, ((PlatformMarker) value).toList()); - } else if (value instanceof PlatformPolygon) { + } else if (value instanceof PlatformPointOfInterest) { stream.write(154); + writeValue(stream, ((PlatformPointOfInterest) value).toList()); + } else if (value instanceof PlatformPolygon) { + stream.write(155); writeValue(stream, ((PlatformPolygon) value).toList()); } else if (value instanceof PlatformPolyline) { - stream.write(155); + stream.write(156); writeValue(stream, ((PlatformPolyline) value).toList()); } else if (value instanceof PlatformCap) { - stream.write(156); + stream.write(157); writeValue(stream, ((PlatformCap) value).toList()); } else if (value instanceof PlatformPatternItem) { - stream.write(157); + stream.write(158); writeValue(stream, ((PlatformPatternItem) value).toList()); } else if (value instanceof PlatformTile) { - stream.write(158); + stream.write(159); writeValue(stream, ((PlatformTile) value).toList()); } else if (value instanceof PlatformTileOverlay) { - stream.write(159); + stream.write(160); writeValue(stream, ((PlatformTileOverlay) value).toList()); } else if (value instanceof PlatformEdgeInsets) { - stream.write(160); + stream.write(161); writeValue(stream, ((PlatformEdgeInsets) value).toList()); } else if (value instanceof PlatformLatLng) { - stream.write(161); + stream.write(162); writeValue(stream, ((PlatformLatLng) value).toList()); } else if (value instanceof PlatformLatLngBounds) { - stream.write(162); + stream.write(163); writeValue(stream, ((PlatformLatLngBounds) value).toList()); } else if (value instanceof PlatformCluster) { - stream.write(163); + stream.write(164); writeValue(stream, ((PlatformCluster) value).toList()); } else if (value instanceof PlatformGroundOverlay) { - stream.write(164); + stream.write(165); writeValue(stream, ((PlatformGroundOverlay) value).toList()); } else if (value instanceof PlatformCameraTargetBounds) { - stream.write(165); + stream.write(166); writeValue(stream, ((PlatformCameraTargetBounds) value).toList()); } else if (value instanceof PlatformMapViewCreationParams) { - stream.write(166); + stream.write(167); writeValue(stream, ((PlatformMapViewCreationParams) value).toList()); } else if (value instanceof PlatformMapConfiguration) { - stream.write(167); + stream.write(168); writeValue(stream, ((PlatformMapConfiguration) value).toList()); } else if (value instanceof PlatformPoint) { - stream.write(168); + stream.write(169); writeValue(stream, ((PlatformPoint) value).toList()); } else if (value instanceof PlatformTileLayer) { - stream.write(169); + stream.write(170); writeValue(stream, ((PlatformTileLayer) value).toList()); } else if (value instanceof PlatformZoomRange) { - stream.write(170); + stream.write(171); writeValue(stream, ((PlatformZoomRange) value).toList()); } else if (value instanceof PlatformBitmap) { - stream.write(171); + stream.write(172); writeValue(stream, ((PlatformBitmap) value).toList()); } else if (value instanceof PlatformBitmapDefaultMarker) { - stream.write(172); + stream.write(173); writeValue(stream, ((PlatformBitmapDefaultMarker) value).toList()); } else if (value instanceof PlatformBitmapBytes) { - stream.write(173); + stream.write(174); writeValue(stream, ((PlatformBitmapBytes) value).toList()); } else if (value instanceof PlatformBitmapAsset) { - stream.write(174); + stream.write(175); writeValue(stream, ((PlatformBitmapAsset) value).toList()); } else if (value instanceof PlatformBitmapAssetImage) { - stream.write(175); + stream.write(176); writeValue(stream, ((PlatformBitmapAssetImage) value).toList()); } else if (value instanceof PlatformBitmapAssetMap) { - stream.write(176); + stream.write(177); writeValue(stream, ((PlatformBitmapAssetMap) value).toList()); } else if (value instanceof PlatformBitmapBytesMap) { - stream.write(177); + stream.write(178); writeValue(stream, ((PlatformBitmapBytesMap) value).toList()); } else { super.writeValue(stream, value); @@ -7963,6 +8064,30 @@ public void onClusterTap(@NonNull PlatformCluster clusterArg, @NonNull VoidResul } }); } + /** Called when a POI is tapped. */ + public void onPoiTap(@NonNull PlatformPointOfInterest poiArg, @NonNull VoidResult result) { + final String channelName = + "dev.flutter.pigeon.google_maps_flutter_android.MapsCallbackApi.onPoiTap" + + messageChannelSuffix; + BasicMessageChannel channel = + new BasicMessageChannel<>(binaryMessenger, channelName, getCodec()); + channel.send( + new ArrayList<>(Collections.singletonList(poiArg)), + channelReply -> { + if (channelReply instanceof List) { + List listReply = (List) channelReply; + if (listReply.size() > 1) { + result.error( + new FlutterError( + (String) listReply.get(0), (String) listReply.get(1), listReply.get(2))); + } else { + result.success(); + } + } else { + result.error(createConnectionError(channelName)); + } + }); + } /** Called when a polygon is tapped. */ public void onPolygonTap(@NonNull String polygonIdArg, @NonNull VoidResult result) { final String channelName = 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 d2339531eba1..0bacd60a4c86 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 @@ -4,6 +4,7 @@ package io.flutter.plugins.googlemaps; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -26,6 +27,7 @@ import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; 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; @@ -35,6 +37,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; @@ -317,4 +320,22 @@ public void getCameraPositionReturnsCorrectData() { Assert.assertEquals(cameraPosition.tilt, result.getTilt(), 1e-15); Assert.assertEquals(cameraPosition.bearing, result.getBearing(), 1e-15); } + + @Test + public void onPoiClick() { + GoogleMapController controller = getGoogleMapControllerWithMockedDependencies(); + PointOfInterest poi = new PointOfInterest(new LatLng(1.0, 2.0), "placeId", "name"); + + controller.onPoiClick(poi); + + ArgumentCaptor poiCaptor = + ArgumentCaptor.forClass(Messages.PlatformPointOfInterest.class); + + verify(flutterApi).onPoiTap(poiCaptor.capture(), any(Messages.VoidResult.class)); + + Messages.PlatformPointOfInterest capturedPoi = poiCaptor.getValue(); + assertEquals("placeId", capturedPoi.getPlaceId()); + assertEquals(1.0, capturedPoi.getPosition().getLatitude(), 1e-6); + assertEquals(2.0, capturedPoi.getPosition().getLongitude(), 1e-6); + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart index 518304c93fd9..1404a4b4049c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart @@ -83,6 +83,9 @@ class ExampleGoogleMapController { .listen( (InfoWindowTapEvent e) => _googleMapState.onInfoWindowTap(e.value), ); + GoogleMapsFlutterPlatform.instance + .onPoiTap(mapId: mapId) + .listen((MapPoiTapEvent e) => _googleMapState.onPoiTap(e.value)); GoogleMapsFlutterPlatform.instance .onPolylineTap(mapId: mapId) .listen((PolylineTapEvent e) => _googleMapState.onPolylineTap(e.value)); @@ -101,6 +104,9 @@ class ExampleGoogleMapController { GoogleMapsFlutterPlatform.instance .onTap(mapId: mapId) .listen((MapTapEvent e) => _googleMapState.onTap(e.position)); + GoogleMapsFlutterPlatform.instance + .onPoiTap(mapId: mapId) + .listen((MapPoiTapEvent e) => _googleMapState.onPoiTap(e.value)); GoogleMapsFlutterPlatform.instance .onLongPress(mapId: mapId) .listen( @@ -307,6 +313,7 @@ class ExampleGoogleMap extends StatefulWidget { this.markers = const {}, this.polygons = const {}, this.polylines = const {}, + this.onPoiTap, this.circles = const {}, this.clusterManagers = const {}, this.onCameraMoveStarted, @@ -379,6 +386,9 @@ class ExampleGoogleMap extends StatefulWidget { /// Polylines to be placed on the map. final Set polylines; + ///Point of Interest Callback + final ArgumentCallback? onPoiTap; + /// Circles to be placed on the map. final Set circles; @@ -648,6 +658,10 @@ class _ExampleGoogleMapState extends State { widget.onTap?.call(position); } + void onPoiTap(PointOfInterest pointOfInterest) { + widget.onPoiTap?.call(pointOfInterest); + } + void onLongPress(LatLng position) { widget.onLongPress?.call(position); } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart.rej b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart.rej new file mode 100644 index 000000000000..b2d158e95efb --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart.rej @@ -0,0 +1,11 @@ +--- packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart ++++ packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart +@@ -104,7 +104,7 @@ class ExampleGoogleMapController { + GoogleMapsFlutterPlatform.instance + .onTap(mapId: mapId) + .listen((MapTapEvent e) => _googleMapState.onTap(e.position)); +- GoogleMapsFlutterPlatform.instance ++ GoogleMapsFlutterPlatform.instance + .onPoiTap(mapId: mapId) + .listen((MapPoiTapEvent e) => _googleMapState.onPoiTap(e.value)); + GoogleMapsFlutterPlatform.instance diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart index f7b60568657c..b074218a0529 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart @@ -22,6 +22,7 @@ import 'padding.dart'; import 'page.dart'; import 'place_circle.dart'; import 'place_marker.dart'; +import 'place_poi.dart'; import 'place_polygon.dart'; import 'place_polyline.dart'; import 'scrolling_map.dart'; @@ -40,6 +41,7 @@ final List _allPages = [ const PlacePolylinePage(), const PlacePolygonPage(), const PlaceCirclePage(), + const PlacePoiPage(), const PaddingPage(), const SnapshotPage(), const LiteModePage(), diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_poi.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_poi.dart new file mode 100644 index 000000000000..9d289f6aeae4 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_poi.dart @@ -0,0 +1,95 @@ +// 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/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +// ignore: prefer_relative_imports +import 'example_google_map.dart'; +import 'page.dart'; + +/// Page for demonstrating Point of Interest (POI) tapping. +class PlacePoiPage extends GoogleMapExampleAppPage { + /// Default constructor. + const PlacePoiPage({super.key}) + : super(const Icon(Icons.business), 'Place POI'); + + @override + Widget build(BuildContext context) { + return const PlacePoiBody(); + } +} + +/// Body of the POI page. +class PlacePoiBody extends StatefulWidget { + /// Default constructor. + const PlacePoiBody({super.key}); + + @override + State createState() => PlacePoiBodyState(); +} + +/// State for [PlacePoiBody]. +class PlacePoiBodyState extends State { + /// The controller for the map. + ExampleGoogleMapController? controller; + PointOfInterest? _lastPoi; + + final CameraPosition _kKolkata = const CameraPosition( + target: LatLng(22.54222641620606, 88.34560669761545), + zoom: 16.0, + ); + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + void _onPoiTap(PointOfInterest poi) { + setState(() { + _lastPoi = poi; + }); + + controller?.animateCamera(CameraUpdate.newLatLng(poi.position)); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: _kKolkata, + onPoiTap: _onPoiTap, + myLocationButtonEnabled: false, + ), + ), + Container( + color: Colors.white, + width: double.infinity, + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Last Tapped POI:', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox(height: 8), + if (_lastPoi != null) ...[ + Text('Place ID: ${_lastPoi!.placeId}'), + Text( + 'Lat/Lng: ${_lastPoi!.position.latitude.toStringAsFixed(5)}, ${_lastPoi!.position.longitude.toStringAsFixed(5)}', + ), + ] else + const Text('Tap on a business or landmark icon...'), + ], + ), + ), + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml index 58cec3dcf1a5..3901e13664b5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the google_maps_flutter plugin. publish_to: none environment: - sdk: ^3.9.0 - flutter: ">=3.35.0" + sdk: ^3.8.0 + flutter: ">=3.32.0" dependencies: cupertino_icons: ^1.0.5 @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - google_maps_flutter_platform_interface: ^2.13.0 + google_maps_flutter_platform_interface: ^2.15.0 dev_dependencies: build_runner: ^2.1.10 @@ -33,3 +33,7 @@ flutter: uses-material-design: true assets: - assets/ + +dependency_overrides: + google_maps_flutter_platform_interface: + path: ../../google_maps_flutter_platform_interface 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 a589658f5c6e..148e1406c481 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 @@ -234,6 +234,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onPoiTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override Stream onPolylineTap({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 b371e7a3a1f6..be95a9e318e9 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 @@ -228,6 +228,11 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPoiTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Future updateMapConfiguration( MapConfiguration configuration, { @@ -1257,6 +1262,19 @@ class HostMapMessageHandler implements MapsCallbackApi { streamController.add(MarkerTapEvent(mapId, MarkerId(markerId))); } + @override + void onPoiTap(PlatformPointOfInterest poi) { + streamController.add( + MapPoiTapEvent( + mapId, + PointOfInterest( + position: LatLng(poi.position.latitude, poi.position.longitude), + placeId: poi.placeId, + ), + ), + ); + } + @override void onPolygonTap(String polygonId) { streamController.add(PolygonTapEvent(mapId, PolygonId(polygonId))); 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 ba5674412009..a522f290ca35 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 @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v26.1.5), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -977,6 +977,47 @@ class PlatformMarker { int get hashCode => Object.hashAll(_toList()); } +/// Pigeon equivalent of the Point of Interest class. +class PlatformPointOfInterest { + PlatformPointOfInterest({required this.position, required this.placeId}); + + PlatformLatLng position; + + String placeId; + + List _toList() { + return [position, placeId]; + } + + Object encode() { + return _toList(); + } + + static PlatformPointOfInterest decode(Object result) { + result as List; + return PlatformPointOfInterest( + position: result[0]! as PlatformLatLng, + placeId: result[1]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlatformPointOfInterest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + /// Pigeon equivalent of the Polygon class. class PlatformPolygon { PlatformPolygon({ @@ -2460,78 +2501,81 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is PlatformMarker) { buffer.putUint8(153); writeValue(buffer, value.encode()); - } else if (value is PlatformPolygon) { + } else if (value is PlatformPointOfInterest) { buffer.putUint8(154); writeValue(buffer, value.encode()); - } else if (value is PlatformPolyline) { + } else if (value is PlatformPolygon) { buffer.putUint8(155); writeValue(buffer, value.encode()); - } else if (value is PlatformCap) { + } else if (value is PlatformPolyline) { buffer.putUint8(156); writeValue(buffer, value.encode()); - } else if (value is PlatformPatternItem) { + } else if (value is PlatformCap) { buffer.putUint8(157); writeValue(buffer, value.encode()); - } else if (value is PlatformTile) { + } else if (value is PlatformPatternItem) { buffer.putUint8(158); writeValue(buffer, value.encode()); - } else if (value is PlatformTileOverlay) { + } else if (value is PlatformTile) { buffer.putUint8(159); writeValue(buffer, value.encode()); - } else if (value is PlatformEdgeInsets) { + } else if (value is PlatformTileOverlay) { buffer.putUint8(160); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLng) { + } else if (value is PlatformEdgeInsets) { buffer.putUint8(161); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLngBounds) { + } else if (value is PlatformLatLng) { buffer.putUint8(162); writeValue(buffer, value.encode()); - } else if (value is PlatformCluster) { + } else if (value is PlatformLatLngBounds) { buffer.putUint8(163); writeValue(buffer, value.encode()); - } else if (value is PlatformGroundOverlay) { + } else if (value is PlatformCluster) { buffer.putUint8(164); writeValue(buffer, value.encode()); - } else if (value is PlatformCameraTargetBounds) { + } else if (value is PlatformGroundOverlay) { buffer.putUint8(165); writeValue(buffer, value.encode()); - } else if (value is PlatformMapViewCreationParams) { + } else if (value is PlatformCameraTargetBounds) { buffer.putUint8(166); writeValue(buffer, value.encode()); - } else if (value is PlatformMapConfiguration) { + } else if (value is PlatformMapViewCreationParams) { buffer.putUint8(167); writeValue(buffer, value.encode()); - } else if (value is PlatformPoint) { + } else if (value is PlatformMapConfiguration) { buffer.putUint8(168); writeValue(buffer, value.encode()); - } else if (value is PlatformTileLayer) { + } else if (value is PlatformPoint) { buffer.putUint8(169); writeValue(buffer, value.encode()); - } else if (value is PlatformZoomRange) { + } else if (value is PlatformTileLayer) { buffer.putUint8(170); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmap) { + } else if (value is PlatformZoomRange) { buffer.putUint8(171); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapDefaultMarker) { + } else if (value is PlatformBitmap) { buffer.putUint8(172); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapBytes) { + } else if (value is PlatformBitmapDefaultMarker) { buffer.putUint8(173); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapAsset) { + } else if (value is PlatformBitmapBytes) { buffer.putUint8(174); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapAssetImage) { + } else if (value is PlatformBitmapAsset) { buffer.putUint8(175); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapAssetMap) { + } else if (value is PlatformBitmapAssetImage) { buffer.putUint8(176); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapBytesMap) { + } else if (value is PlatformBitmapAssetMap) { buffer.putUint8(177); writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapBytesMap) { + buffer.putUint8(178); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2597,52 +2641,54 @@ class _PigeonCodec extends StandardMessageCodec { case 153: return PlatformMarker.decode(readValue(buffer)!); case 154: - return PlatformPolygon.decode(readValue(buffer)!); + return PlatformPointOfInterest.decode(readValue(buffer)!); case 155: - return PlatformPolyline.decode(readValue(buffer)!); + return PlatformPolygon.decode(readValue(buffer)!); case 156: - return PlatformCap.decode(readValue(buffer)!); + return PlatformPolyline.decode(readValue(buffer)!); case 157: - return PlatformPatternItem.decode(readValue(buffer)!); + return PlatformCap.decode(readValue(buffer)!); case 158: - return PlatformTile.decode(readValue(buffer)!); + return PlatformPatternItem.decode(readValue(buffer)!); case 159: - return PlatformTileOverlay.decode(readValue(buffer)!); + return PlatformTile.decode(readValue(buffer)!); case 160: - return PlatformEdgeInsets.decode(readValue(buffer)!); + return PlatformTileOverlay.decode(readValue(buffer)!); case 161: - return PlatformLatLng.decode(readValue(buffer)!); + return PlatformEdgeInsets.decode(readValue(buffer)!); case 162: - return PlatformLatLngBounds.decode(readValue(buffer)!); + return PlatformLatLng.decode(readValue(buffer)!); case 163: - return PlatformCluster.decode(readValue(buffer)!); + return PlatformLatLngBounds.decode(readValue(buffer)!); case 164: - return PlatformGroundOverlay.decode(readValue(buffer)!); + return PlatformCluster.decode(readValue(buffer)!); case 165: - return PlatformCameraTargetBounds.decode(readValue(buffer)!); + return PlatformGroundOverlay.decode(readValue(buffer)!); case 166: - return PlatformMapViewCreationParams.decode(readValue(buffer)!); + return PlatformCameraTargetBounds.decode(readValue(buffer)!); case 167: - return PlatformMapConfiguration.decode(readValue(buffer)!); + return PlatformMapViewCreationParams.decode(readValue(buffer)!); case 168: - return PlatformPoint.decode(readValue(buffer)!); + return PlatformMapConfiguration.decode(readValue(buffer)!); case 169: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformPoint.decode(readValue(buffer)!); case 170: - return PlatformZoomRange.decode(readValue(buffer)!); + return PlatformTileLayer.decode(readValue(buffer)!); case 171: - return PlatformBitmap.decode(readValue(buffer)!); + return PlatformZoomRange.decode(readValue(buffer)!); case 172: - return PlatformBitmapDefaultMarker.decode(readValue(buffer)!); + return PlatformBitmap.decode(readValue(buffer)!); case 173: - return PlatformBitmapBytes.decode(readValue(buffer)!); + return PlatformBitmapDefaultMarker.decode(readValue(buffer)!); case 174: - return PlatformBitmapAsset.decode(readValue(buffer)!); + return PlatformBitmapBytes.decode(readValue(buffer)!); case 175: - return PlatformBitmapAssetImage.decode(readValue(buffer)!); + return PlatformBitmapAsset.decode(readValue(buffer)!); case 176: - return PlatformBitmapAssetMap.decode(readValue(buffer)!); + return PlatformBitmapAssetImage.decode(readValue(buffer)!); case 177: + return PlatformBitmapAssetMap.decode(readValue(buffer)!); + case 178: return PlatformBitmapBytesMap.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -3386,6 +3432,9 @@ abstract class MapsCallbackApi { /// Called when a marker cluster is tapped. void onClusterTap(PlatformCluster cluster); + /// Called when a POI is tapped. + void onPoiTap(PlatformPointOfInterest poi); + /// Called when a polygon is tapped. void onPolygonTap(String polygonId); @@ -3802,6 +3851,40 @@ abstract class MapsCallbackApi { }); } } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_maps_flutter_android.MapsCallbackApi.onPoiTap$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_android.MapsCallbackApi.onPoiTap was null.', + ); + final List args = (message as List?)!; + final PlatformPointOfInterest? arg_poi = + (args[0] as PlatformPointOfInterest?); + assert( + arg_poi != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_android.MapsCallbackApi.onPoiTap was null, expected non-null PlatformPointOfInterest.', + ); + try { + api.onPoiTap(arg_poi!); + 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.onPolygonTap$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 60096d9cf3af..038cf572cecf 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 @@ -235,6 +235,14 @@ class PlatformMarker { final String? clusterManagerId; } +/// Pigeon equivalent of the Point of Interest class. +class PlatformPointOfInterest { + PlatformPointOfInterest({required this.position, required this.placeId}); + + final PlatformLatLng position; + final String placeId; +} + /// Pigeon equivalent of the Polygon class. class PlatformPolygon { PlatformPolygon({ @@ -806,6 +814,9 @@ abstract class MapsCallbackApi { /// Called when a marker cluster is tapped. void onClusterTap(PlatformCluster cluster); + /// Called when a POI is tapped. + void onPoiTap(PlatformPointOfInterest poi); + /// Called when a polygon is tapped. void onPolygonTap(String polygonId); 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 d721130dfee1..1fb9a8d7f6ec 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml @@ -2,11 +2,11 @@ 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.18.12 +version: 2.19.0 environment: - sdk: ^3.9.0 - flutter: ">=3.35.0" + sdk: ^3.8.0 + flutter: ">=3.32.0" flutter: plugin: @@ -21,7 +21,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - google_maps_flutter_platform_interface: ^2.13.0 + google_maps_flutter_platform_interface: ^2.15.0 stream_transform: ^2.0.0 dev_dependencies: @@ -37,3 +37,7 @@ topics: - google-maps - google-maps-flutter - map + +dependency_overrides: + google_maps_flutter_platform_interface: + path: ../google_maps_flutter_platform_interface 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 403adf3df6c6..94c051077d47 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 @@ -1496,4 +1496,29 @@ void main() { reason: 'Should pass mapId in PlatformView creation message', ); }); + + test('onPoiTap sends events to correct stream', () async { + const mapId = 1; + final fakePosition = PlatformLatLng(latitude: 12.34, longitude: 56.78); + const fakePlaceId = 'iso_id_123'; + + final maps = GoogleMapsFlutterAndroid(); + final HostMapMessageHandler callbackHandler = maps.ensureHandlerInitialized( + mapId, + ); + + final stream = StreamQueue(maps.onPoiTap(mapId: mapId)); + + callbackHandler.onPoiTap( + PlatformPointOfInterest(position: fakePosition, placeId: fakePlaceId), + ); + + final MapPoiTapEvent event = await stream.next; + expect(event.mapId, mapId); + + final PointOfInterest poi = event.value; + expect(poi.position.latitude, fakePosition.latitude); + expect(poi.position.longitude, fakePosition.longitude); + expect(poi.placeId, fakePlaceId); + }); } 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 3338803175ed..234dfa5e1170 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,6 +1,6 @@ -## 2.17.1 +## 2.18.0 -* Refactors code for improved testability. +* Implements `onPoiTap` support for iOS using `didTapPOIWithPlaceID`. ## 2.17.0 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 e51da47062f7..6c6591818904 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 @@ -54,6 +54,24 @@ @interface FLTGoogleMapFactory (Test) @property(strong, nonatomic, readonly) id sharedMapServices; @end +@interface FLTGoogleMapController (Testing) +@property(nonatomic, strong) FGMMapsCallbackApi *dartCallbackHandler; +@end + +@interface MockMapCallbackHandler : FGMMapsCallbackApi +@property(nonatomic, strong) FGMPlatformPointOfInterest *lastPoiTap; +@end + +@implementation MockMapCallbackHandler +- (void)didTapPointOfInterest:(FGMPlatformPointOfInterest *)poi + completion:(void (^)(FlutterError *_Nullable))completion { + self.lastPoiTap = poi; + if (completion) { + completion(nil); + } +} +@end + @interface GoogleMapsTests : XCTestCase @end @@ -222,6 +240,37 @@ - (void)testInspectorAPICameraPosition { XCTAssertEqual(cameraPosition.zoom, initialCameraPosition.zoom); } +- (void)testDidTapPOI { + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSMapViewOptions *options = [[GMSMapViewOptions alloc] init]; + options.frame = frame; + options.camera = [GMSCameraPosition cameraWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:options]; + + FLTGoogleMapController *controller = + [[FLTGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + creationParameters:[self emptyCreationParameters] + assetProvider:[[TestAssetProvider alloc] init] + binaryMessenger:[[StubBinaryMessenger alloc] init]]; + + MockMapCallbackHandler *mockHandler = + [[MockMapCallbackHandler alloc] initWithBinaryMessenger:[[StubBinaryMessenger alloc] init]]; + controller.dartCallbackHandler = mockHandler; + + NSString *placeId = @"test_place_id"; + NSString *name = @"Test POI Name"; + CLLocationCoordinate2D location = CLLocationCoordinate2DMake(10.0, 20.0); + + [controller mapView:mapView didTapPOIWithPlaceID:placeId location:location]; + + XCTAssertNotNil(mockHandler.lastPoiTap, @"The POI tap should have been recorded."); + XCTAssertEqualObjects(mockHandler.lastPoiTap.placeId, placeId); + XCTAssertEqual(mockHandler.lastPoiTap.position.latitude, 10.0); + XCTAssertEqual(mockHandler.lastPoiTap.position.longitude, 20.0); +} + /// Creates an empty creation paramaters object for tests where the values don't matter, just that /// there's a valid object to pass in. - (FGMPlatformMapViewCreationParams *)emptyCreationParameters { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart index 152f83c16f38..5fc64a32f02c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart @@ -106,6 +106,9 @@ class ExampleGoogleMapController { .listen( (MapLongPressEvent e) => _googleMapState.onLongPress(e.position), ); + GoogleMapsFlutterPlatform.instance + .onPoiTap(mapId: mapId) + .listen((MapPoiTapEvent e) => _googleMapState.onPoiTap(e.value)); GoogleMapsFlutterPlatform.instance .onClusterTap(mapId: mapId) .listen((ClusterTapEvent e) => _googleMapState.onClusterTap(e.value)); @@ -303,6 +306,7 @@ class ExampleGoogleMap extends StatefulWidget { this.trafficEnabled = false, this.buildingsEnabled = true, this.markers = const {}, + this.onPoiTap, this.polygons = const {}, this.polylines = const {}, this.circles = const {}, @@ -365,6 +369,9 @@ class ExampleGoogleMap extends StatefulWidget { /// Markers to be placed on the map. final Set markers; + /// Point of Interest Callback + final void Function(PointOfInterest poi)? onPoiTap; + /// Polygons to be placed on the map. final Set polygons; @@ -640,6 +647,10 @@ class _ExampleGoogleMapState extends State { widget.onTap?.call(position); } + void onPoiTap(PointOfInterest poi) { + widget.onPoiTap?.call(poi); + } + void onLongPress(LatLng position) { widget.onLongPress?.call(position); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml index 72b3f21ed9ca..9f68a6eaea39 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the google_maps_flutter plugin. publish_to: none environment: - sdk: ^3.9.0 - flutter: ">=3.35.0" + sdk: ^3.8.0 + flutter: ">=3.32.0" dependencies: cupertino_icons: ^1.0.5 @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - google_maps_flutter_platform_interface: ^2.13.0 + google_maps_flutter_platform_interface: ^2.15.0 dev_dependencies: flutter_test: @@ -31,3 +31,7 @@ flutter: uses-material-design: true assets: - assets/ + +dependency_overrides: + google_maps_flutter_platform_interface: + path: ../../google_maps_flutter_platform_interface diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/test/example_google_map_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/test/example_google_map_test.dart index 261fc473e8bb..310f10f909b9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/test/example_google_map_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/test/example_google_map_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_example/example_google_map.dart'; @@ -173,4 +174,33 @@ void main() { await tester.pumpAndSettle(); }); + testWidgets('onPoiTap callback is called', (WidgetTester tester) async { + PointOfInterest? tappedPoi; + final mapCreatedCompleter = Completer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition(target: LatLng(0, 0)), + onPoiTap: (PointOfInterest poi) => tappedPoi = poi, + onMapCreated: (_) => mapCreatedCompleter.complete(), + ), + ), + ); + + await mapCreatedCompleter.future; + + const poi = PointOfInterest( + position: LatLng(10.0, 10.0), + placeId: 'test_id_123', + ); + + platform.mapEventStreamController.add(MapPoiTapEvent(0, poi)); + + await tester.pump(); + + expect(tappedPoi, poi); + expect(tappedPoi!.placeId, 'test_id_123'); + }); } 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 899a7709c548..ed0a7f354f6a 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 @@ -234,6 +234,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onPoiTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override Stream onPolylineTap({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/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.m index c265785d624e..5d415aa3ae3d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios/Sources/google_maps_flutter_ios/GoogleMapController.m @@ -560,6 +560,18 @@ - (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D [self.mapEventHandler didTapAtPosition:FGMGetPigeonLatLngForCoordinate(coordinate)]; } +- (void)mapView:(GMSMapView *)mapView + didTapPOIWithPlaceID:(NSString *)placeID + name:(NSString *)name + location:(CLLocationCoordinate2D)location { + FGMPlatformPointOfInterest *poi = + [FGMPlatformPointOfInterest makeWithPosition:FGMGetPigeonLatLngForCoordinate(location) + placeId:placeID]; + [self.dartCallbackHandler didTapPointOfInterest:poi + completion:^(FlutterError *_Nullable _){ + }]; +} + - (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { [self.mapEventHandler didLongPressAtPosition: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 e833e2f24f42..3ad79d71fc2b 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 @@ -1,19 +1,15 @@ // 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. -// Autogenerated from Pigeon (v26.1.5), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "google_maps_flutter_pigeon_messages.g.h" #if TARGET_OS_OSX -#import +@import FlutterMacOS; #else -#import -#endif - -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. +@import Flutter; #endif static NSArray *wrapResult(id result, FlutterError *error) { @@ -191,6 +187,12 @@ + (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformPointOfInterest () ++ (FGMPlatformPointOfInterest *)fromList:(NSArray *)list; ++ (nullable FGMPlatformPointOfInterest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformPolygon () + (FGMPlatformPolygon *)fromList:(NSArray *)list; + (nullable FGMPlatformPolygon *)nullableFromList:(NSArray *)list; @@ -878,6 +880,30 @@ + (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformPointOfInterest ++ (instancetype)makeWithPosition:(FGMPlatformLatLng *)position placeId:(NSString *)placeId { + FGMPlatformPointOfInterest *pigeonResult = [[FGMPlatformPointOfInterest alloc] init]; + pigeonResult.position = position; + pigeonResult.placeId = placeId; + return pigeonResult; +} ++ (FGMPlatformPointOfInterest *)fromList:(NSArray *)list { + FGMPlatformPointOfInterest *pigeonResult = [[FGMPlatformPointOfInterest alloc] init]; + pigeonResult.position = GetNullableObjectAtIndex(list, 0); + pigeonResult.placeId = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable FGMPlatformPointOfInterest *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformPointOfInterest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.position ?: [NSNull null], + self.placeId ?: [NSNull null], + ]; +} +@end + @implementation FGMPlatformPolygon + (instancetype)makeWithPolygonId:(NSString *)polygonId consumesTapEvents:(BOOL)consumesTapEvents @@ -1795,52 +1821,54 @@ - (nullable id)readValueOfType:(UInt8)type { case 150: return [FGMPlatformMarker fromList:[self readValue]]; case 151: - return [FGMPlatformPolygon fromList:[self readValue]]; + return [FGMPlatformPointOfInterest fromList:[self readValue]]; case 152: - return [FGMPlatformPolyline fromList:[self readValue]]; + return [FGMPlatformPolygon fromList:[self readValue]]; case 153: - return [FGMPlatformPatternItem fromList:[self readValue]]; + return [FGMPlatformPolyline fromList:[self readValue]]; case 154: - return [FGMPlatformTile fromList:[self readValue]]; + return [FGMPlatformPatternItem fromList:[self readValue]]; case 155: - return [FGMPlatformTileOverlay fromList:[self readValue]]; + return [FGMPlatformTile fromList:[self readValue]]; case 156: - return [FGMPlatformEdgeInsets fromList:[self readValue]]; + return [FGMPlatformTileOverlay fromList:[self readValue]]; case 157: - return [FGMPlatformLatLng fromList:[self readValue]]; + return [FGMPlatformEdgeInsets fromList:[self readValue]]; case 158: - return [FGMPlatformLatLngBounds fromList:[self readValue]]; + return [FGMPlatformLatLng fromList:[self readValue]]; case 159: - return [FGMPlatformCameraTargetBounds fromList:[self readValue]]; + return [FGMPlatformLatLngBounds fromList:[self readValue]]; case 160: - return [FGMPlatformGroundOverlay fromList:[self readValue]]; + return [FGMPlatformCameraTargetBounds fromList:[self readValue]]; case 161: - return [FGMPlatformMapViewCreationParams fromList:[self readValue]]; + return [FGMPlatformGroundOverlay fromList:[self readValue]]; case 162: - return [FGMPlatformMapConfiguration fromList:[self readValue]]; + return [FGMPlatformMapViewCreationParams fromList:[self readValue]]; case 163: - return [FGMPlatformPoint fromList:[self readValue]]; + return [FGMPlatformMapConfiguration fromList:[self readValue]]; case 164: - return [FGMPlatformSize fromList:[self readValue]]; + return [FGMPlatformPoint fromList:[self readValue]]; case 165: - return [FGMPlatformColor fromList:[self readValue]]; + return [FGMPlatformSize fromList:[self readValue]]; case 166: - return [FGMPlatformTileLayer fromList:[self readValue]]; + return [FGMPlatformColor fromList:[self readValue]]; case 167: - return [FGMPlatformZoomRange fromList:[self readValue]]; + return [FGMPlatformTileLayer fromList:[self readValue]]; case 168: - return [FGMPlatformBitmap fromList:[self readValue]]; + return [FGMPlatformZoomRange fromList:[self readValue]]; case 169: - return [FGMPlatformBitmapDefaultMarker fromList:[self readValue]]; + return [FGMPlatformBitmap fromList:[self readValue]]; case 170: - return [FGMPlatformBitmapBytes fromList:[self readValue]]; + return [FGMPlatformBitmapDefaultMarker fromList:[self readValue]]; case 171: - return [FGMPlatformBitmapAsset fromList:[self readValue]]; + return [FGMPlatformBitmapBytes fromList:[self readValue]]; case 172: - return [FGMPlatformBitmapAssetImage fromList:[self readValue]]; + return [FGMPlatformBitmapAsset fromList:[self readValue]]; case 173: - return [FGMPlatformBitmapAssetMap fromList:[self readValue]]; + return [FGMPlatformBitmapAssetImage fromList:[self readValue]]; case 174: + return [FGMPlatformBitmapAssetMap fromList:[self readValue]]; + case 175: return [FGMPlatformBitmapBytesMap fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -1922,78 +1950,81 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { [self writeByte:150]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { + } else if ([value isKindOfClass:[FGMPlatformPointOfInterest class]]) { [self writeByte:151]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { [self writeByte:152]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPatternItem class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { [self writeByte:153]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTile class]]) { + } else if ([value isKindOfClass:[FGMPlatformPatternItem class]]) { [self writeByte:154]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { + } else if ([value isKindOfClass:[FGMPlatformTile class]]) { [self writeByte:155]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformEdgeInsets class]]) { + } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { [self writeByte:156]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { + } else if ([value isKindOfClass:[FGMPlatformEdgeInsets class]]) { [self writeByte:157]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { [self writeByte:158]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformCameraTargetBounds class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { [self writeByte:159]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformGroundOverlay class]]) { + } else if ([value isKindOfClass:[FGMPlatformCameraTargetBounds class]]) { [self writeByte:160]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMapViewCreationParams class]]) { + } else if ([value isKindOfClass:[FGMPlatformGroundOverlay class]]) { [self writeByte:161]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { + } else if ([value isKindOfClass:[FGMPlatformMapViewCreationParams class]]) { [self writeByte:162]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { + } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { [self writeByte:163]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformSize class]]) { + } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { [self writeByte:164]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformColor class]]) { + } else if ([value isKindOfClass:[FGMPlatformSize class]]) { [self writeByte:165]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + } else if ([value isKindOfClass:[FGMPlatformColor class]]) { [self writeByte:166]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { [self writeByte:167]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformBitmap class]]) { + } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { [self writeByte:168]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformBitmapDefaultMarker class]]) { + } else if ([value isKindOfClass:[FGMPlatformBitmap class]]) { [self writeByte:169]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformBitmapBytes class]]) { + } else if ([value isKindOfClass:[FGMPlatformBitmapDefaultMarker class]]) { [self writeByte:170]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformBitmapAsset class]]) { + } else if ([value isKindOfClass:[FGMPlatformBitmapBytes class]]) { [self writeByte:171]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformBitmapAssetImage class]]) { + } else if ([value isKindOfClass:[FGMPlatformBitmapAsset class]]) { [self writeByte:172]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformBitmapAssetMap class]]) { + } else if ([value isKindOfClass:[FGMPlatformBitmapAssetImage class]]) { [self writeByte:173]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformBitmapBytesMap class]]) { + } else if ([value isKindOfClass:[FGMPlatformBitmapAssetMap class]]) { [self writeByte:174]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmapBytesMap class]]) { + [self writeByte:175]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -3048,6 +3079,31 @@ - (void)didTapGroundOverlayWithIdentifier:(NSString *)arg_groundOverlayId } }]; } +- (void)didTapPointOfInterest:(FGMPlatformPointOfInterest *)arg_poi + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPoiTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetGoogleMapsFlutterPigeonMessagesCodec()]; + [channel sendMessage:@[ arg_poi ?: [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)tileWithOverlayIdentifier:(NSString *)arg_tileOverlayId location:(FGMPlatformPoint *)arg_location zoom:(NSInteger)arg_zoom 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 db11f425c37c..4df9a8590384 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 @@ -1,10 +1,10 @@ // 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. -// Autogenerated from Pigeon (v26.1.5), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import +@import Foundation; @protocol FlutterBinaryMessenger; @protocol FlutterMessageCodec; @@ -84,6 +84,7 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapBitmapScaling) { @class FGMPlatformCluster; @class FGMPlatformClusterManager; @class FGMPlatformMarker; +@class FGMPlatformPointOfInterest; @class FGMPlatformPolygon; @class FGMPlatformPolyline; @class FGMPlatformPatternItem; @@ -338,6 +339,15 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapBitmapScaling) { @property(nonatomic, copy, nullable) NSString *clusterManagerId; @end +/// Pigeon equivalent of the Point of Interest class. +@interface FGMPlatformPointOfInterest : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPosition:(FGMPlatformLatLng *)position placeId:(NSString *)placeId; +@property(nonatomic, strong) FGMPlatformLatLng *position; +@property(nonatomic, copy) NSString *placeId; +@end + /// Pigeon equivalent of the Polygon class. @interface FGMPlatformPolygon : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -871,6 +881,9 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger /// Called when a ground overlay is tapped. - (void)didTapGroundOverlayWithIdentifier:(NSString *)groundOverlayId completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a point of interest is tapped. +- (void)didTapPointOfInterest:(FGMPlatformPointOfInterest *)poi + completion:(void (^)(FlutterError *_Nullable))completion; /// Called to get data for a map tile. - (void)tileWithOverlayIdentifier:(NSString *)tileOverlayId location:(FGMPlatformPoint *)location 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 c7e469ad1491..d1bec9961f24 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 @@ -206,6 +206,11 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPoiTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Future updateMapConfiguration( MapConfiguration configuration, { @@ -1161,6 +1166,19 @@ class HostMapMessageHandler implements MapsCallbackApi { MapTapEvent(mapId, _latLngFromPlatformLatLng(position)), ); } + + @override + void onPoiTap(PlatformPointOfInterest poi) { + streamController.add( + MapPoiTapEvent( + mapId, + PointOfInterest( + position: LatLng(poi.position.latitude, poi.position.longitude), + placeId: poi.placeId, + ), + ), + ); + } } LatLng _latLngFromPlatformLatLng(PlatformLatLng latLng) { 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 bd54d5f62322..9d5fff9cc140 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 @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v26.1.5), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -956,6 +956,47 @@ class PlatformMarker { int get hashCode => Object.hashAll(_toList()); } +/// Pigeon equivalent of the Point of Interest class. +class PlatformPointOfInterest { + PlatformPointOfInterest({required this.position, required this.placeId}); + + PlatformLatLng position; + + String placeId; + + List _toList() { + return [position, placeId]; + } + + Object encode() { + return _toList(); + } + + static PlatformPointOfInterest decode(Object result) { + result as List; + return PlatformPointOfInterest( + position: result[0]! as PlatformLatLng, + placeId: result[1]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlatformPointOfInterest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + /// Pigeon equivalent of the Polygon class. class PlatformPolygon { PlatformPolygon({ @@ -2388,78 +2429,81 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is PlatformMarker) { buffer.putUint8(150); writeValue(buffer, value.encode()); - } else if (value is PlatformPolygon) { + } else if (value is PlatformPointOfInterest) { buffer.putUint8(151); writeValue(buffer, value.encode()); - } else if (value is PlatformPolyline) { + } else if (value is PlatformPolygon) { buffer.putUint8(152); writeValue(buffer, value.encode()); - } else if (value is PlatformPatternItem) { + } else if (value is PlatformPolyline) { buffer.putUint8(153); writeValue(buffer, value.encode()); - } else if (value is PlatformTile) { + } else if (value is PlatformPatternItem) { buffer.putUint8(154); writeValue(buffer, value.encode()); - } else if (value is PlatformTileOverlay) { + } else if (value is PlatformTile) { buffer.putUint8(155); writeValue(buffer, value.encode()); - } else if (value is PlatformEdgeInsets) { + } else if (value is PlatformTileOverlay) { buffer.putUint8(156); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLng) { + } else if (value is PlatformEdgeInsets) { buffer.putUint8(157); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLngBounds) { + } else if (value is PlatformLatLng) { buffer.putUint8(158); writeValue(buffer, value.encode()); - } else if (value is PlatformCameraTargetBounds) { + } else if (value is PlatformLatLngBounds) { buffer.putUint8(159); writeValue(buffer, value.encode()); - } else if (value is PlatformGroundOverlay) { + } else if (value is PlatformCameraTargetBounds) { buffer.putUint8(160); writeValue(buffer, value.encode()); - } else if (value is PlatformMapViewCreationParams) { + } else if (value is PlatformGroundOverlay) { buffer.putUint8(161); writeValue(buffer, value.encode()); - } else if (value is PlatformMapConfiguration) { + } else if (value is PlatformMapViewCreationParams) { buffer.putUint8(162); writeValue(buffer, value.encode()); - } else if (value is PlatformPoint) { + } else if (value is PlatformMapConfiguration) { buffer.putUint8(163); writeValue(buffer, value.encode()); - } else if (value is PlatformSize) { + } else if (value is PlatformPoint) { buffer.putUint8(164); writeValue(buffer, value.encode()); - } else if (value is PlatformColor) { + } else if (value is PlatformSize) { buffer.putUint8(165); writeValue(buffer, value.encode()); - } else if (value is PlatformTileLayer) { + } else if (value is PlatformColor) { buffer.putUint8(166); writeValue(buffer, value.encode()); - } else if (value is PlatformZoomRange) { + } else if (value is PlatformTileLayer) { buffer.putUint8(167); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmap) { + } else if (value is PlatformZoomRange) { buffer.putUint8(168); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapDefaultMarker) { + } else if (value is PlatformBitmap) { buffer.putUint8(169); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapBytes) { + } else if (value is PlatformBitmapDefaultMarker) { buffer.putUint8(170); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapAsset) { + } else if (value is PlatformBitmapBytes) { buffer.putUint8(171); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapAssetImage) { + } else if (value is PlatformBitmapAsset) { buffer.putUint8(172); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapAssetMap) { + } else if (value is PlatformBitmapAssetImage) { buffer.putUint8(173); writeValue(buffer, value.encode()); - } else if (value is PlatformBitmapBytesMap) { + } else if (value is PlatformBitmapAssetMap) { buffer.putUint8(174); writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapBytesMap) { + buffer.putUint8(175); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2517,52 +2561,54 @@ class _PigeonCodec extends StandardMessageCodec { case 150: return PlatformMarker.decode(readValue(buffer)!); case 151: - return PlatformPolygon.decode(readValue(buffer)!); + return PlatformPointOfInterest.decode(readValue(buffer)!); case 152: - return PlatformPolyline.decode(readValue(buffer)!); + return PlatformPolygon.decode(readValue(buffer)!); case 153: - return PlatformPatternItem.decode(readValue(buffer)!); + return PlatformPolyline.decode(readValue(buffer)!); case 154: - return PlatformTile.decode(readValue(buffer)!); + return PlatformPatternItem.decode(readValue(buffer)!); case 155: - return PlatformTileOverlay.decode(readValue(buffer)!); + return PlatformTile.decode(readValue(buffer)!); case 156: - return PlatformEdgeInsets.decode(readValue(buffer)!); + return PlatformTileOverlay.decode(readValue(buffer)!); case 157: - return PlatformLatLng.decode(readValue(buffer)!); + return PlatformEdgeInsets.decode(readValue(buffer)!); case 158: - return PlatformLatLngBounds.decode(readValue(buffer)!); + return PlatformLatLng.decode(readValue(buffer)!); case 159: - return PlatformCameraTargetBounds.decode(readValue(buffer)!); + return PlatformLatLngBounds.decode(readValue(buffer)!); case 160: - return PlatformGroundOverlay.decode(readValue(buffer)!); + return PlatformCameraTargetBounds.decode(readValue(buffer)!); case 161: - return PlatformMapViewCreationParams.decode(readValue(buffer)!); + return PlatformGroundOverlay.decode(readValue(buffer)!); case 162: - return PlatformMapConfiguration.decode(readValue(buffer)!); + return PlatformMapViewCreationParams.decode(readValue(buffer)!); case 163: - return PlatformPoint.decode(readValue(buffer)!); + return PlatformMapConfiguration.decode(readValue(buffer)!); case 164: - return PlatformSize.decode(readValue(buffer)!); + return PlatformPoint.decode(readValue(buffer)!); case 165: - return PlatformColor.decode(readValue(buffer)!); + return PlatformSize.decode(readValue(buffer)!); case 166: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformColor.decode(readValue(buffer)!); case 167: - return PlatformZoomRange.decode(readValue(buffer)!); + return PlatformTileLayer.decode(readValue(buffer)!); case 168: - return PlatformBitmap.decode(readValue(buffer)!); + return PlatformZoomRange.decode(readValue(buffer)!); case 169: - return PlatformBitmapDefaultMarker.decode(readValue(buffer)!); + return PlatformBitmap.decode(readValue(buffer)!); case 170: - return PlatformBitmapBytes.decode(readValue(buffer)!); + return PlatformBitmapDefaultMarker.decode(readValue(buffer)!); case 171: - return PlatformBitmapAsset.decode(readValue(buffer)!); + return PlatformBitmapBytes.decode(readValue(buffer)!); case 172: - return PlatformBitmapAssetImage.decode(readValue(buffer)!); + return PlatformBitmapAsset.decode(readValue(buffer)!); case 173: - return PlatformBitmapAssetMap.decode(readValue(buffer)!); + return PlatformBitmapAssetImage.decode(readValue(buffer)!); case 174: + return PlatformBitmapAssetMap.decode(readValue(buffer)!); + case 175: return PlatformBitmapBytesMap.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -3301,6 +3347,9 @@ abstract class MapsCallbackApi { /// Called when a ground overlay is tapped. void onGroundOverlayTap(String groundOverlayId); + /// Called when a point of interest is tapped. + void onPoiTap(PlatformPointOfInterest poi); + /// Called to get data for a map tile. Future getTileOverlayTile( String tileOverlayId, @@ -3807,6 +3856,40 @@ abstract class MapsCallbackApi { }); } } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPoiTap$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.onPoiTap was null.', + ); + final List args = (message as List?)!; + final PlatformPointOfInterest? arg_poi = + (args[0] as PlatformPointOfInterest?); + assert( + arg_poi != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPoiTap was null, expected non-null PlatformPointOfInterest.', + ); + try { + api.onPoiTap(arg_poi!); + 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.getTileOverlayTile$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 233e91151b36..e786d3a3077d 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 @@ -237,6 +237,14 @@ class PlatformMarker { final String? clusterManagerId; } +/// Pigeon equivalent of the Point of Interest class. +class PlatformPointOfInterest { + PlatformPointOfInterest({required this.position, required this.placeId}); + + final PlatformLatLng position; + final String placeId; +} + /// Pigeon equivalent of the Polygon class. class PlatformPolygon { PlatformPolygon({ @@ -823,6 +831,10 @@ abstract class MapsCallbackApi { @ObjCSelector('didTapGroundOverlayWithIdentifier:') void onGroundOverlayTap(String groundOverlayId); + /// Called when a point of interest is tapped. + @ObjCSelector('didTapPointOfInterest:') + void onPoiTap(PlatformPointOfInterest poi); + /// Called to get data for a map tile. @async @ObjCSelector('tileWithOverlayIdentifier:location:zoom:') 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 f971ae9b5678..4482f57d458d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,11 +2,11 @@ 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.17.1 +version: 2.18.0 environment: - sdk: ^3.9.0 - flutter: ">=3.35.0" + sdk: ^3.8.0 + flutter: ">=3.32.0" flutter: plugin: @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.13.0 + google_maps_flutter_platform_interface: ^2.15.0 stream_transform: ^2.0.0 dev_dependencies: @@ -35,3 +35,7 @@ topics: - google-maps - google-maps-flutter - map + +dependency_overrides: + google_maps_flutter_platform_interface: + path: ../google_maps_flutter_platform_interface 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 23c7fd0c9fac..3440eb7a81fe 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 @@ -1348,6 +1348,34 @@ void main() { reason: 'Should pass mapId on PlatformView creation message', ); }); + + test('onPoiTap sends events to correct stream', () async { + const mapId = 1; + final fakePosition = PlatformLatLng(latitude: 12.34, longitude: 56.78); + const fakePlaceId = 'iso_id_123'; + + final maps = GoogleMapsFlutterIOS(); + // Initialize the handler which receives messages from the native side + final HostMapMessageHandler callbackHandler = maps.ensureHandlerInitialized( + mapId, + ); + + final stream = StreamQueue(maps.onPoiTap(mapId: mapId)); + + // Simulate a message from the native side via the Pigeon generated handler + callbackHandler.onPoiTap( + PlatformPointOfInterest(position: fakePosition, placeId: fakePlaceId), + ); + + // Verify the event in the stream + final MapPoiTapEvent event = await stream.next; + expect(event.mapId, mapId); + + final PointOfInterest poi = event.value; + expect(poi.position.latitude, fakePosition.latitude); + expect(poi.position.longitude, fakePosition.longitude); + expect(poi.placeId, fakePlaceId); + }); } void _expectColorsEqual(PlatformColor actual, Color expected) { 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 163cb160043b..be29338d9f15 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,3 +1,8 @@ +## 2.15.0 + +* Adds `PointOfInterest` type. +* Adds `MapPoiTapEvent` to support point-of-interest tap events. + ## 2.14.1 * Replaces internal use of deprecated methods. 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..ead7c7b06832 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 @@ -128,6 +128,12 @@ class MarkerDragEndEvent extends _PositionedMapEvent { MarkerDragEndEvent(super.mapId, super.position, super.markerId); } +/// An event fired when a [PointOfInterest] is tapped. +class MapPoiTapEvent extends MapEvent { + /// Creates a [MapPoiTapEvent]. + MapPoiTapEvent(super.mapId, super.value); +} + /// An event fired when a [Polyline] is tapped. class PolylineTapEvent extends MapEvent { /// Build an PolylineTap 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 f9ad5d8def7b..e1d26518ca10 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 @@ -143,6 +143,11 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPoiTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Stream onPolylineTap({required int mapId}) { return _events(mapId).whereType(); @@ -219,6 +224,19 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { MarkerId(arguments['markerId']! as String), ), ); + + case 'map#onPoiTap': + final Map arguments = _getArgumentDictionary(call); + _mapEventStreamController.add( + MapPoiTapEvent( + mapId, + PointOfInterest( + position: LatLng.fromJson(arguments['position'])!, + placeId: arguments['placeId']! as String, + ), + ), + ); + case 'infoWindow#onTap': final Map arguments = _getArgumentDictionary(call); _mapEventStreamController.add( 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 06920d3ea741..a55b0caa7af3 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 @@ -369,6 +369,11 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('onMarkerDragEnd() has not been implemented.'); } + /// A [PointOfInterest] has been tapped. + Stream onPoiTap({required int mapId}) { + return const Stream.empty(); + } + /// A [Polyline] has been tapped. Stream onPolylineTap({required int mapId}) { throw UnimplementedError('onPolylineTap() has not been implemented.'); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/point_of_interest.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/point_of_interest.dart new file mode 100644 index 000000000000..1d2b900d7dd9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/point_of_interest.dart @@ -0,0 +1,43 @@ +// 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; + +import 'types.dart'; + +/// A point of interest (POI) on the map, such as a park, school, or business. +/// +/// This object is returned when a user taps on a POI on the map. +@immutable +class PointOfInterest { + /// Creates an immutable representation of a point of interest. + const PointOfInterest({required this.position, required this.placeId}); + + /// The geographical location of the POI. + final LatLng position; + + /// The unique Place ID defined by Google (e.g., "ChIJj61dQgK6j4AR4GeTYWZsKWw"). + final String placeId; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is PointOfInterest && + position == other.position && + placeId == other.placeId; + } + + @override + int get hashCode => Object.hash(position, placeId); + + @override + String toString() { + return 'PointOfInterest{position: $position, placeId: $placeId}'; + } +} 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 a2483a24c544..a48e2eefcdae 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 @@ -27,6 +27,7 @@ export 'maps_object_updates.dart'; export 'marker.dart'; export 'marker_updates.dart'; export 'pattern_item.dart'; +export 'point_of_interest.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 9aa5ed828406..92bf349686d9 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.14.1 +version: 2.15.0 environment: sdk: ^3.8.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 83d9adfe247d..5da820c537bc 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 @@ -138,5 +138,34 @@ void main() { equals('drag-end-marker'), ); }); + test('onPoiTap', () async { + final platform = MethodChannelGoogleMapsFlutter(); + const mapId = 0; + platform.ensureChannelInitialized(mapId); + + final events = []; + platform + .onPoiTap(mapId: mapId) + .listen((MapPoiTapEvent event) => events.add(event)); + + final poiData = { + 'placeId': 'place123', + 'position': [37.422, -122.084], + }; + + await platform + .ensureChannelInitialized(mapId) + .binaryMessenger + .handlePlatformMessage( + 'plugins.flutter.io/google_maps_0', + const StandardMethodCodec().encodeMethodCall( + MethodCall('map#onPoiTap', poiData), + ), + (ByteData? data) {}, + ); + + expect(events.length, 1); + expect(events[0].value.placeId, 'place123'); + }); }); } 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 1f72413a9485..b5fc1bce2888 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 @@ -154,6 +154,13 @@ void main() { }, ); }); + + test('onPoiTap default implementation returns an empty stream', () { + final platform = ExtendsGoogleMapsFlutterPlatform(); + // The default implementation should now return a stream, not throw. + final Stream stream = platform.onPoiTap(mapId: 0); + expect(stream, isA>()); + }); } class GoogleMapsFlutterPlatformMock extends Mock diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/point_of_interest_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/point_of_interest_test.dart new file mode 100644 index 000000000000..631b4c89a8a2 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/point_of_interest_test.dart @@ -0,0 +1,35 @@ +// 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() { + group('PointOfInterest', () { + test('constructor with all named parameters', () { + const poi = PointOfInterest( + position: LatLng(10.0, 20.0), + placeId: 'test_id', + ); + expect(poi.position, const LatLng(10.0, 20.0)); + expect(poi.placeId, 'test_id'); + }); + + test('equality', () { + const poi1 = PointOfInterest(position: LatLng(10.0, 20.0), placeId: 'ID'); + const poi2 = PointOfInterest(position: LatLng(10.0, 20.0), placeId: 'ID'); + const poi3 = PointOfInterest(position: LatLng(10.1, 20.0), placeId: 'ID'); + + expect(poi1, poi2); + expect(poi1, isNot(poi3)); + }); + + test('hashCode', () { + const poi1 = PointOfInterest(position: LatLng(10.0, 20.0), placeId: 'ID'); + const poi2 = PointOfInterest(position: LatLng(10.0, 20.0), placeId: 'ID'); + + expect(poi1.hashCode, poi2.hashCode); + }); + }); +} 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 149dba550204..543b32c2d353 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,6 +1,6 @@ -## NEXT +## 0.5.15 -* Updates minimum supported SDK version to Flutter 3.35/Dart 3.9. +* Added support for Tap detection on Point of Interest. ## 0.5.14+3 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/poi_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/poi_test.dart new file mode 100644 index 000000000000..81fd56c0ca8b --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/poi_test.dart @@ -0,0 +1,103 @@ +// 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 'dart:async'; +import 'dart:js_interop'; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:google_maps_flutter_web/src/utils.dart'; +import 'package:integration_test/integration_test.dart'; + +@JS() +@anonymous +extension type FakeIconMouseEvent._(JSObject _) implements JSObject { + external factory FakeIconMouseEvent({ + gmaps.LatLng? latLng, + String? placeId, + JSFunction? stop, + }); +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('POI Tap Events', () { + late GoogleMapController controller; + late StreamController> stream; + late gmaps.Map map; + + setUp(() { + stream = StreamController>.broadcast(); + map = gmaps.Map(createDivElement()); + + controller = GoogleMapController( + mapId: 1, + streamController: stream, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: CameraPosition(target: LatLng(0, 0)), + textDirection: TextDirection.ltr, + ), + ); + + controller.debugSetOverrides(createMap: (_, __) => map); + controller.init(); + }); + + tearDown(() { + controller.dispose(); + }); + + testWidgets('Emits MapPoiTapEvent when clicking a POI', ( + WidgetTester tester, + ) async { + final latLng = gmaps.LatLng(10, 20); + bool? stopCalled = false; + + final event = FakeIconMouseEvent( + latLng: latLng, + placeId: 'test_place_id', + stop: (() { + stopCalled = true; + }).toJS, + ); + gmaps.event.trigger(map, 'click', event as JSAny); + + final MapEvent emittedEvent = await stream.stream.first; + + expect(emittedEvent, isA()); + final poiEvent = emittedEvent as MapPoiTapEvent; + + expect(poiEvent.mapId, 1); + expect(poiEvent.value.placeId, 'test_place_id'); + expect(poiEvent.value.position.latitude, 10); + expect(poiEvent.value.position.longitude, 20); + + expect(stopCalled, isTrue); + }); + + testWidgets('Emits MapTapEvent when clicking (no POI)', ( + WidgetTester tester, + ) async { + final latLng = gmaps.LatLng(30, 40); + final event = gmaps.MapMouseEvent()..latLng = latLng; + + gmaps.event.trigger(map, 'click', event); + + final MapEvent emittedEvent = await stream.stream.first; + + expect(emittedEvent, isA()); + final tapEvent = emittedEvent as MapTapEvent; + + expect(tapEvent.mapId, 1); + expect(tapEvent.position.latitude, 30); + expect(tapEvent.position.longitude, 40); + + expect(emittedEvent, isNot(isA())); + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml index a82a05b9be17..532c63cbba21 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml @@ -29,8 +29,8 @@ flutter: - assets/ dependency_overrides: - # Override the google_maps_flutter dependency on google_maps_flutter_web. - # TODO(ditman): Unwind the circular dependency. This will create problems - # if we need to make a breaking change to google_maps_flutter_web. + google_maps_flutter_platform_interface: + path: ../../google_maps_flutter_platform_interface google_maps_flutter_web: path: ../ + \ No newline at end of file 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 8e862f6e3dd7..b16639f9897d 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 @@ -260,9 +260,27 @@ class GoogleMapController { ) { assert(event.latLng != null); if (!_streamController.isClosed) { - _streamController.add( - MapTapEvent(_mapId, gmLatLngToLatLng(event.latLng!)), - ); + if (event is gmaps.IconMouseEvent && event.placeId != null) { + final String placeId = event.placeId!; + + // Stop the event to prevent the default Google Maps InfoWindow from popping up + event.stop(); + + _streamController.add( + MapPoiTapEvent( + _mapId, + PointOfInterest( + position: gmLatLngToLatLng(event.latLng!), + placeId: placeId, + ), + ), + ); + } else { + // If no placeId, treat it as a standard map tap + _streamController.add( + MapTapEvent(_mapId, gmLatLngToLatLng(event.latLng!)), + ); + } } }); _onRightClickSubscription = map.onRightclick.listen(( 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 e980252e7c89..fd83749d4c21 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 @@ -289,6 +289,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onPoiTap({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 f87b3ea66e81..ba40d8618ea1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,11 +2,11 @@ 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.5.14+3 +version: 0.5.15 environment: - sdk: ^3.9.0 - flutter: ">=3.35.0" + sdk: ^3.8.0 + flutter: ">=3.32.0" flutter: plugin: @@ -23,7 +23,7 @@ dependencies: flutter_web_plugins: sdk: flutter google_maps: ^8.1.0 - google_maps_flutter_platform_interface: ^2.14.0 + google_maps_flutter_platform_interface: ^2.15.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 web: ">=0.5.1 <2.0.0" @@ -40,3 +40,14 @@ topics: # The example deliberately includes limited-use secrets. false_secrets: - /example/web/index.html + +dependency_overrides: + google_maps_flutter: + path: ../google_maps_flutter + google_maps_flutter_android: + path: ../google_maps_flutter_android + google_maps_flutter_ios: + path: ../google_maps_flutter_ios + google_maps_flutter_platform_interface: + path: ../google_maps_flutter_platform_interface +