[google_maps_flutter] Federate mobile implementations (#6171)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 33094a2..d1b3525 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -57,7 +57,7 @@
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
- directory: "/packages/google_maps_flutter/google_maps_flutter/android"
+ directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app"
commit-message:
prefix: "[google_maps]"
schedule:
@@ -65,7 +65,15 @@
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
- directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app"
+ directory: "/packages/google_maps_flutter/google_maps_flutter_android/android"
+ commit-message:
+ prefix: "[google_maps]"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
+
+ - package-ecosystem: "gradle"
+ directory: "/packages/google_maps_flutter/google_maps_flutter_android/example/android/app"
commit-message:
prefix: "[google_maps]"
schedule:
diff --git a/CODEOWNERS b/CODEOWNERS
index 9efe4a5..db8187d 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -30,7 +30,7 @@
packages/camera/camera_android/** @camsim99
packages/espresso/** @GaryQian
packages/flutter_plugin_android_lifecycle/** @GaryQian
-packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian
+packages/google_maps_flutter/google_maps_flutter_android/** @GaryQian
packages/google_sign_in/google_sign_in_android/** @camsim99
packages/image_picker/image_picker_android/** @GaryQian
packages/in_app_purchase/in_app_purchase_android/** @GaryQian
@@ -42,7 +42,7 @@
# - iOS
packages/camera/camera_avfoundation/** @hellohuanlin
-packages/google_maps_flutter/google_maps_flutter/ios/** @cyanglaz
+packages/google_maps_flutter/google_maps_flutter_ios/** @cyanglaz
packages/google_sign_in/google_sign_in_ios/** @jmagman
packages/image_picker/image_picker_ios/** @cyanglaz
packages/in_app_purchase/in_app_purchase_storekit/** @cyanglaz
diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
index 9d7d542..86cefdd 100644
--- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
@@ -1,3 +1,7 @@
+## NEXT
+
+* Moves Android and iOS implementations to federated packages.
+
## 2.1.10
* Avoids map shift when scrolling on iOS.
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter/android/settings.gradle
deleted file mode 100644
index dbceadf..0000000
--- a/packages/google_maps_flutter/google_maps_flutter/android/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-rootProject.name = 'google_maps_flutter'
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart
index d82a582..1d7494c 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart
+++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart
@@ -13,8 +13,6 @@
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
import 'package:integration_test/integration_test.dart';
-import 'google_map_inspector.dart';
-
const LatLng _kInitialMapCenter = LatLng(0, 0);
const double _kInitialZoomLevel = 5;
const CameraPosition _kInitialCameraPosition =
@@ -22,16 +20,7 @@
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
-
- // TODO(stuartmorgan): Remove this once mobile implementations are federated
- // and registering their own inpector implementations, and just call
- // enableDebugInspection.
- if (GoogleMapsFlutterPlatform.instance is MethodChannelGoogleMapsFlutter) {
- GoogleMapsInspectorPlatform.instance = MethodChannelGoogleMapsInspector(
- GoogleMapsFlutterPlatform.instance as MethodChannelGoogleMapsFlutter);
- } else {
- GoogleMapsFlutterPlatform.instance.enableDebugInspection();
- }
+ GoogleMapsFlutterPlatform.instance.enableDebugInspection();
testWidgets('testCompassToggle', (WidgetTester tester) async {
final Key key = GlobalKey();
@@ -474,7 +463,6 @@
.round());
}
await tester.binding.setSurfaceSize(null);
- AndroidGoogleMapsFlutter.useAndroidViewSurface = false;
});
testWidgets('testGetVisibleRegion', (WidgetTester tester) async {
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile
index 14b4bdc..8df8fef 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile
+++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile
@@ -29,14 +29,6 @@
target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
- target 'RunnerTests' do
- inherit! :search_paths
-
- pod 'OCMock', '~> 3.9.1'
- end
- target 'RunnerUITests' do
- inherit! :search_paths
- end
end
post_install do |installer|
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap
deleted file mode 100644
index 19513f4..0000000
--- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap
+++ /dev/null
@@ -1,10 +0,0 @@
-framework module google_maps_flutter {
- umbrella header "google_maps_flutter-umbrella.h"
-
- export *
- module * { export * }
-
- explicit module Test {
- header "GoogleMapController_Test.h"
- }
-}
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 751930a..a4be120 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
@@ -13,6 +13,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
export 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'
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 b76d103..e037d7e 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
@@ -40,6 +40,9 @@
}
/// Android specific settings for [GoogleMap].
+// TODO(stuartmorgan): Deprecate this in favor of pointing people who want to
+// change this to using the Android implementation Dart class directly. This
+// should be done as part of switching the default to hybrid composition.
class AndroidGoogleMapsFlutter {
AndroidGoogleMapsFlutter._();
@@ -55,7 +58,7 @@
static bool get useAndroidViewSurface {
final GoogleMapsFlutterPlatform platform =
GoogleMapsFlutterPlatform.instance;
- if (platform is MethodChannelGoogleMapsFlutter) {
+ if (platform is GoogleMapsFlutterAndroid) {
return platform.useAndroidViewSurface;
}
return false;
@@ -73,7 +76,7 @@
static set useAndroidViewSurface(bool useAndroidViewSurface) {
final GoogleMapsFlutterPlatform platform =
GoogleMapsFlutterPlatform.instance;
- if (platform is MethodChannelGoogleMapsFlutter) {
+ if (platform is GoogleMapsFlutterAndroid) {
platform.useAndroidViewSurface = useAndroidViewSurface;
}
}
diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
index b0cff92..8d4e49f 100644
--- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
@@ -3,6 +3,9 @@
repository: https://github.com/flutter/plugins/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.1.10
+# Temporarily disable publishing to allow moving Android and iOS
+# implementations.
+publish_to: none
environment:
sdk: ">=2.14.0 <3.0.0"
@@ -12,15 +15,18 @@
plugin:
platforms:
android:
- package: io.flutter.plugins.googlemaps
- pluginClass: GoogleMapsPlugin
+ default_package: google_maps_flutter_android
ios:
- pluginClass: FLTGoogleMapsPlugin
+ default_package: google_maps_flutter_ios
dependencies:
flutter:
sdk: flutter
- flutter_plugin_android_lifecycle: ^2.0.1
+ # Temporary path dependencies to allow moving Android and iOS implementations.
+ google_maps_flutter_android:
+ path: ../google_maps_flutter_android
+ google_maps_flutter_ios:
+ path: ../google_maps_flutter_ios
google_maps_flutter_platform_interface: ^2.2.1
dev_dependencies:
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 003ae06..2b754af 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
@@ -6,7 +6,6 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
-import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
import 'fake_maps_controllers.dart';
@@ -588,38 +587,4 @@
expect(platformGoogleMap.buildingsEnabled, true);
});
-
- testWidgets(
- 'Default Android widget is AndroidView',
- (WidgetTester tester) async {
- await tester.pumpWidget(
- const Directionality(
- textDirection: TextDirection.ltr,
- child: GoogleMap(
- initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)),
- ),
- ),
- );
-
- expect(find.byType(AndroidView), findsOneWidget);
- },
- );
-
- testWidgets('Use PlatformViewLink on Android', (WidgetTester tester) async {
- final MethodChannelGoogleMapsFlutter platform =
- GoogleMapsFlutterPlatform.instance as MethodChannelGoogleMapsFlutter;
- platform.useAndroidViewSurface = true;
-
- await tester.pumpWidget(
- const Directionality(
- textDirection: TextDirection.ltr,
- child: GoogleMap(
- initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)),
- ),
- ),
- );
-
- expect(find.byType(PlatformViewLink), findsOneWidget);
- platform.useAndroidViewSurface = false;
- });
}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/AUTHORS b/packages/google_maps_flutter/google_maps_flutter_android/AUTHORS
new file mode 100644
index 0000000..9f1b53e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/AUTHORS
@@ -0,0 +1,67 @@
+# Below is a list of people and organizations that have contributed
+# to the Flutter project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
+The Chromium Authors
+German Saprykin <saprykin.h@gmail.com>
+Benjamin Sauer <sauer.benjamin@gmail.com>
+larsenthomasj@gmail.com
+Ali Bitek <alibitek@protonmail.ch>
+Pol Batlló <pol.batllo@gmail.com>
+Anatoly Pulyaevskiy
+Hayden Flinner <haydenflinner@gmail.com>
+Stefano Rodriguez <hlsroddy@gmail.com>
+Salvatore Giordano <salvatoregiordanoo@gmail.com>
+Brian Armstrong <brian@flutter.institute>
+Paul DeMarco <paulmdemarco@gmail.com>
+Fabricio Nogueira <feufeu@gmail.com>
+Simon Lightfoot <simon@devangels.london>
+Ashton Thomas <ashton@acrinta.com>
+Thomas Danner <thmsdnnr@gmail.com>
+Diego Velásquez <diego.velasquez.lopez@gmail.com>
+Hajime Nakamura <nkmrhj@gmail.com>
+Tuyển Vũ Xuân <netsoft1985@gmail.com>
+Miguel Ruivo <miguel@miguelruivo.com>
+Sarthak Verma <sarthak@artiosys.com>
+Mike Diarmid <mike@invertase.io>
+Invertase <oss@invertase.io>
+Elliot Hesp <elliot@invertase.io>
+Vince Varga <vince.varga@smaho.com>
+Aawaz Gyawali <awazgyawali@gmail.com>
+EUI Limited <ian.evans3@admiralgroup.co.uk>
+Katarina Sheremet <katarina@sheremet.ch>
+Thomas Stockx <thomas@stockxit.com>
+Sarbagya Dhaubanjar <sarbagyastha@gmail.com>
+Ozkan Eksi <ozeksi@gmail.com>
+Rishab Nayak <rishab@bu.edu>
+ko2ic <ko2ic.dev@gmail.com>
+Jonathan Younger <jonathan@daikini.com>
+Jose Sanchez <josesm82@gmail.com>
+Debkanchan Samadder <debu.samadder@gmail.com>
+Audrius Karosevicius <audrius.karosevicius@gmail.com>
+Lukasz Piliszczuk <lukasz@intheloup.io>
+SoundReply Solutions GmbH <ch@soundreply.com>
+Rafal Wachol <rwachol@gmail.com>
+Pau Picas <pau.picas@gmail.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Alexandru Tuca <salexandru.tuca@outlook.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Rhodes Davis Jr. <rody.davis.jr@gmail.com>
+Luigi Agosti <luigi@tengio.com>
+Quentin Le Guennec <quentin@tengio.com>
+Koushik Ravikumar <koushik@tengio.com>
+Nissim Dsilva <nissim@tengio.com>
+Giancarlo Rocha <giancarloiff@gmail.com>
+Ryo Miyake <ryo@miyake.id>
+Théo Champion <contact.theochampion@gmail.com>
+Kazuki Yamaguchi <y.kazuki0614n@gmail.com>
+Eitan Schwartz <eshvartz@gmail.com>
+Chris Rutkowski <chrisrutkowski89@gmail.com>
+Juan Alvarez <juan.alvarez@resideo.com>
+Aleksandr Yurkovskiy <sanekyy@gmail.com>
+Anton Borries <mail@antonborri.es>
+Alex Li <google@alexv525.com>
+Rahul Raj <64.rahulraj@gmail.com>
+Taha Tesser <tesser@gmail.com>
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md
new file mode 100644
index 0000000..bd2f99c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md
@@ -0,0 +1,4 @@
+## 2.1.10
+
+* Splits Android implementation out of `google_maps_flutter` as a federated
+ implementation.
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/LICENSE b/packages/google_maps_flutter/google_maps_flutter_android/LICENSE
new file mode 100644
index 0000000..c6823b8
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2013 The Flutter Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/README.md b/packages/google_maps_flutter/google_maps_flutter_android/README.md
new file mode 100644
index 0000000..5ac1df0
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/README.md
@@ -0,0 +1,12 @@
+# google\_maps\_flutter\_android
+
+The Android implementation of [`google_maps_flutter`][1].
+
+## Usage
+
+This package is [endorsed][2], which means you can simply use
+`google_maps_flutter` normally. This package will be automatically included in
+your app when you do.
+
+[1]: https://pub.dev/packages/google_maps_flutter
+[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/build.gradle
rename to packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/settings.gradle
new file mode 100644
index 0000000..d873c7a
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'google_maps_flutter_android'
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/AndroidManifest.xml b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/AndroidManifest.xml
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/AndroidManifest.xml
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/AndroidManifest.xml
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/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
similarity index 99%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
index 2c2287c..66d3e28 100644
--- a/packages/google_maps_flutter/google_maps_flutter/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
@@ -96,7 +96,8 @@
this.options = options;
this.mapView = new MapView(context, options);
this.density = context.getResources().getDisplayMetrics().density;
- methodChannel = new MethodChannel(binaryMessenger, "plugins.flutter.io/google_maps_" + id);
+ methodChannel =
+ new MethodChannel(binaryMessenger, "plugins.flutter.dev/google_maps_android_" + id);
methodChannel.setMethodCallHandler(this);
this.lifecycleProvider = lifecycleProvider;
this.markersController = new MarkersController(methodChannel);
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java
similarity index 98%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java
index 763cd9e..715b357 100644
--- a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java
+++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java
@@ -28,7 +28,7 @@
@Nullable private Lifecycle lifecycle;
- private static final String VIEW_TYPE = "plugins.flutter.io/google_maps";
+ private static final String VIEW_TYPE = "plugins.flutter.dev/google_maps_android";
@SuppressWarnings("deprecation")
public static void registerWith(
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/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
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/.metadata b/packages/google_maps_flutter/google_maps_flutter_android/example/.metadata
new file mode 100644
index 0000000..46e884c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/.metadata
@@ -0,0 +1,8 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 3ea4d06340a97a1e9d7cae97567c64e0569dcaa2
+ channel: beta
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/README.md b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md
new file mode 100644
index 0000000..c885264
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md
@@ -0,0 +1,3 @@
+# google_maps_flutter_example
+
+Demonstrates how to use the google_maps_flutter plugin.
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle
new file mode 100644
index 0000000..f6d29f6
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle
@@ -0,0 +1,73 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 31
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ applicationId "io.flutter.plugins.googlemapsexample"
+ minSdkVersion 20
+ targetSdkVersion 28
+ multiDexEnabled true
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ defaultConfig {
+ manifestPlaceholders = [mapsApiKey: "$System.env.MAPS_API_KEY"]
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+
+ testOptions {
+ unitTests {
+ includeAndroidResources = true
+ }
+ }
+
+ dependencies {
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test:runner:1.2.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ api 'androidx.test:core:1.2.0'
+ testImplementation 'com.google.android.gms:play-services-maps:17.0.0'
+ }
+}
+
+flutter {
+ source '../..'
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..9a4163a
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java
new file mode 100644
index 0000000..0f4298d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java
@@ -0,0 +1,14 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface DartIntegrationTest {}
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java
rename to packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/MainActivityTest.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/MainActivityTest.java
new file mode 100644
index 0000000..244a22b
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/MainActivityTest.java
@@ -0,0 +1,19 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.googlemaps;
+
+import androidx.test.rule.ActivityTestRule;
+import dev.flutter.plugins.integration_test.FlutterTestRunner;
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.plugins.DartIntegrationTest;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@DartIntegrationTest
+@RunWith(FlutterTestRunner.class)
+public class MainActivityTest {
+ @Rule
+ public ActivityTestRule<FlutterActivity> rule = new ActivityTestRule<>(FlutterActivity.class);
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/AndroidManifest.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..9c1f83d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="io.flutter.plugins.googlemapsexample">
+ <!-- Flutter needs internet permission to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <application android:usesCleartextTraffic="true">
+ <activity
+ android:name=".GoogleMapsTestActivity"
+ android:launchMode="singleTop"
+ android:theme="@style/LaunchTheme"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/java/io/flutter/plugins/googlemapsexample/GoogleMapsTestActivity.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/java/io/flutter/plugins/googlemapsexample/GoogleMapsTestActivity.java
new file mode 100644
index 0000000..e183a7c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/java/io/flutter/plugins/googlemapsexample/GoogleMapsTestActivity.java
@@ -0,0 +1,20 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.googlemapsexample;
+
+import androidx.annotation.NonNull;
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.embedding.engine.FlutterEngine;
+
+// Makes the FlutterEngine accessible for testing.
+public class GoogleMapsTestActivity extends FlutterActivity {
+ public FlutterEngine engine;
+
+ @Override
+ public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
+ super.configureFlutterEngine(flutterEngine);
+ engine = flutterEngine;
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/AndroidManifest.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..815074b
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="io.flutter.plugins.googlemapsexample">
+
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+
+ <application android:label="google_maps_flutter_example" android:icon="@mipmap/ic_launcher">
+ <meta-data
+ android:name="com.google.android.gms.version"
+ android:value="@integer/google_play_services_version" />
+ <!-- Update this value to your google maps api key. -->
+ <meta-data
+ android:name="com.google.android.geo.API_KEY"
+ android:value="${mapsApiKey}" />
+ <activity android:name="io.flutter.embedding.android.FlutterActivity"
+ android:theme="@style/LaunchTheme"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ <meta-data
+ android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
+ android:value="true" />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <meta-data android:name="flutterEmbedding" android:value="2"/>
+ </application>
+</manifest>
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/drawable/launch_background.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@android:color/white" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/values/styles.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..00fa441
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ Flutter draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+</resources>
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle
new file mode 100644
index 0000000..4d8d45d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.4'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle.properties
new file mode 100644
index 0000000..207beb6
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle.properties
@@ -0,0 +1,5 @@
+org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
+
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..9ec7236
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle
new file mode 100644
index 0000000..5a2f14f
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle
@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+ pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+}
+
+plugins.each { name, path ->
+ def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+ include ":$name"
+ project(":$name").projectDir = pluginDirectory
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/2.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/2.0x/red_square.png
new file mode 100644
index 0000000..0f82237
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/2.0x/red_square.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/3.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/3.0x/red_square.png
new file mode 100644
index 0000000..7e27399
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/3.0x/red_square.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/night_mode.json b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/night_mode.json
new file mode 100644
index 0000000..1f16e00
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/night_mode.json
@@ -0,0 +1,162 @@
+[
+ {
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#242f3e"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#746855"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.stroke",
+ "stylers": [
+ {
+ "color": "#242f3e"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative.locality",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#263c3f"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#6b9a76"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#38414e"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry.stroke",
+ "stylers": [
+ {
+ "color": "#212a37"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#9ca5b3"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#746855"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry.stroke",
+ "stylers": [
+ {
+ "color": "#1f2835"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#f3d19c"
+ }
+ ]
+ },
+ {
+ "featureType": "transit",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#2f3948"
+ }
+ ]
+ },
+ {
+ "featureType": "transit.station",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#17263c"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#515c6d"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.stroke",
+ "stylers": [
+ {
+ "color": "#17263c"
+ }
+ ]
+ }
+]
+
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/red_square.png b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/red_square.png
new file mode 100644
index 0000000..650a2de
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/red_square.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart
new file mode 100644
index 0000000..00c51eb
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart
@@ -0,0 +1,1189 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data';
+import 'dart:ui' as ui;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:google_maps_flutter_example/example_google_map.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+import 'package:integration_test/integration_test.dart';
+
+const LatLng _kInitialMapCenter = LatLng(0, 0);
+const double _kInitialZoomLevel = 5;
+const CameraPosition _kInitialCameraPosition =
+ CameraPosition(target: _kInitialMapCenter, zoom: _kInitialZoomLevel);
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+ GoogleMapsFlutterPlatform.instance.enableDebugInspection();
+
+ testWidgets('testCompassToggle', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ compassEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool compassEnabled = await inspector.isCompassEnabled(mapId: mapId);
+ expect(compassEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ compassEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ compassEnabled = await inspector.isCompassEnabled(mapId: mapId);
+ expect(compassEnabled, true);
+ });
+
+ testWidgets('testMapToolbarToggle', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ mapToolbarEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool mapToolbarEnabled = await inspector.isMapToolbarEnabled(mapId: mapId);
+ expect(mapToolbarEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ mapToolbarEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ mapToolbarEnabled = await inspector.isMapToolbarEnabled(mapId: mapId);
+ expect(mapToolbarEnabled, true);
+ });
+
+ testWidgets('updateMinMaxZoomLevels', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ const MinMaxZoomPreference initialZoomLevel = MinMaxZoomPreference(4, 8);
+ const MinMaxZoomPreference finalZoomLevel = MinMaxZoomPreference(6, 10);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ minMaxZoomPreference: initialZoomLevel,
+ onMapCreated: (ExampleGoogleMapController c) async {
+ controllerCompleter.complete(c);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ // On Android, zooming with zoomTo is constrained by the min/max.
+ await controller.moveCamera(CameraUpdate.zoomTo(15));
+ await tester.pumpAndSettle();
+ double? zoomLevel = await controller.getZoomLevel();
+ expect(zoomLevel, equals(initialZoomLevel.maxZoom));
+
+ await controller.moveCamera(CameraUpdate.zoomTo(1));
+ await tester.pumpAndSettle();
+ zoomLevel = await controller.getZoomLevel();
+ expect(zoomLevel, equals(initialZoomLevel.minZoom));
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ minMaxZoomPreference: finalZoomLevel,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ await controller.moveCamera(CameraUpdate.zoomTo(15));
+ await tester.pumpAndSettle();
+ zoomLevel = await controller.getZoomLevel();
+ expect(zoomLevel, equals(finalZoomLevel.maxZoom));
+
+ await controller.moveCamera(CameraUpdate.zoomTo(1));
+ await tester.pumpAndSettle();
+ zoomLevel = await controller.getZoomLevel();
+ expect(zoomLevel, equals(finalZoomLevel.minZoom));
+ });
+
+ testWidgets('testZoomGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ zoomGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool zoomGesturesEnabled =
+ await inspector.areZoomGesturesEnabled(mapId: mapId);
+ expect(zoomGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ zoomGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ zoomGesturesEnabled = await inspector.areZoomGesturesEnabled(mapId: mapId);
+ expect(zoomGesturesEnabled, true);
+ });
+
+ testWidgets('testZoomControlsEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool zoomControlsEnabled =
+ await inspector.areZoomControlsEnabled(mapId: mapId);
+ expect(zoomControlsEnabled, true);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ zoomControlsEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ zoomControlsEnabled = await inspector.areZoomControlsEnabled(mapId: mapId);
+ expect(zoomControlsEnabled, false);
+ });
+
+ testWidgets('testLiteModeEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ liteModeEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool liteModeEnabled = await inspector.isLiteModeEnabled(mapId: mapId);
+ expect(liteModeEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ liteModeEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ liteModeEnabled = await inspector.isLiteModeEnabled(mapId: mapId);
+ expect(liteModeEnabled, true);
+ });
+
+ testWidgets('testRotateGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ rotateGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool rotateGesturesEnabled =
+ await inspector.areRotateGesturesEnabled(mapId: mapId);
+ expect(rotateGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ rotateGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ rotateGesturesEnabled =
+ await inspector.areRotateGesturesEnabled(mapId: mapId);
+ expect(rotateGesturesEnabled, true);
+ });
+
+ testWidgets('testTiltGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tiltGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool tiltGesturesEnabled =
+ await inspector.areTiltGesturesEnabled(mapId: mapId);
+ expect(tiltGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tiltGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ tiltGesturesEnabled = await inspector.areTiltGesturesEnabled(mapId: mapId);
+ expect(tiltGesturesEnabled, true);
+ });
+
+ testWidgets('testScrollGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ scrollGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool scrollGesturesEnabled =
+ await inspector.areScrollGesturesEnabled(mapId: mapId);
+ expect(scrollGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ scrollGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ scrollGesturesEnabled =
+ await inspector.areScrollGesturesEnabled(mapId: mapId);
+ expect(scrollGesturesEnabled, true);
+ });
+
+ testWidgets('testInitialCenterLocationAtCenter', (WidgetTester tester) async {
+ await tester.binding.setSurfaceSize(const Size(800, 600));
+
+ final Completer<ExampleGoogleMapController> mapControllerCompleter =
+ Completer<ExampleGoogleMapController>();
+ final Key key = GlobalKey();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapControllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+ final ExampleGoogleMapController mapController =
+ await mapControllerCompleter.future;
+
+ await tester.pumpAndSettle();
+
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and
+ // `mapControllerCompleter.complete(controller)` above should happen in
+ // `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ final ScreenCoordinate coordinate =
+ await mapController.getScreenCoordinate(_kInitialCameraPosition.target);
+ final Rect rect = tester.getRect(find.byKey(key));
+ expect(
+ coordinate.x,
+ ((rect.center.dx - rect.topLeft.dx) *
+ tester.binding.window.devicePixelRatio)
+ .round());
+ expect(
+ coordinate.y,
+ ((rect.center.dy - rect.topLeft.dy) *
+ tester.binding.window.devicePixelRatio)
+ .round());
+ await tester.binding.setSurfaceSize(null);
+ });
+
+ testWidgets('testGetVisibleRegion', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final LatLngBounds zeroLatLngBounds = LatLngBounds(
+ southwest: const LatLng(0, 0), northeast: const LatLng(0, 0));
+
+ final Completer<ExampleGoogleMapController> mapControllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapControllerCompleter.complete(controller);
+ },
+ ),
+ ));
+ await tester.pumpAndSettle();
+
+ final ExampleGoogleMapController mapController =
+ await mapControllerCompleter.future;
+
+ final LatLngBounds firstVisibleRegion =
+ await mapController.getVisibleRegion();
+
+ expect(firstVisibleRegion, isNotNull);
+ expect(firstVisibleRegion.southwest, isNotNull);
+ expect(firstVisibleRegion.northeast, isNotNull);
+ expect(firstVisibleRegion, isNot(zeroLatLngBounds));
+ expect(firstVisibleRegion.contains(_kInitialMapCenter), isTrue);
+
+ // Making a new `LatLngBounds` about (10, 10) distance south west to the `firstVisibleRegion`.
+ // The size of the `LatLngBounds` is 10 by 10.
+ final LatLng southWest = LatLng(firstVisibleRegion.southwest.latitude - 20,
+ firstVisibleRegion.southwest.longitude - 20);
+ final LatLng northEast = LatLng(firstVisibleRegion.southwest.latitude - 10,
+ firstVisibleRegion.southwest.longitude - 10);
+ final LatLng newCenter = LatLng(
+ (northEast.latitude + southWest.latitude) / 2,
+ (northEast.longitude + southWest.longitude) / 2,
+ );
+
+ expect(firstVisibleRegion.contains(northEast), isFalse);
+ expect(firstVisibleRegion.contains(southWest), isFalse);
+
+ final LatLngBounds latLngBounds =
+ LatLngBounds(southwest: southWest, northeast: northEast);
+
+ // TODO(iskakaushik): non-zero padding is needed for some device configurations
+ // https://github.com/flutter/flutter/issues/30575
+ const double padding = 0;
+ await mapController
+ .moveCamera(CameraUpdate.newLatLngBounds(latLngBounds, padding));
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final LatLngBounds secondVisibleRegion =
+ await mapController.getVisibleRegion();
+
+ expect(secondVisibleRegion, isNotNull);
+ expect(secondVisibleRegion.southwest, isNotNull);
+ expect(secondVisibleRegion.northeast, isNotNull);
+ expect(secondVisibleRegion, isNot(zeroLatLngBounds));
+
+ expect(firstVisibleRegion, isNot(secondVisibleRegion));
+ expect(secondVisibleRegion.contains(newCenter), isTrue);
+ });
+
+ testWidgets('testTraffic', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ trafficEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId);
+ expect(isTrafficEnabled, true);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ trafficEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId);
+ expect(isTrafficEnabled, false);
+ });
+
+ testWidgets('testBuildings', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ buildingsEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool isBuildingsEnabled =
+ await inspector.areBuildingsEnabled(mapId: mapId);
+ expect(isBuildingsEnabled, true);
+ });
+
+ testWidgets('testMyLocationButtonToggle', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: true,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, true);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: false,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, false);
+ },
+ // Location button tests are skipped in Android because we don't have location permission to test.
+ skip: true);
+
+ testWidgets('testMyLocationButton initial value false',
+ (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: false,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, false);
+ },
+ // Location button tests are skipped in Android because we don't have location permission to test.
+ skip: true);
+
+ testWidgets('testMyLocationButton initial value true',
+ (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: true,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, true);
+ },
+ // Location button tests are skipped in Android because we don't have location permission to test.
+ skip: true);
+
+ testWidgets('testSetMapStyle valid Json String', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+ const String mapStyle =
+ '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]';
+ await controller.setMapStyle(mapStyle);
+ });
+
+ testWidgets('testSetMapStyle invalid Json String',
+ (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ try {
+ await controller.setMapStyle('invalid_value');
+ fail('expected MapStyleException');
+ } on MapStyleException catch (e) {
+ expect(e.cause, isNotNull);
+ }
+ });
+
+ testWidgets('testSetMapStyle null string', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+ await controller.setMapStyle(null);
+ });
+
+ testWidgets('testGetLatLng', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ final LatLngBounds visibleRegion = await controller.getVisibleRegion();
+ final LatLng topLeft =
+ await controller.getLatLng(const ScreenCoordinate(x: 0, y: 0));
+ final LatLng northWest = LatLng(
+ visibleRegion.northeast.latitude,
+ visibleRegion.southwest.longitude,
+ );
+
+ expect(topLeft, northWest);
+ });
+
+ testWidgets('testGetZoomLevel', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ double zoom = await controller.getZoomLevel();
+ expect(zoom, _kInitialZoomLevel);
+
+ await controller.moveCamera(CameraUpdate.zoomTo(7));
+ await tester.pumpAndSettle();
+ zoom = await controller.getZoomLevel();
+ expect(zoom, equals(7));
+ });
+
+ testWidgets('testScreenCoordinate', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ final LatLngBounds visibleRegion = await controller.getVisibleRegion();
+ final LatLng northWest = LatLng(
+ visibleRegion.northeast.latitude,
+ visibleRegion.southwest.longitude,
+ );
+ final ScreenCoordinate topLeft =
+ await controller.getScreenCoordinate(northWest);
+ expect(topLeft, const ScreenCoordinate(x: 0, y: 0));
+ });
+
+ testWidgets('testResizeWidget', (WidgetTester tester) async {
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+ final ExampleGoogleMap map = ExampleGoogleMap(
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) async {
+ controllerCompleter.complete(controller);
+ },
+ );
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: MaterialApp(
+ home: Scaffold(
+ body: SizedBox(height: 100, width: 100, child: map)))));
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: MaterialApp(
+ home: Scaffold(
+ body: SizedBox(height: 400, width: 400, child: map)))));
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ // Simple call to make sure that the app hasn't crashed.
+ final LatLngBounds bounds1 = await controller.getVisibleRegion();
+ final LatLngBounds bounds2 = await controller.getVisibleRegion();
+ expect(bounds1, bounds2);
+ });
+
+ testWidgets('testToggleInfoWindow', (WidgetTester tester) async {
+ const Marker marker = Marker(
+ markerId: MarkerId('marker'),
+ infoWindow: InfoWindow(title: 'InfoWindow'));
+ final Set<Marker> markers = <Marker>{marker};
+
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
+ markers: markers,
+ onMapCreated: (ExampleGoogleMapController googleMapController) {
+ controllerCompleter.complete(googleMapController);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ bool iwVisibleStatus =
+ await controller.isMarkerInfoWindowShown(marker.markerId);
+ expect(iwVisibleStatus, false);
+
+ await controller.showMarkerInfoWindow(marker.markerId);
+ iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId);
+ expect(iwVisibleStatus, true);
+
+ await controller.hideMarkerInfoWindow(marker.markerId);
+ iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId);
+ expect(iwVisibleStatus, false);
+ });
+
+ testWidgets('fromAssetImage', (WidgetTester tester) async {
+ const double pixelRatio = 2;
+ const ImageConfiguration imageConfiguration =
+ ImageConfiguration(devicePixelRatio: pixelRatio);
+ final BitmapDescriptor mip = await BitmapDescriptor.fromAssetImage(
+ imageConfiguration, 'red_square.png');
+ final BitmapDescriptor scaled = await BitmapDescriptor.fromAssetImage(
+ imageConfiguration, 'red_square.png',
+ mipmaps: false);
+ expect((mip.toJson() as List<dynamic>)[2], 1);
+ expect((scaled.toJson() as List<dynamic>)[2], 2);
+ });
+
+ testWidgets('testTakeSnapshot', (WidgetTester tester) async {
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+ final Uint8List? bytes = await controller.takeSnapshot();
+ expect(bytes?.isNotEmpty, true);
+ },
+ // TODO(cyanglaz): un-skip the test when we can test this on CI with API key enabled.
+ // https://github.com/flutter/flutter/issues/57057
+ skip: Platform.isAndroid);
+
+ testWidgets(
+ 'set tileOverlay correctly',
+ (WidgetTester tester) async {
+ final Completer<int> mapIdCompleter = Completer<int>();
+ final TileOverlay tileOverlay1 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 2,
+ visible: true,
+ transparency: 0.2,
+ fadeIn: true,
+ );
+
+ final TileOverlay tileOverlay2 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_2'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 1,
+ visible: false,
+ transparency: 0.3,
+ fadeIn: false,
+ );
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1, tileOverlay2},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ),
+ );
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+
+ final TileOverlay tileOverlayInfo1 = (await inspector
+ .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!;
+ final TileOverlay tileOverlayInfo2 = (await inspector
+ .getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId))!;
+
+ expect(tileOverlayInfo1.visible, isTrue);
+ expect(tileOverlayInfo1.fadeIn, isTrue);
+ expect(
+ tileOverlayInfo1.transparency, moreOrLessEquals(0.2, epsilon: 0.001));
+ expect(tileOverlayInfo1.zIndex, 2);
+
+ expect(tileOverlayInfo2.visible, isFalse);
+ expect(tileOverlayInfo2.fadeIn, isFalse);
+ expect(
+ tileOverlayInfo2.transparency, moreOrLessEquals(0.3, epsilon: 0.001));
+ expect(tileOverlayInfo2.zIndex, 1);
+ },
+ );
+
+ testWidgets(
+ 'update tileOverlays correctly',
+ (WidgetTester tester) async {
+ final Completer<int> mapIdCompleter = Completer<int>();
+ final Key key = GlobalKey();
+ final TileOverlay tileOverlay1 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 2,
+ visible: true,
+ transparency: 0.2,
+ fadeIn: true,
+ );
+
+ final TileOverlay tileOverlay2 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_2'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 3,
+ visible: true,
+ transparency: 0.5,
+ fadeIn: true,
+ );
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1, tileOverlay2},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ),
+ );
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+
+ final TileOverlay tileOverlay1New = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 1,
+ visible: false,
+ transparency: 0.3,
+ fadeIn: false,
+ );
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1New},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('update: OnMapCreated should get called only once.');
+ },
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final TileOverlay tileOverlayInfo1 = (await inspector
+ .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!;
+ final TileOverlay? tileOverlayInfo2 =
+ await inspector.getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId);
+
+ expect(tileOverlayInfo1.visible, isFalse);
+ expect(tileOverlayInfo1.fadeIn, isFalse);
+ expect(
+ tileOverlayInfo1.transparency, moreOrLessEquals(0.3, epsilon: 0.001));
+ expect(tileOverlayInfo1.zIndex, 1);
+
+ expect(tileOverlayInfo2, isNull);
+ },
+ );
+
+ testWidgets(
+ 'remove tileOverlays correctly',
+ (WidgetTester tester) async {
+ final Completer<int> mapIdCompleter = Completer<int>();
+ final Key key = GlobalKey();
+ final TileOverlay tileOverlay1 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 2,
+ visible: true,
+ transparency: 0.2,
+ fadeIn: true,
+ );
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ),
+ );
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+ final TileOverlay? tileOverlayInfo1 =
+ await inspector.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId);
+
+ expect(tileOverlayInfo1, isNull);
+ },
+ );
+}
+
+class _DebugTileProvider implements TileProvider {
+ _DebugTileProvider() {
+ boxPaint.isAntiAlias = true;
+ boxPaint.color = Colors.blue;
+ boxPaint.strokeWidth = 2.0;
+ boxPaint.style = PaintingStyle.stroke;
+ }
+
+ static const int width = 100;
+ static const int height = 100;
+ static final Paint boxPaint = Paint();
+ static const TextStyle textStyle = TextStyle(
+ color: Colors.red,
+ fontSize: 20,
+ );
+
+ @override
+ Future<Tile> getTile(int x, int y, int? zoom) async {
+ final ui.PictureRecorder recorder = ui.PictureRecorder();
+ final Canvas canvas = Canvas(recorder);
+ final TextSpan textSpan = TextSpan(
+ text: '$x,$y',
+ style: textStyle,
+ );
+ final TextPainter textPainter = TextPainter(
+ text: textSpan,
+ textDirection: TextDirection.ltr,
+ );
+ textPainter.layout(
+ minWidth: 0.0,
+ maxWidth: width.toDouble(),
+ );
+ const Offset offset = Offset(0, 0);
+ textPainter.paint(canvas, offset);
+ canvas.drawRect(
+ Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint);
+ final ui.Picture picture = recorder.endRecording();
+ final Uint8List byteData = await picture
+ .toImage(width, height)
+ .then((ui.Image image) =>
+ image.toByteData(format: ui.ImageByteFormat.png))
+ .then((ByteData? byteData) => byteData!.buffer.asUint8List());
+ return Tile(width, height, byteData);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/animate_camera.dart
new file mode 100644
index 0000000..c34a3ba
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/animate_camera.dart
@@ -0,0 +1,171 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class AnimateCameraPage extends GoogleMapExampleAppPage {
+ const AnimateCameraPage({Key? key})
+ : super(const Icon(Icons.map), 'Camera control, animated', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const AnimateCamera();
+ }
+}
+
+class AnimateCamera extends StatefulWidget {
+ const AnimateCamera({Key? key}) : super(key: key);
+ @override
+ State createState() => AnimateCameraState();
+}
+
+class AnimateCameraState extends State<AnimateCamera> {
+ ExampleGoogleMapController? mapController;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ mapController = controller;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition:
+ const CameraPosition(target: LatLng(0.0, 0.0)),
+ ),
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newCameraPosition(
+ const CameraPosition(
+ bearing: 270.0,
+ target: LatLng(51.5160895, -0.1294527),
+ tilt: 30.0,
+ zoom: 17.0,
+ ),
+ ),
+ );
+ },
+ child: const Text('newCameraPosition'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newLatLng(
+ const LatLng(56.1725505, 10.1850512),
+ ),
+ );
+ },
+ child: const Text('newLatLng'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newLatLngBounds(
+ LatLngBounds(
+ southwest: const LatLng(-38.483935, 113.248673),
+ northeast: const LatLng(-8.982446, 153.823821),
+ ),
+ 10.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngBounds'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newLatLngZoom(
+ const LatLng(37.4231613, -122.087159),
+ 11.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngZoom'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.scrollBy(150.0, -225.0),
+ );
+ },
+ child: const Text('scrollBy'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomBy(
+ -0.5,
+ const Offset(30.0, 20.0),
+ ),
+ );
+ },
+ child: const Text('zoomBy with focus'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomBy(-0.5),
+ );
+ },
+ child: const Text('zoomBy'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomIn(),
+ );
+ },
+ child: const Text('zoomIn'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomOut(),
+ );
+ },
+ child: const Text('zoomOut'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomTo(16.0),
+ );
+ },
+ child: const Text('zoomTo'),
+ ),
+ ],
+ ),
+ ],
+ )
+ ],
+ );
+ }
+}
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
new file mode 100644
index 0000000..e2c7131
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart
@@ -0,0 +1,538 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+// This is a pared down version of the Dart code from the app-facing package,
+// to allow running the same examples for package-local testing.
+// TODO(stuartmorgan): Consider extracting this to a shared package. See also
+// https://github.com/flutter/flutter/issues/46716.
+
+/// Controller for a single ExampleGoogleMap instance running on the host platform.
+class ExampleGoogleMapController {
+ ExampleGoogleMapController._(
+ this._googleMapState, {
+ required this.mapId,
+ }) {
+ _connectStreams(mapId);
+ }
+
+ /// The mapId for this controller
+ final int mapId;
+
+ /// Initialize control of a [ExampleGoogleMap] with [id].
+ ///
+ /// Mainly for internal use when instantiating a [ExampleGoogleMapController] passed
+ /// in [ExampleGoogleMap.onMapCreated] callback.
+ static Future<ExampleGoogleMapController> _init(
+ int id,
+ CameraPosition initialCameraPosition,
+ _ExampleGoogleMapState googleMapState,
+ ) async {
+ await GoogleMapsFlutterPlatform.instance.init(id);
+ return ExampleGoogleMapController._(
+ googleMapState,
+ mapId: id,
+ );
+ }
+
+ final _ExampleGoogleMapState _googleMapState;
+
+ void _connectStreams(int mapId) {
+ if (_googleMapState.widget.onCameraMoveStarted != null) {
+ GoogleMapsFlutterPlatform.instance
+ .onCameraMoveStarted(mapId: mapId)
+ .listen((_) => _googleMapState.widget.onCameraMoveStarted!());
+ }
+ if (_googleMapState.widget.onCameraMove != null) {
+ GoogleMapsFlutterPlatform.instance.onCameraMove(mapId: mapId).listen(
+ (CameraMoveEvent e) => _googleMapState.widget.onCameraMove!(e.value));
+ }
+ if (_googleMapState.widget.onCameraIdle != null) {
+ GoogleMapsFlutterPlatform.instance
+ .onCameraIdle(mapId: mapId)
+ .listen((_) => _googleMapState.widget.onCameraIdle!());
+ }
+ GoogleMapsFlutterPlatform.instance
+ .onMarkerTap(mapId: mapId)
+ .listen((MarkerTapEvent e) => _googleMapState.onMarkerTap(e.value));
+ GoogleMapsFlutterPlatform.instance.onMarkerDragStart(mapId: mapId).listen(
+ (MarkerDragStartEvent e) =>
+ _googleMapState.onMarkerDragStart(e.value, e.position));
+ GoogleMapsFlutterPlatform.instance.onMarkerDrag(mapId: mapId).listen(
+ (MarkerDragEvent e) =>
+ _googleMapState.onMarkerDrag(e.value, e.position));
+ GoogleMapsFlutterPlatform.instance.onMarkerDragEnd(mapId: mapId).listen(
+ (MarkerDragEndEvent e) =>
+ _googleMapState.onMarkerDragEnd(e.value, e.position));
+ GoogleMapsFlutterPlatform.instance.onInfoWindowTap(mapId: mapId).listen(
+ (InfoWindowTapEvent e) => _googleMapState.onInfoWindowTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onPolylineTap(mapId: mapId)
+ .listen((PolylineTapEvent e) => _googleMapState.onPolylineTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onPolygonTap(mapId: mapId)
+ .listen((PolygonTapEvent e) => _googleMapState.onPolygonTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onCircleTap(mapId: mapId)
+ .listen((CircleTapEvent e) => _googleMapState.onCircleTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onTap(mapId: mapId)
+ .listen((MapTapEvent e) => _googleMapState.onTap(e.position));
+ GoogleMapsFlutterPlatform.instance.onLongPress(mapId: mapId).listen(
+ (MapLongPressEvent e) => _googleMapState.onLongPress(e.position));
+ }
+
+ /// Updates configuration options of the map user interface.
+ Future<void> _updateMapConfiguration(MapConfiguration update) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateMapConfiguration(update, mapId: mapId);
+ }
+
+ /// Updates marker configuration.
+ Future<void> _updateMarkers(MarkerUpdates markerUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateMarkers(markerUpdates, mapId: mapId);
+ }
+
+ /// Updates polygon configuration.
+ Future<void> _updatePolygons(PolygonUpdates polygonUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updatePolygons(polygonUpdates, mapId: mapId);
+ }
+
+ /// Updates polyline configuration.
+ Future<void> _updatePolylines(PolylineUpdates polylineUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updatePolylines(polylineUpdates, mapId: mapId);
+ }
+
+ /// Updates circle configuration.
+ Future<void> _updateCircles(CircleUpdates circleUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateCircles(circleUpdates, mapId: mapId);
+ }
+
+ /// Updates tile overlays configuration.
+ Future<void> _updateTileOverlays(Set<TileOverlay> newTileOverlays) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateTileOverlays(newTileOverlays: newTileOverlays, mapId: mapId);
+ }
+
+ /// Clears the tile cache so that all tiles will be requested again from the
+ /// [TileProvider].
+ Future<void> clearTileCache(TileOverlayId tileOverlayId) async {
+ return GoogleMapsFlutterPlatform.instance
+ .clearTileCache(tileOverlayId, mapId: mapId);
+ }
+
+ /// Starts an animated change of the map camera position.
+ Future<void> animateCamera(CameraUpdate cameraUpdate) {
+ return GoogleMapsFlutterPlatform.instance
+ .animateCamera(cameraUpdate, mapId: mapId);
+ }
+
+ /// Changes the map camera position.
+ Future<void> moveCamera(CameraUpdate cameraUpdate) {
+ return GoogleMapsFlutterPlatform.instance
+ .moveCamera(cameraUpdate, mapId: mapId);
+ }
+
+ /// Sets the styling of the base map.
+ Future<void> setMapStyle(String? mapStyle) {
+ return GoogleMapsFlutterPlatform.instance
+ .setMapStyle(mapStyle, mapId: mapId);
+ }
+
+ /// Return [LatLngBounds] defining the region that is visible in a map.
+ Future<LatLngBounds> getVisibleRegion() {
+ return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId);
+ }
+
+ /// Return [ScreenCoordinate] of the [LatLng] in the current map view.
+ Future<ScreenCoordinate> getScreenCoordinate(LatLng latLng) {
+ return GoogleMapsFlutterPlatform.instance
+ .getScreenCoordinate(latLng, mapId: mapId);
+ }
+
+ /// Returns [LatLng] corresponding to the [ScreenCoordinate] in the current map view.
+ Future<LatLng> getLatLng(ScreenCoordinate screenCoordinate) {
+ return GoogleMapsFlutterPlatform.instance
+ .getLatLng(screenCoordinate, mapId: mapId);
+ }
+
+ /// Programmatically show the Info Window for a [Marker].
+ Future<void> showMarkerInfoWindow(MarkerId markerId) {
+ return GoogleMapsFlutterPlatform.instance
+ .showMarkerInfoWindow(markerId, mapId: mapId);
+ }
+
+ /// Programmatically hide the Info Window for a [Marker].
+ Future<void> hideMarkerInfoWindow(MarkerId markerId) {
+ return GoogleMapsFlutterPlatform.instance
+ .hideMarkerInfoWindow(markerId, mapId: mapId);
+ }
+
+ /// Returns `true` when the [InfoWindow] is showing, `false` otherwise.
+ Future<bool> isMarkerInfoWindowShown(MarkerId markerId) {
+ return GoogleMapsFlutterPlatform.instance
+ .isMarkerInfoWindowShown(markerId, mapId: mapId);
+ }
+
+ /// Returns the current zoom level of the map
+ Future<double> getZoomLevel() {
+ return GoogleMapsFlutterPlatform.instance.getZoomLevel(mapId: mapId);
+ }
+
+ /// Returns the image bytes of the map
+ Future<Uint8List?> takeSnapshot() {
+ return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId);
+ }
+
+ /// Disposes of the platform resources
+ void dispose() {
+ GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId);
+ }
+}
+
+// The next map ID to create.
+int _nextMapCreationId = 0;
+
+/// A widget which displays a map with data obtained from the Google Maps service.
+class ExampleGoogleMap extends StatefulWidget {
+ /// Creates a widget displaying data from Google Maps services.
+ ///
+ /// [AssertionError] will be thrown if [initialCameraPosition] is null;
+ const ExampleGoogleMap({
+ Key? key,
+ required this.initialCameraPosition,
+ this.onMapCreated,
+ this.gestureRecognizers = const <Factory<OneSequenceGestureRecognizer>>{},
+ this.compassEnabled = true,
+ this.mapToolbarEnabled = true,
+ this.cameraTargetBounds = CameraTargetBounds.unbounded,
+ this.mapType = MapType.normal,
+ this.minMaxZoomPreference = MinMaxZoomPreference.unbounded,
+ this.rotateGesturesEnabled = true,
+ this.scrollGesturesEnabled = true,
+ this.zoomControlsEnabled = true,
+ this.zoomGesturesEnabled = true,
+ this.liteModeEnabled = false,
+ this.tiltGesturesEnabled = true,
+ this.myLocationEnabled = false,
+ this.myLocationButtonEnabled = true,
+ this.layoutDirection,
+
+ /// If no padding is specified default padding will be 0.
+ this.padding = const EdgeInsets.all(0),
+ this.indoorViewEnabled = false,
+ this.trafficEnabled = false,
+ this.buildingsEnabled = true,
+ this.markers = const <Marker>{},
+ this.polygons = const <Polygon>{},
+ this.polylines = const <Polyline>{},
+ this.circles = const <Circle>{},
+ this.onCameraMoveStarted,
+ this.tileOverlays = const <TileOverlay>{},
+ this.onCameraMove,
+ this.onCameraIdle,
+ this.onTap,
+ this.onLongPress,
+ }) : super(key: key);
+
+ /// Callback method for when the map is ready to be used.
+ ///
+ /// Used to receive a [ExampleGoogleMapController] for this [ExampleGoogleMap].
+ final void Function(ExampleGoogleMapController controller)? onMapCreated;
+
+ /// The initial position of the map's camera.
+ final CameraPosition initialCameraPosition;
+
+ /// True if the map should show a compass when rotated.
+ final bool compassEnabled;
+
+ /// True if the map should show a toolbar when you interact with the map. Android only.
+ final bool mapToolbarEnabled;
+
+ /// Geographical bounding box for the camera target.
+ final CameraTargetBounds cameraTargetBounds;
+
+ /// Type of map tiles to be rendered.
+ final MapType mapType;
+
+ /// The layout direction to use for the embedded view.
+ final TextDirection? layoutDirection;
+
+ /// Preferred bounds for the camera zoom level.
+ ///
+ /// Actual bounds depend on map data and device.
+ final MinMaxZoomPreference minMaxZoomPreference;
+
+ /// True if the map view should respond to rotate gestures.
+ final bool rotateGesturesEnabled;
+
+ /// True if the map view should respond to scroll gestures.
+ final bool scrollGesturesEnabled;
+
+ /// True if the map view should show zoom controls. This includes two buttons
+ /// to zoom in and zoom out. The default value is to show zoom controls.
+ final bool zoomControlsEnabled;
+
+ /// True if the map view should respond to zoom gestures.
+ final bool zoomGesturesEnabled;
+
+ /// True if the map view should be in lite mode. Android only.
+ final bool liteModeEnabled;
+
+ /// True if the map view should respond to tilt gestures.
+ final bool tiltGesturesEnabled;
+
+ /// Padding to be set on map.
+ final EdgeInsets padding;
+
+ /// Markers to be placed on the map.
+ final Set<Marker> markers;
+
+ /// Polygons to be placed on the map.
+ final Set<Polygon> polygons;
+
+ /// Polylines to be placed on the map.
+ final Set<Polyline> polylines;
+
+ /// Circles to be placed on the map.
+ final Set<Circle> circles;
+
+ /// Tile overlays to be placed on the map.
+ final Set<TileOverlay> tileOverlays;
+
+ /// Called when the camera starts moving.
+ final VoidCallback? onCameraMoveStarted;
+
+ /// Called repeatedly as the camera continues to move after an
+ /// onCameraMoveStarted call.
+ final CameraPositionCallback? onCameraMove;
+
+ /// Called when camera movement has ended, there are no pending
+ /// animations and the user has stopped interacting with the map.
+ final VoidCallback? onCameraIdle;
+
+ /// Called every time a [ExampleGoogleMap] is tapped.
+ final ArgumentCallback<LatLng>? onTap;
+
+ /// Called every time a [ExampleGoogleMap] is long pressed.
+ final ArgumentCallback<LatLng>? onLongPress;
+
+ /// True if a "My Location" layer should be shown on the map.
+ final bool myLocationEnabled;
+
+ /// Enables or disables the my-location button.
+ final bool myLocationButtonEnabled;
+
+ /// Enables or disables the indoor view from the map
+ final bool indoorViewEnabled;
+
+ /// Enables or disables the traffic layer of the map
+ final bool trafficEnabled;
+
+ /// Enables or disables showing 3D buildings where available
+ final bool buildingsEnabled;
+
+ /// Which gestures should be consumed by the map.
+ final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
+
+ /// Creates a [State] for this [ExampleGoogleMap].
+ @override
+ State createState() => _ExampleGoogleMapState();
+}
+
+class _ExampleGoogleMapState extends State<ExampleGoogleMap> {
+ final int _mapId = _nextMapCreationId++;
+
+ final Completer<ExampleGoogleMapController> _controller =
+ Completer<ExampleGoogleMapController>();
+
+ Map<MarkerId, Marker> _markers = <MarkerId, Marker>{};
+ Map<PolygonId, Polygon> _polygons = <PolygonId, Polygon>{};
+ Map<PolylineId, Polyline> _polylines = <PolylineId, Polyline>{};
+ Map<CircleId, Circle> _circles = <CircleId, Circle>{};
+ late MapConfiguration _mapConfiguration;
+
+ @override
+ Widget build(BuildContext context) {
+ return GoogleMapsFlutterPlatform.instance.buildViewWithConfiguration(
+ _mapId,
+ onPlatformViewCreated,
+ widgetConfiguration: MapWidgetConfiguration(
+ textDirection: widget.layoutDirection ??
+ Directionality.maybeOf(context) ??
+ TextDirection.ltr,
+ initialCameraPosition: widget.initialCameraPosition,
+ gestureRecognizers: widget.gestureRecognizers,
+ ),
+ mapObjects: MapObjects(
+ markers: widget.markers,
+ polygons: widget.polygons,
+ polylines: widget.polylines,
+ circles: widget.circles,
+ ),
+ mapConfiguration: _mapConfiguration,
+ );
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _mapConfiguration = _configurationFromMapWidget(widget);
+ _markers = keyByMarkerId(widget.markers);
+ _polygons = keyByPolygonId(widget.polygons);
+ _polylines = keyByPolylineId(widget.polylines);
+ _circles = keyByCircleId(widget.circles);
+ }
+
+ @override
+ void dispose() {
+ _controller.future
+ .then((ExampleGoogleMapController controller) => controller.dispose());
+ super.dispose();
+ }
+
+ @override
+ void didUpdateWidget(ExampleGoogleMap oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ _updateOptions();
+ _updateMarkers();
+ _updatePolygons();
+ _updatePolylines();
+ _updateCircles();
+ _updateTileOverlays();
+ }
+
+ Future<void> _updateOptions() async {
+ final MapConfiguration newConfig = _configurationFromMapWidget(widget);
+ final MapConfiguration updates = newConfig.diffFrom(_mapConfiguration);
+ if (updates.isEmpty) {
+ return;
+ }
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateMapConfiguration(updates);
+ _mapConfiguration = newConfig;
+ }
+
+ Future<void> _updateMarkers() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateMarkers(
+ MarkerUpdates.from(_markers.values.toSet(), widget.markers));
+ _markers = keyByMarkerId(widget.markers);
+ }
+
+ Future<void> _updatePolygons() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updatePolygons(
+ PolygonUpdates.from(_polygons.values.toSet(), widget.polygons));
+ _polygons = keyByPolygonId(widget.polygons);
+ }
+
+ Future<void> _updatePolylines() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updatePolylines(
+ PolylineUpdates.from(_polylines.values.toSet(), widget.polylines));
+ _polylines = keyByPolylineId(widget.polylines);
+ }
+
+ Future<void> _updateCircles() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateCircles(
+ CircleUpdates.from(_circles.values.toSet(), widget.circles));
+ _circles = keyByCircleId(widget.circles);
+ }
+
+ Future<void> _updateTileOverlays() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateTileOverlays(widget.tileOverlays);
+ }
+
+ Future<void> onPlatformViewCreated(int id) async {
+ final ExampleGoogleMapController controller =
+ await ExampleGoogleMapController._init(
+ id,
+ widget.initialCameraPosition,
+ this,
+ );
+ _controller.complete(controller);
+ _updateTileOverlays();
+ widget.onMapCreated?.call(controller);
+ }
+
+ void onMarkerTap(MarkerId markerId) {
+ _markers[markerId]!.onTap?.call();
+ }
+
+ void onMarkerDragStart(MarkerId markerId, LatLng position) {
+ _markers[markerId]!.onDragStart?.call(position);
+ }
+
+ void onMarkerDrag(MarkerId markerId, LatLng position) {
+ _markers[markerId]!.onDrag?.call(position);
+ }
+
+ void onMarkerDragEnd(MarkerId markerId, LatLng position) {
+ _markers[markerId]!.onDragEnd?.call(position);
+ }
+
+ void onPolygonTap(PolygonId polygonId) {
+ _polygons[polygonId]!.onTap?.call();
+ }
+
+ void onPolylineTap(PolylineId polylineId) {
+ _polylines[polylineId]!.onTap?.call();
+ }
+
+ void onCircleTap(CircleId circleId) {
+ _circles[circleId]!.onTap?.call();
+ }
+
+ void onInfoWindowTap(MarkerId markerId) {
+ _markers[markerId]!.infoWindow.onTap?.call();
+ }
+
+ void onTap(LatLng position) {
+ widget.onTap?.call(position);
+ }
+
+ void onLongPress(LatLng position) {
+ widget.onLongPress?.call(position);
+ }
+}
+
+/// Builds a [MapConfiguration] from the given [map].
+MapConfiguration _configurationFromMapWidget(ExampleGoogleMap map) {
+ return MapConfiguration(
+ compassEnabled: map.compassEnabled,
+ mapToolbarEnabled: map.mapToolbarEnabled,
+ cameraTargetBounds: map.cameraTargetBounds,
+ mapType: map.mapType,
+ minMaxZoomPreference: map.minMaxZoomPreference,
+ rotateGesturesEnabled: map.rotateGesturesEnabled,
+ scrollGesturesEnabled: map.scrollGesturesEnabled,
+ tiltGesturesEnabled: map.tiltGesturesEnabled,
+ trackCameraPosition: map.onCameraMove != null,
+ zoomControlsEnabled: map.zoomControlsEnabled,
+ zoomGesturesEnabled: map.zoomGesturesEnabled,
+ liteModeEnabled: map.liteModeEnabled,
+ myLocationEnabled: map.myLocationEnabled,
+ myLocationButtonEnabled: map.myLocationButtonEnabled,
+ padding: map.padding,
+ indoorViewEnabled: map.indoorViewEnabled,
+ trafficEnabled: map.trafficEnabled,
+ buildingsEnabled: map.buildingsEnabled,
+ );
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/lite_mode.dart
new file mode 100644
index 0000000..f7bead9
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/lite_mode.dart
@@ -0,0 +1,47 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class LiteModePage extends GoogleMapExampleAppPage {
+ const LiteModePage({Key? key})
+ : super(const Icon(Icons.map), 'Lite mode', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const _LiteModeBody();
+ }
+}
+
+class _LiteModeBody extends StatelessWidget {
+ const _LiteModeBody();
+
+ @override
+ Widget build(BuildContext context) {
+ return const Card(
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 30.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: _kInitialPosition,
+ liteModeEnabled: true,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
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
new file mode 100644
index 0000000..6b96e6f
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart
@@ -0,0 +1,78 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
+import 'package:google_maps_flutter_example/lite_mode.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'animate_camera.dart';
+import 'map_click.dart';
+import 'map_coordinates.dart';
+import 'map_ui.dart';
+import 'marker_icons.dart';
+import 'move_camera.dart';
+import 'padding.dart';
+import 'page.dart';
+import 'place_circle.dart';
+import 'place_marker.dart';
+import 'place_polygon.dart';
+import 'place_polyline.dart';
+import 'scrolling_map.dart';
+import 'snapshot.dart';
+import 'tile_overlay.dart';
+
+final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
+ const MapUiPage(),
+ const MapCoordinatesPage(),
+ const MapClickPage(),
+ const AnimateCameraPage(),
+ const MoveCameraPage(),
+ const PlaceMarkerPage(),
+ const MarkerIconsPage(),
+ const ScrollingMapPage(),
+ const PlacePolylinePage(),
+ const PlacePolygonPage(),
+ const PlaceCirclePage(),
+ const PaddingPage(),
+ const SnapshotPage(),
+ const LiteModePage(),
+ const TileOverlayPage(),
+];
+
+/// MapsDemo is the Main Application.
+class MapsDemo extends StatelessWidget {
+ /// Default Constructor
+ const MapsDemo({Key? key}) : super(key: key);
+
+ void _pushPage(BuildContext context, GoogleMapExampleAppPage page) {
+ Navigator.of(context).push(MaterialPageRoute<void>(
+ builder: (_) => Scaffold(
+ appBar: AppBar(title: Text(page.title)),
+ body: page,
+ )));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('GoogleMaps examples')),
+ body: ListView.builder(
+ itemCount: _allPages.length,
+ itemBuilder: (_, int index) => ListTile(
+ leading: _allPages[index].leading,
+ title: Text(_allPages[index].title),
+ onTap: () => _pushPage(context, _allPages[index]),
+ ),
+ ),
+ );
+ }
+}
+
+void main() {
+ final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance;
+ // Default to Hybrid Composition for the example.
+ (platform as GoogleMapsFlutterAndroid).useAndroidViewSurface = true;
+ runApp(const MaterialApp(home: MapsDemo()));
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart
new file mode 100644
index 0000000..20718d9
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart
@@ -0,0 +1,106 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class MapClickPage extends GoogleMapExampleAppPage {
+ const MapClickPage({Key? key})
+ : super(const Icon(Icons.mouse), 'Map click', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const _MapClickBody();
+ }
+}
+
+class _MapClickBody extends StatefulWidget {
+ const _MapClickBody();
+
+ @override
+ State<StatefulWidget> createState() => _MapClickBodyState();
+}
+
+class _MapClickBodyState extends State<_MapClickBody> {
+ _MapClickBodyState();
+
+ ExampleGoogleMapController? mapController;
+ LatLng? _lastTap;
+ LatLng? _lastLongPress;
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ onTap: (LatLng pos) {
+ setState(() {
+ _lastTap = pos;
+ });
+ },
+ onLongPress: (LatLng pos) {
+ setState(() {
+ _lastLongPress = pos;
+ });
+ },
+ );
+
+ final List<Widget> columnChildren = <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ ];
+
+ if (mapController != null) {
+ final String lastTap = 'Tap:\n${_lastTap ?? ""}\n';
+ final String lastLongPress = 'Long press:\n${_lastLongPress ?? ""}';
+ columnChildren.add(Center(
+ child: Text(
+ lastTap,
+ textAlign: TextAlign.center,
+ )));
+ columnChildren.add(Center(
+ child: Text(
+ _lastTap != null ? 'Tapped' : '',
+ textAlign: TextAlign.center,
+ )));
+ columnChildren.add(Center(
+ child: Text(
+ lastLongPress,
+ textAlign: TextAlign.center,
+ )));
+ columnChildren.add(Center(
+ child: Text(
+ _lastLongPress != null ? 'Long pressed' : '',
+ textAlign: TextAlign.center,
+ )));
+ }
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: columnChildren,
+ );
+ }
+
+ Future<void> onMapCreated(ExampleGoogleMapController controller) async {
+ setState(() {
+ mapController = controller;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_coordinates.dart
new file mode 100644
index 0000000..185a97e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_coordinates.dart
@@ -0,0 +1,100 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class MapCoordinatesPage extends GoogleMapExampleAppPage {
+ const MapCoordinatesPage({Key? key})
+ : super(const Icon(Icons.map), 'Map coordinates', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const _MapCoordinatesBody();
+ }
+}
+
+class _MapCoordinatesBody extends StatefulWidget {
+ const _MapCoordinatesBody();
+
+ @override
+ State<StatefulWidget> createState() => _MapCoordinatesBodyState();
+}
+
+class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> {
+ _MapCoordinatesBodyState();
+
+ ExampleGoogleMapController? mapController;
+ LatLngBounds _visibleRegion = LatLngBounds(
+ southwest: const LatLng(0, 0),
+ northeast: const LatLng(0, 0),
+ );
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ onCameraIdle:
+ _updateVisibleRegion, // https://github.com/flutter/flutter/issues/54758
+ );
+
+ return NotificationListener<ScrollNotification>(
+ onNotification: (ScrollNotification scrollState) {
+ _updateVisibleRegion();
+ return true;
+ },
+ child: ListView(
+ children: <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ if (mapController != null)
+ Center(
+ child: Text('VisibleRegion:'
+ '\nnortheast: ${_visibleRegion.northeast},'
+ '\nsouthwest: ${_visibleRegion.southwest}'),
+ ),
+ // Add a block at the bottom of this list to allow validation that the visible region of the map
+ // does not change when scrolled under the safe view on iOS.
+ // https://github.com/flutter/flutter/issues/107913
+ Container(
+ width: 300,
+ height: 1000,
+ ),
+ ],
+ ),
+ );
+ }
+
+ Future<void> onMapCreated(ExampleGoogleMapController controller) async {
+ final LatLngBounds visibleRegion = await controller.getVisibleRegion();
+ setState(() {
+ mapController = controller;
+ _visibleRegion = visibleRegion;
+ });
+ }
+
+ Future<void> _updateVisibleRegion() async {
+ final LatLngBounds visibleRegion = await mapController!.getVisibleRegion();
+ setState(() {
+ _visibleRegion = visibleRegion;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart
new file mode 100644
index 0000000..6c38e14
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart
@@ -0,0 +1,358 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart' show rootBundle;
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+final LatLngBounds sydneyBounds = LatLngBounds(
+ southwest: const LatLng(-34.022631, 150.620685),
+ northeast: const LatLng(-33.571835, 151.325952),
+);
+
+class MapUiPage extends GoogleMapExampleAppPage {
+ const MapUiPage({Key? key})
+ : super(const Icon(Icons.map), 'User interface', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MapUiBody();
+ }
+}
+
+class MapUiBody extends StatefulWidget {
+ const MapUiBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => MapUiBodyState();
+}
+
+class MapUiBodyState extends State<MapUiBody> {
+ MapUiBodyState();
+
+ static const CameraPosition _kInitialPosition = CameraPosition(
+ target: LatLng(-33.852, 151.211),
+ zoom: 11.0,
+ );
+
+ CameraPosition _position = _kInitialPosition;
+ bool _isMapCreated = false;
+ final bool _isMoving = false;
+ bool _compassEnabled = true;
+ bool _mapToolbarEnabled = true;
+ CameraTargetBounds _cameraTargetBounds = CameraTargetBounds.unbounded;
+ MinMaxZoomPreference _minMaxZoomPreference = MinMaxZoomPreference.unbounded;
+ MapType _mapType = MapType.normal;
+ bool _rotateGesturesEnabled = true;
+ bool _scrollGesturesEnabled = true;
+ bool _tiltGesturesEnabled = true;
+ bool _zoomControlsEnabled = false;
+ bool _zoomGesturesEnabled = true;
+ bool _indoorViewEnabled = true;
+ bool _myLocationEnabled = true;
+ bool _myTrafficEnabled = false;
+ bool _myLocationButtonEnabled = true;
+ late ExampleGoogleMapController _controller;
+ bool _nightMode = false;
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ Widget _compassToggler() {
+ return TextButton(
+ child: Text('${_compassEnabled ? 'disable' : 'enable'} compass'),
+ onPressed: () {
+ setState(() {
+ _compassEnabled = !_compassEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _mapToolbarToggler() {
+ return TextButton(
+ child: Text('${_mapToolbarEnabled ? 'disable' : 'enable'} map toolbar'),
+ onPressed: () {
+ setState(() {
+ _mapToolbarEnabled = !_mapToolbarEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _latLngBoundsToggler() {
+ return TextButton(
+ child: Text(
+ _cameraTargetBounds.bounds == null
+ ? 'bound camera target'
+ : 'release camera target',
+ ),
+ onPressed: () {
+ setState(() {
+ _cameraTargetBounds = _cameraTargetBounds.bounds == null
+ ? CameraTargetBounds(sydneyBounds)
+ : CameraTargetBounds.unbounded;
+ });
+ },
+ );
+ }
+
+ Widget _zoomBoundsToggler() {
+ return TextButton(
+ child: Text(_minMaxZoomPreference.minZoom == null
+ ? 'bound zoom'
+ : 'release zoom'),
+ onPressed: () {
+ setState(() {
+ _minMaxZoomPreference = _minMaxZoomPreference.minZoom == null
+ ? const MinMaxZoomPreference(12.0, 16.0)
+ : MinMaxZoomPreference.unbounded;
+ });
+ },
+ );
+ }
+
+ Widget _mapTypeCycler() {
+ final MapType nextType =
+ MapType.values[(_mapType.index + 1) % MapType.values.length];
+ return TextButton(
+ child: Text('change map type to $nextType'),
+ onPressed: () {
+ setState(() {
+ _mapType = nextType;
+ });
+ },
+ );
+ }
+
+ Widget _rotateToggler() {
+ return TextButton(
+ child: Text('${_rotateGesturesEnabled ? 'disable' : 'enable'} rotate'),
+ onPressed: () {
+ setState(() {
+ _rotateGesturesEnabled = !_rotateGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _scrollToggler() {
+ return TextButton(
+ child: Text('${_scrollGesturesEnabled ? 'disable' : 'enable'} scroll'),
+ onPressed: () {
+ setState(() {
+ _scrollGesturesEnabled = !_scrollGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _tiltToggler() {
+ return TextButton(
+ child: Text('${_tiltGesturesEnabled ? 'disable' : 'enable'} tilt'),
+ onPressed: () {
+ setState(() {
+ _tiltGesturesEnabled = !_tiltGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _zoomToggler() {
+ return TextButton(
+ child: Text('${_zoomGesturesEnabled ? 'disable' : 'enable'} zoom'),
+ onPressed: () {
+ setState(() {
+ _zoomGesturesEnabled = !_zoomGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _zoomControlsToggler() {
+ return TextButton(
+ child:
+ Text('${_zoomControlsEnabled ? 'disable' : 'enable'} zoom controls'),
+ onPressed: () {
+ setState(() {
+ _zoomControlsEnabled = !_zoomControlsEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _indoorViewToggler() {
+ return TextButton(
+ child: Text('${_indoorViewEnabled ? 'disable' : 'enable'} indoor'),
+ onPressed: () {
+ setState(() {
+ _indoorViewEnabled = !_indoorViewEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _myLocationToggler() {
+ return TextButton(
+ child: Text(
+ '${_myLocationEnabled ? 'disable' : 'enable'} my location marker'),
+ onPressed: () {
+ setState(() {
+ _myLocationEnabled = !_myLocationEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _myLocationButtonToggler() {
+ return TextButton(
+ child: Text(
+ '${_myLocationButtonEnabled ? 'disable' : 'enable'} my location button'),
+ onPressed: () {
+ setState(() {
+ _myLocationButtonEnabled = !_myLocationButtonEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _myTrafficToggler() {
+ return TextButton(
+ child: Text('${_myTrafficEnabled ? 'disable' : 'enable'} my traffic'),
+ onPressed: () {
+ setState(() {
+ _myTrafficEnabled = !_myTrafficEnabled;
+ });
+ },
+ );
+ }
+
+ Future<String> _getFileData(String path) async {
+ return await rootBundle.loadString(path);
+ }
+
+ void _setMapStyle(String mapStyle) {
+ setState(() {
+ _nightMode = true;
+ _controller.setMapStyle(mapStyle);
+ });
+ }
+
+ // Should only be called if _isMapCreated is true.
+ Widget _nightModeToggler() {
+ assert(_isMapCreated);
+ return TextButton(
+ child: Text('${_nightMode ? 'disable' : 'enable'} night mode'),
+ onPressed: () {
+ if (_nightMode) {
+ setState(() {
+ _nightMode = false;
+ _controller.setMapStyle(null);
+ });
+ } else {
+ _getFileData('assets/night_mode.json').then(_setMapStyle);
+ }
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ compassEnabled: _compassEnabled,
+ mapToolbarEnabled: _mapToolbarEnabled,
+ cameraTargetBounds: _cameraTargetBounds,
+ minMaxZoomPreference: _minMaxZoomPreference,
+ mapType: _mapType,
+ rotateGesturesEnabled: _rotateGesturesEnabled,
+ scrollGesturesEnabled: _scrollGesturesEnabled,
+ tiltGesturesEnabled: _tiltGesturesEnabled,
+ zoomGesturesEnabled: _zoomGesturesEnabled,
+ zoomControlsEnabled: _zoomControlsEnabled,
+ indoorViewEnabled: _indoorViewEnabled,
+ myLocationEnabled: _myLocationEnabled,
+ myLocationButtonEnabled: _myLocationButtonEnabled,
+ trafficEnabled: _myTrafficEnabled,
+ onCameraMove: _updateCameraPosition,
+ );
+
+ final List<Widget> columnChildren = <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ ];
+
+ if (_isMapCreated) {
+ columnChildren.add(
+ Expanded(
+ child: ListView(
+ children: <Widget>[
+ Text('camera bearing: ${_position.bearing}'),
+ Text(
+ 'camera target: ${_position.target.latitude.toStringAsFixed(4)},'
+ '${_position.target.longitude.toStringAsFixed(4)}'),
+ Text('camera zoom: ${_position.zoom}'),
+ Text('camera tilt: ${_position.tilt}'),
+ Text(_isMoving ? '(Camera moving)' : '(Camera idle)'),
+ _compassToggler(),
+ _mapToolbarToggler(),
+ _latLngBoundsToggler(),
+ _mapTypeCycler(),
+ _zoomBoundsToggler(),
+ _rotateToggler(),
+ _scrollToggler(),
+ _tiltToggler(),
+ _zoomToggler(),
+ _zoomControlsToggler(),
+ _indoorViewToggler(),
+ _myLocationToggler(),
+ _myLocationButtonToggler(),
+ _myTrafficToggler(),
+ _nightModeToggler(),
+ ],
+ ),
+ ),
+ );
+ }
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: columnChildren,
+ );
+ }
+
+ void _updateCameraPosition(CameraPosition position) {
+ setState(() {
+ _position = position;
+ });
+ }
+
+ void onMapCreated(ExampleGoogleMapController controller) {
+ setState(() {
+ _controller = controller;
+ _isMapCreated = true;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/marker_icons.dart
new file mode 100644
index 0000000..fe28eb6
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/marker_icons.dart
@@ -0,0 +1,98 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: unawaited_futures
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class MarkerIconsPage extends GoogleMapExampleAppPage {
+ const MarkerIconsPage({Key? key})
+ : super(const Icon(Icons.image), 'Marker icons', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MarkerIconsBody();
+ }
+}
+
+class MarkerIconsBody extends StatefulWidget {
+ const MarkerIconsBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => MarkerIconsBodyState();
+}
+
+const LatLng _kMapCenter = LatLng(52.4478, -3.5402);
+
+class MarkerIconsBodyState extends State<MarkerIconsBody> {
+ ExampleGoogleMapController? controller;
+ BitmapDescriptor? _markerIcon;
+
+ @override
+ Widget build(BuildContext context) {
+ _createMarkerImageFromAsset(context);
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: _kMapCenter,
+ zoom: 7.0,
+ ),
+ markers: <Marker>{_createMarker()},
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ )
+ ],
+ );
+ }
+
+ Marker _createMarker() {
+ if (_markerIcon != null) {
+ return Marker(
+ markerId: const MarkerId('marker_1'),
+ position: _kMapCenter,
+ icon: _markerIcon!,
+ );
+ } else {
+ return const Marker(
+ markerId: MarkerId('marker_1'),
+ position: _kMapCenter,
+ );
+ }
+ }
+
+ Future<void> _createMarkerImageFromAsset(BuildContext context) async {
+ if (_markerIcon == null) {
+ final ImageConfiguration imageConfiguration =
+ createLocalImageConfiguration(context, size: const Size.square(48));
+ BitmapDescriptor.fromAssetImage(
+ imageConfiguration, 'assets/red_square.png')
+ .then(_updateBitmap);
+ }
+ }
+
+ void _updateBitmap(BitmapDescriptor bitmap) {
+ setState(() {
+ _markerIcon = bitmap;
+ });
+ }
+
+ void _onMapCreated(ExampleGoogleMapController controllerParam) {
+ setState(() {
+ controller = controllerParam;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/move_camera.dart
new file mode 100644
index 0000000..7f44d89
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/move_camera.dart
@@ -0,0 +1,171 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class MoveCameraPage extends GoogleMapExampleAppPage {
+ const MoveCameraPage({Key? key})
+ : super(const Icon(Icons.map), 'Camera control', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MoveCamera();
+ }
+}
+
+class MoveCamera extends StatefulWidget {
+ const MoveCamera({Key? key}) : super(key: key);
+ @override
+ State createState() => MoveCameraState();
+}
+
+class MoveCameraState extends State<MoveCamera> {
+ ExampleGoogleMapController? mapController;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ mapController = controller;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition:
+ const CameraPosition(target: LatLng(0.0, 0.0)),
+ ),
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newCameraPosition(
+ const CameraPosition(
+ bearing: 270.0,
+ target: LatLng(51.5160895, -0.1294527),
+ tilt: 30.0,
+ zoom: 17.0,
+ ),
+ ),
+ );
+ },
+ child: const Text('newCameraPosition'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newLatLng(
+ const LatLng(56.1725505, 10.1850512),
+ ),
+ );
+ },
+ child: const Text('newLatLng'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newLatLngBounds(
+ LatLngBounds(
+ southwest: const LatLng(-38.483935, 113.248673),
+ northeast: const LatLng(-8.982446, 153.823821),
+ ),
+ 10.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngBounds'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newLatLngZoom(
+ const LatLng(37.4231613, -122.087159),
+ 11.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngZoom'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.scrollBy(150.0, -225.0),
+ );
+ },
+ child: const Text('scrollBy'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomBy(
+ -0.5,
+ const Offset(30.0, 20.0),
+ ),
+ );
+ },
+ child: const Text('zoomBy with focus'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomBy(-0.5),
+ );
+ },
+ child: const Text('zoomBy'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomIn(),
+ );
+ },
+ child: const Text('zoomIn'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomOut(),
+ );
+ },
+ child: const Text('zoomOut'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomTo(16.0),
+ );
+ },
+ child: const Text('zoomTo'),
+ ),
+ ],
+ ),
+ ],
+ )
+ ],
+ );
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart
new file mode 100644
index 0000000..d346549
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart
@@ -0,0 +1,181 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PaddingPage extends GoogleMapExampleAppPage {
+ const PaddingPage({Key? key})
+ : super(const Icon(Icons.map), 'Add padding to the map', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MarkerIconsBody();
+ }
+}
+
+class MarkerIconsBody extends StatefulWidget {
+ const MarkerIconsBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => MarkerIconsBodyState();
+}
+
+const LatLng _kMapCenter = LatLng(52.4478, -3.5402);
+
+class MarkerIconsBodyState extends State<MarkerIconsBody> {
+ ExampleGoogleMapController? controller;
+
+ EdgeInsets _padding = const EdgeInsets.all(0);
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition: const CameraPosition(
+ target: _kMapCenter,
+ zoom: 7.0,
+ ),
+ padding: _padding,
+ );
+
+ final List<Widget> columnChildren = <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.only(top: 20),
+ child: Center(
+ child: Text(
+ 'Enter Padding Below',
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ ),
+ ),
+ ];
+
+ columnChildren.addAll(<Widget>[_paddingInput(), _buttons()]);
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: columnChildren,
+ );
+ }
+
+ void _onMapCreated(ExampleGoogleMapController controllerParam) {
+ setState(() {
+ controller = controllerParam;
+ });
+ }
+
+ final TextEditingController _topController = TextEditingController();
+ final TextEditingController _bottomController = TextEditingController();
+ final TextEditingController _leftController = TextEditingController();
+ final TextEditingController _rightController = TextEditingController();
+
+ Widget _paddingInput() {
+ return Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Row(
+ children: <Widget>[
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _topController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Top',
+ ),
+ ),
+ ),
+ const Spacer(),
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _bottomController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Bottom',
+ ),
+ ),
+ ),
+ const Spacer(),
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _leftController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Left',
+ ),
+ ),
+ ),
+ const Spacer(),
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _rightController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Right',
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buttons() {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ TextButton(
+ child: const Text('Set Padding'),
+ onPressed: () {
+ setState(() {
+ _padding = EdgeInsets.fromLTRB(
+ double.tryParse(_leftController.value.text) ?? 0,
+ double.tryParse(_topController.value.text) ?? 0,
+ double.tryParse(_rightController.value.text) ?? 0,
+ double.tryParse(_bottomController.value.text) ?? 0);
+ });
+ },
+ ),
+ TextButton(
+ child: const Text('Reset Padding'),
+ onPressed: () {
+ setState(() {
+ _topController.clear();
+ _bottomController.clear();
+ _leftController.clear();
+ _rightController.clear();
+ _padding = const EdgeInsets.all(0);
+ });
+ },
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/page.dart
new file mode 100644
index 0000000..eb01ab0
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/page.dart
@@ -0,0 +1,15 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+
+abstract class GoogleMapExampleAppPage extends StatelessWidget {
+ const GoogleMapExampleAppPage(this.leading, this.title, {Key? key})
+ : super(key: key);
+
+ final Widget leading;
+ final String title;
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_circle.dart
new file mode 100644
index 0000000..9dc5760
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_circle.dart
@@ -0,0 +1,232 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlaceCirclePage extends GoogleMapExampleAppPage {
+ const PlaceCirclePage({Key? key})
+ : super(const Icon(Icons.linear_scale), 'Place circle', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlaceCircleBody();
+ }
+}
+
+class PlaceCircleBody extends StatefulWidget {
+ const PlaceCircleBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlaceCircleBodyState();
+}
+
+class PlaceCircleBodyState extends State<PlaceCircleBody> {
+ PlaceCircleBodyState();
+
+ ExampleGoogleMapController? controller;
+ Map<CircleId, Circle> circles = <CircleId, Circle>{};
+ int _circleIdCounter = 1;
+ CircleId? selectedCircle;
+
+ // Values when toggling circle color
+ int fillColorsIndex = 0;
+ int strokeColorsIndex = 0;
+ List<Color> colors = <Color>[
+ Colors.purple,
+ Colors.red,
+ Colors.green,
+ Colors.pink,
+ ];
+
+ // Values when toggling circle stroke width
+ int widthsIndex = 0;
+ List<int> widths = <int>[10, 20, 5];
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onCircleTapped(CircleId circleId) {
+ setState(() {
+ selectedCircle = circleId;
+ });
+ }
+
+ void _remove(CircleId circleId) {
+ setState(() {
+ if (circles.containsKey(circleId)) {
+ circles.remove(circleId);
+ }
+ if (circleId == selectedCircle) {
+ selectedCircle = null;
+ }
+ });
+ }
+
+ void _add() {
+ final int circleCount = circles.length;
+
+ if (circleCount == 12) {
+ return;
+ }
+
+ final String circleIdVal = 'circle_id_$_circleIdCounter';
+ _circleIdCounter++;
+ final CircleId circleId = CircleId(circleIdVal);
+
+ final Circle circle = Circle(
+ circleId: circleId,
+ consumeTapEvents: true,
+ strokeColor: Colors.orange,
+ fillColor: Colors.green,
+ strokeWidth: 5,
+ center: _createCenter(),
+ radius: 50000,
+ onTap: () {
+ _onCircleTapped(circleId);
+ },
+ );
+
+ setState(() {
+ circles[circleId] = circle;
+ });
+ }
+
+ void _toggleVisible(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ visibleParam: !circle.visible,
+ );
+ });
+ }
+
+ void _changeFillColor(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ fillColorParam: colors[++fillColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeStrokeColor(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ strokeColorParam: colors[++strokeColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeStrokeWidth(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ strokeWidthParam: widths[++widthsIndex % widths.length],
+ );
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final CircleId? selectedId = selectedCircle;
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(52.4478, -3.5402),
+ zoom: 7.0,
+ ),
+ circles: Set<Circle>.of(circles.values),
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Row(
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('add'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _remove(selectedId),
+ child: const Text('remove'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeStrokeWidth(selectedId),
+ child: const Text('change stroke width'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeStrokeColor(selectedId),
+ child: const Text('change stroke color'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeFillColor(selectedId),
+ child: const Text('change fill color'),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ LatLng _createCenter() {
+ final double offset = _circleIdCounter.ceilToDouble();
+ return _createLatLng(51.4816 + offset * 0.2, -3.1791);
+ }
+
+ LatLng _createLatLng(double lat, double lng) {
+ return LatLng(lat, lng);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart
new file mode 100644
index 0000000..b7d2a69
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart
@@ -0,0 +1,422 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:async';
+import 'dart:math';
+import 'dart:typed_data';
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlaceMarkerPage extends GoogleMapExampleAppPage {
+ const PlaceMarkerPage({Key? key})
+ : super(const Icon(Icons.place), 'Place marker', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlaceMarkerBody();
+ }
+}
+
+class PlaceMarkerBody extends StatefulWidget {
+ const PlaceMarkerBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlaceMarkerBodyState();
+}
+
+typedef MarkerUpdateAction = Marker Function(Marker marker);
+
+class PlaceMarkerBodyState extends State<PlaceMarkerBody> {
+ PlaceMarkerBodyState();
+ static const LatLng center = LatLng(-33.86711, 151.1947171);
+
+ ExampleGoogleMapController? controller;
+ Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
+ MarkerId? selectedMarker;
+ int _markerIdCounter = 1;
+ LatLng? markerPosition;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onMarkerTapped(MarkerId markerId) {
+ final Marker? tappedMarker = markers[markerId];
+ if (tappedMarker != null) {
+ setState(() {
+ final MarkerId? previousMarkerId = selectedMarker;
+ if (previousMarkerId != null && markers.containsKey(previousMarkerId)) {
+ final Marker resetOld = markers[previousMarkerId]!
+ .copyWith(iconParam: BitmapDescriptor.defaultMarker);
+ markers[previousMarkerId] = resetOld;
+ }
+ selectedMarker = markerId;
+ final Marker newMarker = tappedMarker.copyWith(
+ iconParam: BitmapDescriptor.defaultMarkerWithHue(
+ BitmapDescriptor.hueGreen,
+ ),
+ );
+ markers[markerId] = newMarker;
+
+ markerPosition = null;
+ });
+ }
+ }
+
+ Future<void> _onMarkerDrag(MarkerId markerId, LatLng newPosition) async {
+ setState(() {
+ markerPosition = newPosition;
+ });
+ }
+
+ Future<void> _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async {
+ final Marker? tappedMarker = markers[markerId];
+ if (tappedMarker != null) {
+ setState(() {
+ markerPosition = null;
+ });
+ await showDialog<void>(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ actions: <Widget>[
+ TextButton(
+ child: const Text('OK'),
+ onPressed: () => Navigator.of(context).pop(),
+ )
+ ],
+ content: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 66),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: <Widget>[
+ Text('Old position: ${tappedMarker.position}'),
+ Text('New position: $newPosition'),
+ ],
+ )));
+ });
+ }
+ }
+
+ void _add() {
+ final int markerCount = markers.length;
+
+ if (markerCount == 12) {
+ return;
+ }
+
+ final String markerIdVal = 'marker_id_$_markerIdCounter';
+ _markerIdCounter++;
+ final MarkerId markerId = MarkerId(markerIdVal);
+
+ final Marker marker = Marker(
+ markerId: markerId,
+ position: LatLng(
+ center.latitude + sin(_markerIdCounter * pi / 6.0) / 20.0,
+ center.longitude + cos(_markerIdCounter * pi / 6.0) / 20.0,
+ ),
+ infoWindow: InfoWindow(title: markerIdVal, snippet: '*'),
+ onTap: () => _onMarkerTapped(markerId),
+ onDragEnd: (LatLng position) => _onMarkerDragEnd(markerId, position),
+ onDrag: (LatLng position) => _onMarkerDrag(markerId, position),
+ );
+
+ setState(() {
+ markers[markerId] = marker;
+ });
+ }
+
+ void _remove(MarkerId markerId) {
+ setState(() {
+ if (markers.containsKey(markerId)) {
+ markers.remove(markerId);
+ }
+ });
+ }
+
+ void _changePosition(MarkerId markerId) {
+ final Marker marker = markers[markerId]!;
+ final LatLng current = marker.position;
+ final Offset offset = Offset(
+ center.latitude - current.latitude,
+ center.longitude - current.longitude,
+ );
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ positionParam: LatLng(
+ center.latitude + offset.dy,
+ center.longitude + offset.dx,
+ ),
+ );
+ });
+ }
+
+ void _changeAnchor(MarkerId markerId) {
+ final Marker marker = markers[markerId]!;
+ final Offset currentAnchor = marker.anchor;
+ final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx);
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ anchorParam: newAnchor,
+ );
+ });
+ }
+
+ Future<void> _changeInfoAnchor(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final Offset currentAnchor = marker.infoWindow.anchor;
+ final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx);
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ infoWindowParam: marker.infoWindow.copyWith(
+ anchorParam: newAnchor,
+ ),
+ );
+ });
+ }
+
+ Future<void> _toggleDraggable(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ draggableParam: !marker.draggable,
+ );
+ });
+ }
+
+ Future<void> _toggleFlat(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ flatParam: !marker.flat,
+ );
+ });
+ }
+
+ Future<void> _changeInfo(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final String newSnippet = '${marker.infoWindow.snippet!}*';
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ infoWindowParam: marker.infoWindow.copyWith(
+ snippetParam: newSnippet,
+ ),
+ );
+ });
+ }
+
+ Future<void> _changeAlpha(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final double current = marker.alpha;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ alphaParam: current < 0.1 ? 1.0 : current * 0.75,
+ );
+ });
+ }
+
+ Future<void> _changeRotation(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final double current = marker.rotation;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ rotationParam: current == 330.0 ? 0.0 : current + 30.0,
+ );
+ });
+ }
+
+ Future<void> _toggleVisible(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ visibleParam: !marker.visible,
+ );
+ });
+ }
+
+ Future<void> _changeZIndex(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final double current = marker.zIndex;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ zIndexParam: current == 12.0 ? 0.0 : current + 1.0,
+ );
+ });
+ }
+
+ void _setMarkerIcon(MarkerId markerId, BitmapDescriptor assetIcon) {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ iconParam: assetIcon,
+ );
+ });
+ }
+
+ Future<BitmapDescriptor> _getAssetIcon(BuildContext context) async {
+ final Completer<BitmapDescriptor> bitmapIcon =
+ Completer<BitmapDescriptor>();
+ final ImageConfiguration config = createLocalImageConfiguration(context);
+
+ const AssetImage('assets/red_square.png')
+ .resolve(config)
+ .addListener(ImageStreamListener((ImageInfo image, bool sync) async {
+ final ByteData? bytes =
+ await image.image.toByteData(format: ImageByteFormat.png);
+ if (bytes == null) {
+ bitmapIcon.completeError(Exception('Unable to encode icon'));
+ return;
+ }
+ final BitmapDescriptor bitmap =
+ BitmapDescriptor.fromBytes(bytes.buffer.asUint8List());
+ bitmapIcon.complete(bitmap);
+ }));
+
+ return await bitmapIcon.future;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final MarkerId? selectedId = selectedMarker;
+ return Stack(children: <Widget>[
+ Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Expanded(
+ child: ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(-33.852, 151.211),
+ zoom: 11.0,
+ ),
+ markers: Set<Marker>.of(markers.values),
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('Add'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _remove(selectedId),
+ child: const Text('Remove'),
+ ),
+ ],
+ ),
+ Wrap(
+ alignment: WrapAlignment.spaceEvenly,
+ children: <Widget>[
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeInfo(selectedId),
+ child: const Text('change info'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _changeInfoAnchor(selectedId),
+ child: const Text('change info anchor'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeAlpha(selectedId),
+ child: const Text('change alpha'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeAnchor(selectedId),
+ child: const Text('change anchor'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _toggleDraggable(selectedId),
+ child: const Text('toggle draggable'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _toggleFlat(selectedId),
+ child: const Text('toggle flat'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _changePosition(selectedId),
+ child: const Text('change position'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _changeRotation(selectedId),
+ child: const Text('change rotation'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeZIndex(selectedId),
+ child: const Text('change zIndex'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () {
+ _getAssetIcon(context).then(
+ (BitmapDescriptor icon) {
+ _setMarkerIcon(selectedId, icon);
+ },
+ );
+ },
+ child: const Text('set marker icon'),
+ ),
+ ],
+ ),
+ ],
+ ),
+ Visibility(
+ visible: markerPosition != null,
+ child: Container(
+ color: Colors.white70,
+ height: 30,
+ padding: const EdgeInsets.only(left: 12, right: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ mainAxisSize: MainAxisSize.max,
+ children: <Widget>[
+ if (markerPosition == null)
+ Container()
+ else
+ Expanded(child: Text('lat: ${markerPosition!.latitude}')),
+ if (markerPosition == null)
+ Container()
+ else
+ Expanded(child: Text('lng: ${markerPosition!.longitude}')),
+ ],
+ ),
+ ),
+ ),
+ ]);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polygon.dart
new file mode 100644
index 0000000..b41cb5d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polygon.dart
@@ -0,0 +1,306 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlacePolygonPage extends GoogleMapExampleAppPage {
+ const PlacePolygonPage({Key? key})
+ : super(const Icon(Icons.linear_scale), 'Place polygon', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlacePolygonBody();
+ }
+}
+
+class PlacePolygonBody extends StatefulWidget {
+ const PlacePolygonBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlacePolygonBodyState();
+}
+
+class PlacePolygonBodyState extends State<PlacePolygonBody> {
+ PlacePolygonBodyState();
+
+ ExampleGoogleMapController? controller;
+ Map<PolygonId, Polygon> polygons = <PolygonId, Polygon>{};
+ Map<PolygonId, double> polygonOffsets = <PolygonId, double>{};
+ int _polygonIdCounter = 0;
+ PolygonId? selectedPolygon;
+
+ // Values when toggling polygon color
+ int strokeColorsIndex = 0;
+ int fillColorsIndex = 0;
+ List<Color> colors = <Color>[
+ Colors.purple,
+ Colors.red,
+ Colors.green,
+ Colors.pink,
+ ];
+
+ // Values when toggling polygon width
+ int widthsIndex = 0;
+ List<int> widths = <int>[10, 20, 5];
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onPolygonTapped(PolygonId polygonId) {
+ setState(() {
+ selectedPolygon = polygonId;
+ });
+ }
+
+ void _remove(PolygonId polygonId) {
+ setState(() {
+ if (polygons.containsKey(polygonId)) {
+ polygons.remove(polygonId);
+ }
+ selectedPolygon = null;
+ });
+ }
+
+ void _add() {
+ final int polygonCount = polygons.length;
+
+ if (polygonCount == 12) {
+ return;
+ }
+
+ final String polygonIdVal = 'polygon_id_$_polygonIdCounter';
+ final PolygonId polygonId = PolygonId(polygonIdVal);
+
+ final Polygon polygon = Polygon(
+ polygonId: polygonId,
+ consumeTapEvents: true,
+ strokeColor: Colors.orange,
+ strokeWidth: 5,
+ fillColor: Colors.green,
+ points: _createPoints(),
+ onTap: () {
+ _onPolygonTapped(polygonId);
+ },
+ );
+
+ setState(() {
+ polygons[polygonId] = polygon;
+ polygonOffsets[polygonId] = _polygonIdCounter.ceilToDouble();
+ // increment _polygonIdCounter to have unique polygon id each time
+ _polygonIdCounter++;
+ });
+ }
+
+ void _toggleGeodesic(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ geodesicParam: !polygon.geodesic,
+ );
+ });
+ }
+
+ void _toggleVisible(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ visibleParam: !polygon.visible,
+ );
+ });
+ }
+
+ void _changeStrokeColor(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ strokeColorParam: colors[++strokeColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeFillColor(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ fillColorParam: colors[++fillColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeWidth(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ strokeWidthParam: widths[++widthsIndex % widths.length],
+ );
+ });
+ }
+
+ void _addHoles(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] =
+ polygon.copyWith(holesParam: _createHoles(polygonId));
+ });
+ }
+
+ void _removeHoles(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ holesParam: <List<LatLng>>[],
+ );
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final PolygonId? selectedId = selectedPolygon;
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(52.4478, -3.5402),
+ zoom: 7.0,
+ ),
+ polygons: Set<Polygon>.of(polygons.values),
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Row(
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('add'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _remove(selectedId),
+ child: const Text('remove'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleGeodesic(selectedId),
+ child: const Text('toggle geodesic'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : ((polygons[selectedId]!.holes.isNotEmpty)
+ ? null
+ : () => _addHoles(selectedId)),
+ child: const Text('add holes'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : ((polygons[selectedId]!.holes.isEmpty)
+ ? null
+ : () => _removeHoles(selectedId)),
+ child: const Text('remove holes'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeWidth(selectedId),
+ child: const Text('change stroke width'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeStrokeColor(selectedId),
+ child: const Text('change stroke color'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeFillColor(selectedId),
+ child: const Text('change fill color'),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ List<LatLng> _createPoints() {
+ final List<LatLng> points = <LatLng>[];
+ final double offset = _polygonIdCounter.ceilToDouble();
+ points.add(_createLatLng(51.2395 + offset, -3.4314));
+ points.add(_createLatLng(53.5234 + offset, -3.5314));
+ points.add(_createLatLng(52.4351 + offset, -4.5235));
+ points.add(_createLatLng(52.1231 + offset, -5.0829));
+ return points;
+ }
+
+ List<List<LatLng>> _createHoles(PolygonId polygonId) {
+ final List<List<LatLng>> holes = <List<LatLng>>[];
+ final double offset = polygonOffsets[polygonId]!;
+
+ final List<LatLng> hole1 = <LatLng>[];
+ hole1.add(_createLatLng(51.8395 + offset, -3.8814));
+ hole1.add(_createLatLng(52.0234 + offset, -3.9914));
+ hole1.add(_createLatLng(52.1351 + offset, -4.4435));
+ hole1.add(_createLatLng(52.0231 + offset, -4.5829));
+ holes.add(hole1);
+
+ final List<LatLng> hole2 = <LatLng>[];
+ hole2.add(_createLatLng(52.2395 + offset, -3.6814));
+ hole2.add(_createLatLng(52.4234 + offset, -3.7914));
+ hole2.add(_createLatLng(52.5351 + offset, -4.2435));
+ hole2.add(_createLatLng(52.4231 + offset, -4.3829));
+ holes.add(hole2);
+
+ return holes;
+ }
+
+ LatLng _createLatLng(double lat, double lng) {
+ return LatLng(lat, lng);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polyline.dart
new file mode 100644
index 0000000..004206b
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polyline.dart
@@ -0,0 +1,325 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlacePolylinePage extends GoogleMapExampleAppPage {
+ const PlacePolylinePage({Key? key})
+ : super(const Icon(Icons.linear_scale), 'Place polyline', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlacePolylineBody();
+ }
+}
+
+class PlacePolylineBody extends StatefulWidget {
+ const PlacePolylineBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlacePolylineBodyState();
+}
+
+class PlacePolylineBodyState extends State<PlacePolylineBody> {
+ PlacePolylineBodyState();
+
+ ExampleGoogleMapController? controller;
+ Map<PolylineId, Polyline> polylines = <PolylineId, Polyline>{};
+ int _polylineIdCounter = 0;
+ PolylineId? selectedPolyline;
+
+ // Values when toggling polyline color
+ int colorsIndex = 0;
+ List<Color> colors = <Color>[
+ Colors.purple,
+ Colors.red,
+ Colors.green,
+ Colors.pink,
+ ];
+
+ // Values when toggling polyline width
+ int widthsIndex = 0;
+ List<int> widths = <int>[10, 20, 5];
+
+ int jointTypesIndex = 0;
+ List<JointType> jointTypes = <JointType>[
+ JointType.mitered,
+ JointType.bevel,
+ JointType.round
+ ];
+
+ // Values when toggling polyline end cap type
+ int endCapsIndex = 0;
+ List<Cap> endCaps = <Cap>[Cap.buttCap, Cap.squareCap, Cap.roundCap];
+
+ // Values when toggling polyline start cap type
+ int startCapsIndex = 0;
+ List<Cap> startCaps = <Cap>[Cap.buttCap, Cap.squareCap, Cap.roundCap];
+
+ // Values when toggling polyline pattern
+ int patternsIndex = 0;
+ List<List<PatternItem>> patterns = <List<PatternItem>>[
+ <PatternItem>[],
+ <PatternItem>[
+ PatternItem.dash(30.0),
+ PatternItem.gap(20.0),
+ PatternItem.dot,
+ PatternItem.gap(20.0)
+ ],
+ <PatternItem>[PatternItem.dash(30.0), PatternItem.gap(20.0)],
+ <PatternItem>[PatternItem.dot, PatternItem.gap(10.0)],
+ ];
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onPolylineTapped(PolylineId polylineId) {
+ setState(() {
+ selectedPolyline = polylineId;
+ });
+ }
+
+ void _remove(PolylineId polylineId) {
+ setState(() {
+ if (polylines.containsKey(polylineId)) {
+ polylines.remove(polylineId);
+ }
+ selectedPolyline = null;
+ });
+ }
+
+ void _add() {
+ final int polylineCount = polylines.length;
+
+ if (polylineCount == 12) {
+ return;
+ }
+
+ final String polylineIdVal = 'polyline_id_$_polylineIdCounter';
+ _polylineIdCounter++;
+ final PolylineId polylineId = PolylineId(polylineIdVal);
+
+ final Polyline polyline = Polyline(
+ polylineId: polylineId,
+ consumeTapEvents: true,
+ color: Colors.orange,
+ width: 5,
+ points: _createPoints(),
+ onTap: () {
+ _onPolylineTapped(polylineId);
+ },
+ );
+
+ setState(() {
+ polylines[polylineId] = polyline;
+ });
+ }
+
+ void _toggleGeodesic(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ geodesicParam: !polyline.geodesic,
+ );
+ });
+ }
+
+ void _toggleVisible(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ visibleParam: !polyline.visible,
+ );
+ });
+ }
+
+ void _changeColor(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ colorParam: colors[++colorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeWidth(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ widthParam: widths[++widthsIndex % widths.length],
+ );
+ });
+ }
+
+ void _changeJointType(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ jointTypeParam: jointTypes[++jointTypesIndex % jointTypes.length],
+ );
+ });
+ }
+
+ void _changeEndCap(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ endCapParam: endCaps[++endCapsIndex % endCaps.length],
+ );
+ });
+ }
+
+ void _changeStartCap(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ startCapParam: startCaps[++startCapsIndex % startCaps.length],
+ );
+ });
+ }
+
+ void _changePattern(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ patternsParam: patterns[++patternsIndex % patterns.length],
+ );
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final bool isIOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;
+
+ final PolylineId? selectedId = selectedPolyline;
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(53.1721, -3.5402),
+ zoom: 7.0,
+ ),
+ polylines: Set<Polyline>.of(polylines.values),
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Row(
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('add'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _remove(selectedId),
+ child: const Text('remove'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleGeodesic(selectedId),
+ child: const Text('toggle geodesic'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeWidth(selectedId),
+ child: const Text('change width'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeColor(selectedId),
+ child: const Text('change color'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changeStartCap(selectedId),
+ child: const Text('change start cap [Android only]'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changeEndCap(selectedId),
+ child: const Text('change end cap [Android only]'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changeJointType(selectedId),
+ child: const Text('change joint type [Android only]'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changePattern(selectedId),
+ child: const Text('change pattern [Android only]'),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ List<LatLng> _createPoints() {
+ final List<LatLng> points = <LatLng>[];
+ final double offset = _polylineIdCounter.ceilToDouble();
+ points.add(_createLatLng(51.4816 + offset, -3.1791));
+ points.add(_createLatLng(53.0430 + offset, -2.9925));
+ points.add(_createLatLng(53.1396 + offset, -4.2739));
+ points.add(_createLatLng(52.4153 + offset, -4.0829));
+ return points;
+ }
+
+ LatLng _createLatLng(double lat, double lng) {
+ return LatLng(lat, lng);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/scrolling_map.dart
new file mode 100644
index 0000000..7a9b75c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/scrolling_map.dart
@@ -0,0 +1,114 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const LatLng _center = LatLng(32.080664, 34.9563837);
+
+class ScrollingMapPage extends GoogleMapExampleAppPage {
+ const ScrollingMapPage({Key? key})
+ : super(const Icon(Icons.map), 'Scrolling map', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const ScrollingMapBody();
+ }
+}
+
+class ScrollingMapBody extends StatelessWidget {
+ const ScrollingMapBody({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return ListView(
+ children: <Widget>[
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 30.0),
+ child: Column(
+ children: <Widget>[
+ const Padding(
+ padding: EdgeInsets.only(bottom: 12.0),
+ child: Text('This map consumes all touch events.'),
+ ),
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: _center,
+ zoom: 11.0,
+ ),
+ gestureRecognizers: //
+ <Factory<OneSequenceGestureRecognizer>>{
+ Factory<OneSequenceGestureRecognizer>(
+ () => EagerGestureRecognizer(),
+ ),
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 30.0),
+ child: Column(
+ children: <Widget>[
+ const Text("This map doesn't consume the vertical drags."),
+ const Padding(
+ padding: EdgeInsets.only(bottom: 12.0),
+ child:
+ Text('It still gets other gestures (e.g scale or tap).'),
+ ),
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: _center,
+ zoom: 11.0,
+ ),
+ markers: <Marker>{
+ Marker(
+ markerId: const MarkerId('test_marker_id'),
+ position: LatLng(
+ _center.latitude,
+ _center.longitude,
+ ),
+ infoWindow: const InfoWindow(
+ title: 'An interesting location',
+ snippet: '*',
+ ),
+ ),
+ },
+ gestureRecognizers: <
+ Factory<OneSequenceGestureRecognizer>>{
+ Factory<OneSequenceGestureRecognizer>(
+ () => ScaleGestureRecognizer(),
+ ),
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/snapshot.dart
new file mode 100644
index 0000000..56a90a8
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/snapshot.dart
@@ -0,0 +1,76 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class SnapshotPage extends GoogleMapExampleAppPage {
+ const SnapshotPage({Key? key})
+ : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map',
+ key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return _SnapshotBody();
+ }
+}
+
+class _SnapshotBody extends StatefulWidget {
+ @override
+ _SnapshotBodyState createState() => _SnapshotBodyState();
+}
+
+class _SnapshotBodyState extends State<_SnapshotBody> {
+ ExampleGoogleMapController? _mapController;
+ Uint8List? _imageBytes;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ SizedBox(
+ height: 180,
+ child: ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ ),
+ ),
+ TextButton(
+ child: const Text('Take a snapshot'),
+ onPressed: () async {
+ final Uint8List? imageBytes =
+ await _mapController?.takeSnapshot();
+ setState(() {
+ _imageBytes = imageBytes;
+ });
+ },
+ ),
+ Container(
+ decoration: BoxDecoration(color: Colors.blueGrey[50]),
+ height: 180,
+ child: _imageBytes != null ? Image.memory(_imageBytes!) : null,
+ ),
+ ],
+ ),
+ );
+ }
+
+ // ignore: use_setters_to_change_properties
+ void onMapCreated(ExampleGoogleMapController controller) {
+ _mapController = controller;
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart
new file mode 100644
index 0000000..555f037
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart
@@ -0,0 +1,156 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:typed_data';
+import 'dart:ui' as ui;
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class TileOverlayPage extends GoogleMapExampleAppPage {
+ const TileOverlayPage({Key? key})
+ : super(const Icon(Icons.map), 'Tile overlay', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const TileOverlayBody();
+ }
+}
+
+class TileOverlayBody extends StatefulWidget {
+ const TileOverlayBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => TileOverlayBodyState();
+}
+
+class TileOverlayBodyState extends State<TileOverlayBody> {
+ TileOverlayBodyState();
+
+ ExampleGoogleMapController? controller;
+ TileOverlay? _tileOverlay;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _removeTileOverlay() {
+ setState(() {
+ _tileOverlay = null;
+ });
+ }
+
+ void _addTileOverlay() {
+ final TileOverlay tileOverlay = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ );
+ setState(() {
+ _tileOverlay = tileOverlay;
+ });
+ }
+
+ void _clearTileCache() {
+ if (_tileOverlay != null && controller != null) {
+ controller!.clearTileCache(_tileOverlay!.tileOverlayId);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final Set<TileOverlay> overlays = <TileOverlay>{
+ if (_tileOverlay != null) _tileOverlay!,
+ };
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(59.935460, 30.325177),
+ zoom: 7.0,
+ ),
+ tileOverlays: overlays,
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ TextButton(
+ onPressed: _addTileOverlay,
+ child: const Text('Add tile overlay'),
+ ),
+ TextButton(
+ onPressed: _removeTileOverlay,
+ child: const Text('Remove tile overlay'),
+ ),
+ TextButton(
+ onPressed: _clearTileCache,
+ child: const Text('Clear tile cache'),
+ ),
+ ],
+ );
+ }
+}
+
+class _DebugTileProvider implements TileProvider {
+ _DebugTileProvider() {
+ boxPaint.isAntiAlias = true;
+ boxPaint.color = Colors.blue;
+ boxPaint.strokeWidth = 2.0;
+ boxPaint.style = PaintingStyle.stroke;
+ }
+
+ static const int width = 100;
+ static const int height = 100;
+ static final Paint boxPaint = Paint();
+ static const TextStyle textStyle = TextStyle(
+ color: Colors.red,
+ fontSize: 20,
+ );
+
+ @override
+ Future<Tile> getTile(int x, int y, int? zoom) async {
+ final ui.PictureRecorder recorder = ui.PictureRecorder();
+ final Canvas canvas = Canvas(recorder);
+ final TextSpan textSpan = TextSpan(
+ text: '$x,$y',
+ style: textStyle,
+ );
+ final TextPainter textPainter = TextPainter(
+ text: textSpan,
+ textDirection: TextDirection.ltr,
+ );
+ textPainter.layout(
+ minWidth: 0.0,
+ maxWidth: width.toDouble(),
+ );
+ const Offset offset = Offset(0, 0);
+ textPainter.paint(canvas, offset);
+ canvas.drawRect(
+ Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint);
+ final ui.Picture picture = recorder.endRecording();
+ final Uint8List byteData = await picture
+ .toImage(width, height)
+ .then((ui.Image image) =>
+ image.toByteData(format: ui.ImageByteFormat.png))
+ .then((ByteData? byteData) => byteData!.buffer.asUint8List());
+ return Tile(width, height, byteData);
+ }
+}
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
new file mode 100644
index 0000000..fc1059b
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml
@@ -0,0 +1,33 @@
+name: google_maps_flutter_example
+description: Demonstrates how to use the google_maps_flutter plugin.
+publish_to: none
+
+environment:
+ sdk: ">=2.14.0 <3.0.0"
+ flutter: ">=2.8.0"
+
+dependencies:
+ cupertino_icons: ^0.1.0
+ flutter:
+ sdk: flutter
+ flutter_plugin_android_lifecycle: ^2.0.1
+ google_maps_flutter_android:
+ # When depending on this package from a real application you should use:
+ # google_maps_flutter_android: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # 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.2.1
+
+dev_dependencies:
+ espresso: ^0.1.0+2
+ flutter_driver:
+ sdk: flutter
+ integration_test:
+ sdk: flutter
+
+flutter:
+ uses-material-design: true
+ assets:
+ - assets/
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/test_driver/integration_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/test_driver/integration_test.dart
new file mode 100644
index 0000000..4f10f2a
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/example/test_driver/integration_test.dart
@@ -0,0 +1,7 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:integration_test/integration_test_driver.dart';
+
+Future<void> main() => integrationDriver();
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/google_maps_flutter_android.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/google_maps_flutter_android.dart
new file mode 100644
index 0000000..edd231e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/google_maps_flutter_android.dart
@@ -0,0 +1,5 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'src/google_maps_flutter_android.dart';
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_map_inspector_android.dart
similarity index 67%
rename from packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart
rename to packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_map_inspector_android.dart
index 95ca969..4e0cad7 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_map_inspector_android.dart
@@ -2,66 +2,59 @@
// 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';
+import 'package:flutter/services.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
-/// A method-channel-based implementation of [GoogleMapsInspectorPlatform], for
-/// use in tests in conjunction with [MethodChannelGoogleMapsFlutter].
-// TODO(stuartmorgan): Move this into the platform implementations when
-// federating the mobile implementations.
-class MethodChannelGoogleMapsInspector extends GoogleMapsInspectorPlatform {
+/// An Android of implementation of [GoogleMapsInspectorPlatform].
+@visibleForTesting
+class GoogleMapsInspectorAndroid extends GoogleMapsInspectorPlatform {
/// Creates a method-channel-based inspector instance that gets the channel
- /// for a given map ID from [mapsPlatform].
- MethodChannelGoogleMapsInspector(MethodChannelGoogleMapsFlutter mapsPlatform)
- : _mapsPlatform = mapsPlatform;
+ /// for a given map ID from [channelProvider].
+ GoogleMapsInspectorAndroid(MethodChannel? Function(int mapId) channelProvider)
+ : _channelProvider = channelProvider;
- final MethodChannelGoogleMapsFlutter _mapsPlatform;
+ final MethodChannel? Function(int mapId) _channelProvider;
@override
Future<bool> areBuildingsEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isBuildingsEnabled'))!;
}
@override
Future<bool> areRotateGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isRotateGesturesEnabled'))!;
}
@override
Future<bool> areScrollGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isScrollGesturesEnabled'))!;
}
@override
Future<bool> areTiltGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isTiltGesturesEnabled'))!;
}
@override
Future<bool> areZoomControlsEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isZoomControlsEnabled'))!;
}
@override
Future<bool> areZoomGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isZoomGesturesEnabled'))!;
}
@override
Future<MinMaxZoomPreference> getMinMaxZoomLevels({required int mapId}) async {
- final List<double> zoomLevels = (await _mapsPlatform
- .channel(mapId)
+ final List<double> zoomLevels = (await _channelProvider(mapId)!
.invokeMethod<List<dynamic>>('map#getMinMaxZoomLevels'))!
.cast<double>();
return MinMaxZoomPreference(zoomLevels[0], zoomLevels[1]);
@@ -70,8 +63,7 @@
@override
Future<TileOverlay?> getTileOverlayInfo(TileOverlayId tileOverlayId,
{required int mapId}) async {
- final Map<String, Object?>? tileInfo = await _mapsPlatform
- .channel(mapId)
+ final Map<String, Object?>? tileInfo = await _channelProvider(mapId)!
.invokeMapMethod<String, dynamic>(
'map#getTileOverlayInfo', <String, String>{
'tileOverlayId': tileOverlayId.value,
@@ -91,36 +83,31 @@
@override
Future<bool> isCompassEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isCompassEnabled'))!;
}
@override
Future<bool> isLiteModeEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isLiteModeEnabled'))!;
}
@override
Future<bool> isMapToolbarEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isMapToolbarEnabled'))!;
}
@override
Future<bool> isMyLocationButtonEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isMyLocationButtonEnabled'))!;
}
@override
Future<bool> isTrafficEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isTrafficEnabled'))!;
}
}
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
new file mode 100644
index 0000000..2c11053
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart
@@ -0,0 +1,696 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter/services.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+import 'package:stream_transform/stream_transform.dart';
+
+import 'google_map_inspector_android.dart';
+
+// TODO(stuartmorgan): Remove the dependency on platform interface toJson
+// methods. Channel serialization details should all be package-internal.
+
+/// Error thrown when an unknown map ID is provided to a method channel API.
+class UnknownMapIDError extends Error {
+ /// Creates an assertion error with the provided [mapId] and optional
+ /// [message].
+ UnknownMapIDError(this.mapId, [this.message]);
+
+ /// The unknown ID.
+ final int mapId;
+
+ /// Message describing the assertion error.
+ final Object? message;
+
+ @override
+ String toString() {
+ if (message != null) {
+ return 'Unknown map ID $mapId: ${Error.safeToString(message)}';
+ }
+ return 'Unknown map ID $mapId';
+ }
+}
+
+/// An implementation of [GoogleMapsFlutterPlatform] for Android.
+class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform {
+ /// Registers the Android implementation of GoogleMapsFlutterPlatform.
+ static void registerWith() {
+ GoogleMapsFlutterPlatform.instance = GoogleMapsFlutterAndroid();
+ }
+
+ // Keep a collection of id -> channel
+ // Every method call passes the int mapId
+ final Map<int, MethodChannel> _channels = <int, MethodChannel>{};
+
+ /// Accesses the MethodChannel associated to the passed mapId.
+ MethodChannel _channel(int mapId) {
+ final MethodChannel? channel = _channels[mapId];
+ if (channel == null) {
+ throw UnknownMapIDError(mapId);
+ }
+ return channel;
+ }
+
+ // Keep a collection of mapId to a map of TileOverlays.
+ final Map<int, Map<TileOverlayId, TileOverlay>> _tileOverlays =
+ <int, Map<TileOverlayId, TileOverlay>>{};
+
+ /// Returns the channel for [mapId], creating it if it doesn't already exist.
+ @visibleForTesting
+ MethodChannel ensureChannelInitialized(int mapId) {
+ MethodChannel? channel = _channels[mapId];
+ if (channel == null) {
+ channel = MethodChannel('plugins.flutter.dev/google_maps_android_$mapId');
+ channel.setMethodCallHandler(
+ (MethodCall call) => _handleMethodCall(call, mapId));
+ _channels[mapId] = channel;
+ }
+ return channel;
+ }
+
+ @override
+ Future<void> init(int mapId) {
+ final MethodChannel channel = ensureChannelInitialized(mapId);
+ return channel.invokeMethod<void>('map#waitForMap');
+ }
+
+ @override
+ void dispose({required int mapId}) {
+ // Noop!
+ }
+
+ // The controller we need to broadcast the different events coming
+ // from handleMethodCall.
+ //
+ // It is a `broadcast` because multiple controllers will connect to
+ // different stream views of this Controller.
+ final StreamController<MapEvent<Object?>> _mapEventStreamController =
+ StreamController<MapEvent<Object?>>.broadcast();
+
+ // Returns a filtered view of the events in the _controller, by mapId.
+ Stream<MapEvent<Object?>> _events(int mapId) =>
+ _mapEventStreamController.stream
+ .where((MapEvent<Object?> event) => event.mapId == mapId);
+
+ @override
+ Stream<CameraMoveStartedEvent> onCameraMoveStarted({required int mapId}) {
+ return _events(mapId).whereType<CameraMoveStartedEvent>();
+ }
+
+ @override
+ Stream<CameraMoveEvent> onCameraMove({required int mapId}) {
+ return _events(mapId).whereType<CameraMoveEvent>();
+ }
+
+ @override
+ Stream<CameraIdleEvent> onCameraIdle({required int mapId}) {
+ return _events(mapId).whereType<CameraIdleEvent>();
+ }
+
+ @override
+ Stream<MarkerTapEvent> onMarkerTap({required int mapId}) {
+ return _events(mapId).whereType<MarkerTapEvent>();
+ }
+
+ @override
+ Stream<InfoWindowTapEvent> onInfoWindowTap({required int mapId}) {
+ return _events(mapId).whereType<InfoWindowTapEvent>();
+ }
+
+ @override
+ Stream<MarkerDragStartEvent> onMarkerDragStart({required int mapId}) {
+ return _events(mapId).whereType<MarkerDragStartEvent>();
+ }
+
+ @override
+ Stream<MarkerDragEvent> onMarkerDrag({required int mapId}) {
+ return _events(mapId).whereType<MarkerDragEvent>();
+ }
+
+ @override
+ Stream<MarkerDragEndEvent> onMarkerDragEnd({required int mapId}) {
+ return _events(mapId).whereType<MarkerDragEndEvent>();
+ }
+
+ @override
+ Stream<PolylineTapEvent> onPolylineTap({required int mapId}) {
+ return _events(mapId).whereType<PolylineTapEvent>();
+ }
+
+ @override
+ Stream<PolygonTapEvent> onPolygonTap({required int mapId}) {
+ return _events(mapId).whereType<PolygonTapEvent>();
+ }
+
+ @override
+ Stream<CircleTapEvent> onCircleTap({required int mapId}) {
+ return _events(mapId).whereType<CircleTapEvent>();
+ }
+
+ @override
+ Stream<MapTapEvent> onTap({required int mapId}) {
+ return _events(mapId).whereType<MapTapEvent>();
+ }
+
+ @override
+ Stream<MapLongPressEvent> onLongPress({required int mapId}) {
+ return _events(mapId).whereType<MapLongPressEvent>();
+ }
+
+ Future<dynamic> _handleMethodCall(MethodCall call, int mapId) async {
+ switch (call.method) {
+ case 'camera#onMoveStarted':
+ _mapEventStreamController.add(CameraMoveStartedEvent(mapId));
+ break;
+ case 'camera#onMove':
+ _mapEventStreamController.add(CameraMoveEvent(
+ mapId,
+ CameraPosition.fromMap(call.arguments['position'])!,
+ ));
+ break;
+ case 'camera#onIdle':
+ _mapEventStreamController.add(CameraIdleEvent(mapId));
+ break;
+ case 'marker#onTap':
+ _mapEventStreamController.add(MarkerTapEvent(
+ mapId,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'marker#onDragStart':
+ _mapEventStreamController.add(MarkerDragStartEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'marker#onDrag':
+ _mapEventStreamController.add(MarkerDragEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'marker#onDragEnd':
+ _mapEventStreamController.add(MarkerDragEndEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'infoWindow#onTap':
+ _mapEventStreamController.add(InfoWindowTapEvent(
+ mapId,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'polyline#onTap':
+ _mapEventStreamController.add(PolylineTapEvent(
+ mapId,
+ PolylineId(call.arguments['polylineId'] as String),
+ ));
+ break;
+ case 'polygon#onTap':
+ _mapEventStreamController.add(PolygonTapEvent(
+ mapId,
+ PolygonId(call.arguments['polygonId'] as String),
+ ));
+ break;
+ case 'circle#onTap':
+ _mapEventStreamController.add(CircleTapEvent(
+ mapId,
+ CircleId(call.arguments['circleId'] as String),
+ ));
+ break;
+ case 'map#onTap':
+ _mapEventStreamController.add(MapTapEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ ));
+ break;
+ case 'map#onLongPress':
+ _mapEventStreamController.add(MapLongPressEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ ));
+ break;
+ case 'tileOverlay#getTile':
+ final Map<TileOverlayId, TileOverlay>? tileOverlaysForThisMap =
+ _tileOverlays[mapId];
+ final String tileOverlayId = call.arguments['tileOverlayId'] as String;
+ final TileOverlay? tileOverlay =
+ tileOverlaysForThisMap?[TileOverlayId(tileOverlayId)];
+ final TileProvider? tileProvider = tileOverlay?.tileProvider;
+ if (tileProvider == null) {
+ return TileProvider.noTile.toJson();
+ }
+ final Tile tile = await tileProvider.getTile(
+ call.arguments['x'] as int,
+ call.arguments['y'] as int,
+ call.arguments['zoom'] as int?,
+ );
+ return tile.toJson();
+ default:
+ throw MissingPluginException();
+ }
+ }
+
+ @override
+ Future<void> updateMapOptions(
+ Map<String, dynamic> optionsUpdate, {
+ required int mapId,
+ }) {
+ assert(optionsUpdate != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'map#update',
+ <String, dynamic>{
+ 'options': optionsUpdate,
+ },
+ );
+ }
+
+ @override
+ Future<void> updateMarkers(
+ MarkerUpdates markerUpdates, {
+ required int mapId,
+ }) {
+ assert(markerUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'markers#update',
+ markerUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updatePolygons(
+ PolygonUpdates polygonUpdates, {
+ required int mapId,
+ }) {
+ assert(polygonUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'polygons#update',
+ polygonUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updatePolylines(
+ PolylineUpdates polylineUpdates, {
+ required int mapId,
+ }) {
+ assert(polylineUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'polylines#update',
+ polylineUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updateCircles(
+ CircleUpdates circleUpdates, {
+ required int mapId,
+ }) {
+ assert(circleUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'circles#update',
+ circleUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updateTileOverlays({
+ required Set<TileOverlay> newTileOverlays,
+ required int mapId,
+ }) {
+ final Map<TileOverlayId, TileOverlay>? currentTileOverlays =
+ _tileOverlays[mapId];
+ final Set<TileOverlay> previousSet = currentTileOverlays != null
+ ? currentTileOverlays.values.toSet()
+ : <TileOverlay>{};
+ final _TileOverlayUpdates updates =
+ _TileOverlayUpdates.from(previousSet, newTileOverlays);
+ _tileOverlays[mapId] = keyTileOverlayId(newTileOverlays);
+ return _channel(mapId).invokeMethod<void>(
+ 'tileOverlays#update',
+ updates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> clearTileCache(
+ TileOverlayId tileOverlayId, {
+ required int mapId,
+ }) {
+ return _channel(mapId)
+ .invokeMethod<void>('tileOverlays#clearTileCache', <String, Object>{
+ 'tileOverlayId': tileOverlayId.value,
+ });
+ }
+
+ @override
+ Future<void> animateCamera(
+ CameraUpdate cameraUpdate, {
+ required int mapId,
+ }) {
+ return _channel(mapId)
+ .invokeMethod<void>('camera#animate', <String, Object>{
+ 'cameraUpdate': cameraUpdate.toJson(),
+ });
+ }
+
+ @override
+ Future<void> moveCamera(
+ CameraUpdate cameraUpdate, {
+ required int mapId,
+ }) {
+ return _channel(mapId).invokeMethod<void>('camera#move', <String, dynamic>{
+ 'cameraUpdate': cameraUpdate.toJson(),
+ });
+ }
+
+ @override
+ Future<void> setMapStyle(
+ String? mapStyle, {
+ required int mapId,
+ }) async {
+ final List<dynamic> successAndError = (await _channel(mapId)
+ .invokeMethod<List<dynamic>>('map#setStyle', mapStyle))!;
+ final bool success = successAndError[0] as bool;
+ if (!success) {
+ throw MapStyleException(successAndError[1] as String);
+ }
+ }
+
+ @override
+ Future<LatLngBounds> getVisibleRegion({
+ required int mapId,
+ }) async {
+ final Map<String, dynamic> latLngBounds = (await _channel(mapId)
+ .invokeMapMethod<String, dynamic>('map#getVisibleRegion'))!;
+ final LatLng southwest = LatLng.fromJson(latLngBounds['southwest'])!;
+ final LatLng northeast = LatLng.fromJson(latLngBounds['northeast'])!;
+
+ return LatLngBounds(northeast: northeast, southwest: southwest);
+ }
+
+ @override
+ Future<ScreenCoordinate> getScreenCoordinate(
+ LatLng latLng, {
+ required int mapId,
+ }) async {
+ final Map<String, int> point = (await _channel(mapId)
+ .invokeMapMethod<String, int>(
+ 'map#getScreenCoordinate', latLng.toJson()))!;
+
+ return ScreenCoordinate(x: point['x']!, y: point['y']!);
+ }
+
+ @override
+ Future<LatLng> getLatLng(
+ ScreenCoordinate screenCoordinate, {
+ required int mapId,
+ }) async {
+ final List<dynamic> latLng = (await _channel(mapId)
+ .invokeMethod<List<dynamic>>(
+ 'map#getLatLng', screenCoordinate.toJson()))!;
+ return LatLng(latLng[0] as double, latLng[1] as double);
+ }
+
+ @override
+ Future<void> showMarkerInfoWindow(
+ MarkerId markerId, {
+ required int mapId,
+ }) {
+ assert(markerId != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'markers#showInfoWindow', <String, String>{'markerId': markerId.value});
+ }
+
+ @override
+ Future<void> hideMarkerInfoWindow(
+ MarkerId markerId, {
+ required int mapId,
+ }) {
+ assert(markerId != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'markers#hideInfoWindow', <String, String>{'markerId': markerId.value});
+ }
+
+ @override
+ Future<bool> isMarkerInfoWindowShown(
+ MarkerId markerId, {
+ required int mapId,
+ }) async {
+ assert(markerId != null);
+ return (await _channel(mapId).invokeMethod<bool>(
+ 'markers#isInfoWindowShown',
+ <String, String>{'markerId': markerId.value}))!;
+ }
+
+ @override
+ Future<double> getZoomLevel({
+ required int mapId,
+ }) async {
+ return (await _channel(mapId).invokeMethod<double>('map#getZoomLevel'))!;
+ }
+
+ @override
+ Future<Uint8List?> takeSnapshot({
+ required int mapId,
+ }) {
+ return _channel(mapId).invokeMethod<Uint8List>('map#takeSnapshot');
+ }
+
+ /// Set [GoogleMapsFlutterPlatform] to use [AndroidViewSurface] to build the Google Maps widget.
+ ///
+ /// This implementation uses hybrid composition to render the Google Maps
+ /// Widget on Android. This comes at the cost of some performance on Android
+ /// versions below 10. See
+ /// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more
+ /// information.
+ ///
+ /// If set to true, the google map widget should be built with
+ /// [buildViewWithTextDirection] instead of [buildView].
+ ///
+ /// Defaults to false.
+ bool useAndroidViewSurface = false;
+
+ Widget _buildView(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required MapWidgetConfiguration widgetConfiguration,
+ MapObjects mapObjects = const MapObjects(),
+ Map<String, dynamic> mapOptions = const <String, dynamic>{},
+ }) {
+ final Map<String, dynamic> creationParams = <String, dynamic>{
+ 'initialCameraPosition':
+ widgetConfiguration.initialCameraPosition.toMap(),
+ 'options': mapOptions,
+ 'markersToAdd': serializeMarkerSet(mapObjects.markers),
+ 'polygonsToAdd': serializePolygonSet(mapObjects.polygons),
+ 'polylinesToAdd': serializePolylineSet(mapObjects.polylines),
+ 'circlesToAdd': serializeCircleSet(mapObjects.circles),
+ 'tileOverlaysToAdd': serializeTileOverlaySet(mapObjects.tileOverlays),
+ };
+
+ const String viewType = 'plugins.flutter.dev/google_maps_android';
+ if (useAndroidViewSurface) {
+ return PlatformViewLink(
+ viewType: viewType,
+ surfaceFactory: (
+ BuildContext context,
+ PlatformViewController controller,
+ ) {
+ return AndroidViewSurface(
+ controller: controller as AndroidViewController,
+ gestureRecognizers: widgetConfiguration.gestureRecognizers,
+ hitTestBehavior: PlatformViewHitTestBehavior.opaque,
+ );
+ },
+ onCreatePlatformView: (PlatformViewCreationParams params) {
+ final SurfaceAndroidViewController controller =
+ PlatformViewsService.initSurfaceAndroidView(
+ id: params.id,
+ viewType: viewType,
+ layoutDirection: widgetConfiguration.textDirection,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ onFocus: () => params.onFocusChanged(true),
+ );
+ controller.addOnPlatformViewCreatedListener(
+ params.onPlatformViewCreated,
+ );
+ controller.addOnPlatformViewCreatedListener(
+ onPlatformViewCreated,
+ );
+
+ controller.create();
+ return controller;
+ },
+ );
+ } else {
+ return AndroidView(
+ viewType: viewType,
+ onPlatformViewCreated: onPlatformViewCreated,
+ gestureRecognizers: widgetConfiguration.gestureRecognizers,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ );
+ }
+ }
+
+ @override
+ Widget buildViewWithConfiguration(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required MapWidgetConfiguration widgetConfiguration,
+ MapConfiguration mapConfiguration = const MapConfiguration(),
+ MapObjects mapObjects = const MapObjects(),
+ }) {
+ return _buildView(
+ creationId,
+ onPlatformViewCreated,
+ widgetConfiguration: widgetConfiguration,
+ mapObjects: mapObjects,
+ mapOptions: _jsonForMapConfiguration(mapConfiguration),
+ );
+ }
+
+ @override
+ Widget buildViewWithTextDirection(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required CameraPosition initialCameraPosition,
+ required TextDirection textDirection,
+ Set<Marker> markers = const <Marker>{},
+ Set<Polygon> polygons = const <Polygon>{},
+ Set<Polyline> polylines = const <Polyline>{},
+ Set<Circle> circles = const <Circle>{},
+ Set<TileOverlay> tileOverlays = const <TileOverlay>{},
+ Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
+ Map<String, dynamic> mapOptions = const <String, dynamic>{},
+ }) {
+ return _buildView(
+ creationId,
+ onPlatformViewCreated,
+ widgetConfiguration: MapWidgetConfiguration(
+ initialCameraPosition: initialCameraPosition,
+ textDirection: textDirection),
+ mapObjects: MapObjects(
+ markers: markers,
+ polygons: polygons,
+ polylines: polylines,
+ circles: circles,
+ tileOverlays: tileOverlays),
+ mapOptions: mapOptions,
+ );
+ }
+
+ @override
+ Widget buildView(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required CameraPosition initialCameraPosition,
+ Set<Marker> markers = const <Marker>{},
+ Set<Polygon> polygons = const <Polygon>{},
+ Set<Polyline> polylines = const <Polyline>{},
+ Set<Circle> circles = const <Circle>{},
+ Set<TileOverlay> tileOverlays = const <TileOverlay>{},
+ Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
+ Map<String, dynamic> mapOptions = const <String, dynamic>{},
+ }) {
+ return buildViewWithTextDirection(
+ creationId,
+ onPlatformViewCreated,
+ initialCameraPosition: initialCameraPosition,
+ textDirection: TextDirection.ltr,
+ markers: markers,
+ polygons: polygons,
+ polylines: polylines,
+ circles: circles,
+ tileOverlays: tileOverlays,
+ gestureRecognizers: gestureRecognizers,
+ mapOptions: mapOptions,
+ );
+ }
+
+ @override
+ @visibleForTesting
+ void enableDebugInspection() {
+ GoogleMapsInspectorPlatform.instance =
+ GoogleMapsInspectorAndroid((int mapId) => _channel(mapId));
+ }
+}
+
+Map<String, Object> _jsonForMapConfiguration(MapConfiguration config) {
+ final EdgeInsets? padding = config.padding;
+ return <String, Object>{
+ if (config.compassEnabled != null) 'compassEnabled': config.compassEnabled!,
+ if (config.mapToolbarEnabled != null)
+ 'mapToolbarEnabled': config.mapToolbarEnabled!,
+ if (config.cameraTargetBounds != null)
+ 'cameraTargetBounds': config.cameraTargetBounds!.toJson(),
+ if (config.mapType != null) 'mapType': config.mapType!.index,
+ if (config.minMaxZoomPreference != null)
+ 'minMaxZoomPreference': config.minMaxZoomPreference!.toJson(),
+ if (config.rotateGesturesEnabled != null)
+ 'rotateGesturesEnabled': config.rotateGesturesEnabled!,
+ if (config.scrollGesturesEnabled != null)
+ 'scrollGesturesEnabled': config.scrollGesturesEnabled!,
+ if (config.tiltGesturesEnabled != null)
+ 'tiltGesturesEnabled': config.tiltGesturesEnabled!,
+ if (config.zoomControlsEnabled != null)
+ 'zoomControlsEnabled': config.zoomControlsEnabled!,
+ if (config.zoomGesturesEnabled != null)
+ 'zoomGesturesEnabled': config.zoomGesturesEnabled!,
+ if (config.liteModeEnabled != null)
+ 'liteModeEnabled': config.liteModeEnabled!,
+ if (config.trackCameraPosition != null)
+ 'trackCameraPosition': config.trackCameraPosition!,
+ if (config.myLocationEnabled != null)
+ 'myLocationEnabled': config.myLocationEnabled!,
+ if (config.myLocationButtonEnabled != null)
+ 'myLocationButtonEnabled': config.myLocationButtonEnabled!,
+ if (padding != null)
+ 'padding': <double>[
+ padding.top,
+ padding.left,
+ padding.bottom,
+ padding.right,
+ ],
+ if (config.indoorViewEnabled != null)
+ 'indoorEnabled': config.indoorViewEnabled!,
+ if (config.trafficEnabled != null) 'trafficEnabled': config.trafficEnabled!,
+ if (config.buildingsEnabled != null)
+ 'buildingsEnabled': config.buildingsEnabled!,
+ };
+}
+
+/// Update specification for a set of [TileOverlay]s.
+// TODO(stuartmorgan): Fix the missing export of this class in the platform
+// interface, and remove this copy.
+class _TileOverlayUpdates extends MapsObjectUpdates<TileOverlay> {
+ /// Computes [TileOverlayUpdates] given previous and current [TileOverlay]s.
+ _TileOverlayUpdates.from(Set<TileOverlay> previous, Set<TileOverlay> current)
+ : super.from(previous, current, objectName: 'tileOverlay');
+
+ /// Set of TileOverlays to be added in this update.
+ Set<TileOverlay> get tileOverlaysToAdd => objectsToAdd;
+
+ /// Set of TileOverlayIds to be removed in this update.
+ Set<TileOverlayId> get tileOverlayIdsToRemove =>
+ objectIdsToRemove.cast<TileOverlayId>();
+
+ /// Set of TileOverlays to be changed in this update.
+ Set<TileOverlay> get tileOverlaysToChange => objectsToChange;
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml
new file mode 100644
index 0000000..a0417d5
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml
@@ -0,0 +1,31 @@
+name: google_maps_flutter_android
+description: Android implementation of the google_maps_flutter plugin.
+repository: https://github.com/flutter/plugins/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.1.10
+
+environment:
+ sdk: ">=2.14.0 <3.0.0"
+ flutter: ">=2.8.0"
+
+flutter:
+ plugin:
+ implements: google_maps_flutter
+ platforms:
+ android:
+ package: io.flutter.plugins.googlemaps
+ pluginClass: GoogleMapsPlugin
+ dartPluginClass: GoogleMapsFlutterAndroid
+
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_plugin_android_lifecycle: ^2.0.1
+ google_maps_flutter_platform_interface: ^2.2.1
+ stream_transform: ^2.0.0
+
+dev_dependencies:
+ async: ^2.5.0
+ flutter_test:
+ sdk: flutter
+ plugin_platform_interface: ^2.0.0
diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart
new file mode 100644
index 0000000..cba23d0
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart
@@ -0,0 +1,153 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:async/async.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ late List<String> log;
+
+ setUp(() async {
+ log = <String>[];
+ });
+
+ /// Initializes a map with the given ID and canned responses, logging all
+ /// calls to [log].
+ void configureMockMap(
+ GoogleMapsFlutterAndroid maps, {
+ required int mapId,
+ required Future<dynamic>? Function(MethodCall call) handler,
+ }) {
+ maps
+ .ensureChannelInitialized(mapId)
+ .setMockMethodCallHandler((MethodCall methodCall) {
+ log.add(methodCall.method);
+ return handler(methodCall);
+ });
+ }
+
+ Future<void> sendPlatformMessage(
+ int mapId, String method, Map<dynamic, dynamic> data) async {
+ final ByteData byteData =
+ const StandardMethodCodec().encodeMethodCall(MethodCall(method, data));
+ await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
+ .handlePlatformMessage('plugins.flutter.dev/google_maps_android_$mapId',
+ byteData, (ByteData? data) {});
+ }
+
+ test('registers instance', () async {
+ GoogleMapsFlutterAndroid.registerWith();
+ expect(GoogleMapsFlutterPlatform.instance, isA<GoogleMapsFlutterAndroid>());
+ });
+
+ // Calls each method that uses invokeMethod with a return type other than
+ // void to ensure that the casting/nullability handling succeeds.
+ //
+ // TODO(stuartmorgan): Remove this once there is real test coverage of
+ // each method, since that would cover this issue.
+ test('non-void invokeMethods handle types correctly', () async {
+ const int mapId = 0;
+ final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid();
+ configureMockMap(maps, mapId: mapId,
+ handler: (MethodCall methodCall) async {
+ switch (methodCall.method) {
+ case 'map#getLatLng':
+ return <dynamic>[1.0, 2.0];
+ case 'markers#isInfoWindowShown':
+ return true;
+ case 'map#getZoomLevel':
+ return 2.5;
+ case 'map#takeSnapshot':
+ return null;
+ }
+ });
+
+ await maps.getLatLng(const ScreenCoordinate(x: 0, y: 0), mapId: mapId);
+ await maps.isMarkerInfoWindowShown(const MarkerId(''), mapId: mapId);
+ await maps.getZoomLevel(mapId: mapId);
+ await maps.takeSnapshot(mapId: mapId);
+ // Check that all the invokeMethod calls happened.
+ expect(log, <String>[
+ 'map#getLatLng',
+ 'markers#isInfoWindowShown',
+ 'map#getZoomLevel',
+ 'map#takeSnapshot',
+ ]);
+ });
+
+ test('markers send drag event to correct streams', () async {
+ const int mapId = 1;
+ final Map<dynamic, dynamic> jsonMarkerDragStartEvent = <dynamic, dynamic>{
+ 'mapId': mapId,
+ 'markerId': 'drag-start-marker',
+ 'position': <double>[1.0, 1.0]
+ };
+ final Map<dynamic, dynamic> jsonMarkerDragEvent = <dynamic, dynamic>{
+ 'mapId': mapId,
+ 'markerId': 'drag-marker',
+ 'position': <double>[1.0, 1.0]
+ };
+ final Map<dynamic, dynamic> jsonMarkerDragEndEvent = <dynamic, dynamic>{
+ 'mapId': mapId,
+ 'markerId': 'drag-end-marker',
+ 'position': <double>[1.0, 1.0]
+ };
+
+ final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid();
+ maps.ensureChannelInitialized(mapId);
+
+ final StreamQueue<MarkerDragStartEvent> markerDragStartStream =
+ StreamQueue<MarkerDragStartEvent>(maps.onMarkerDragStart(mapId: mapId));
+ final StreamQueue<MarkerDragEvent> markerDragStream =
+ StreamQueue<MarkerDragEvent>(maps.onMarkerDrag(mapId: mapId));
+ final StreamQueue<MarkerDragEndEvent> markerDragEndStream =
+ StreamQueue<MarkerDragEndEvent>(maps.onMarkerDragEnd(mapId: mapId));
+
+ await sendPlatformMessage(
+ mapId, 'marker#onDragStart', jsonMarkerDragStartEvent);
+ await sendPlatformMessage(mapId, 'marker#onDrag', jsonMarkerDragEvent);
+ await sendPlatformMessage(
+ mapId, 'marker#onDragEnd', jsonMarkerDragEndEvent);
+
+ expect((await markerDragStartStream.next).value.value,
+ equals('drag-start-marker'));
+ expect((await markerDragStream.next).value.value, equals('drag-marker'));
+ expect((await markerDragEndStream.next).value.value,
+ equals('drag-end-marker'));
+ });
+
+ test(
+ 'Default widget is AndroidView',
+ () async {
+ final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid();
+ final Widget widget = maps.buildViewWithConfiguration(1, (int _) {},
+ widgetConfiguration: const MapWidgetConfiguration(
+ initialCameraPosition:
+ CameraPosition(target: LatLng(0, 0), zoom: 1),
+ textDirection: TextDirection.ltr));
+
+ expect(widget, isA<AndroidView>());
+ },
+ );
+
+ testWidgets('Use PlatformViewLink when using surface view',
+ (WidgetTester tester) async {
+ final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid();
+ maps.useAndroidViewSurface = true;
+
+ final Widget widget = maps.buildViewWithConfiguration(1, (int _) {},
+ widgetConfiguration: const MapWidgetConfiguration(
+ initialCameraPosition:
+ CameraPosition(target: LatLng(0, 0), zoom: 1),
+ textDirection: TextDirection.ltr));
+
+ expect(widget, isA<PlatformViewLink>());
+ });
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/AUTHORS b/packages/google_maps_flutter/google_maps_flutter_ios/AUTHORS
new file mode 100644
index 0000000..9f1b53e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/AUTHORS
@@ -0,0 +1,67 @@
+# Below is a list of people and organizations that have contributed
+# to the Flutter project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
+The Chromium Authors
+German Saprykin <saprykin.h@gmail.com>
+Benjamin Sauer <sauer.benjamin@gmail.com>
+larsenthomasj@gmail.com
+Ali Bitek <alibitek@protonmail.ch>
+Pol Batlló <pol.batllo@gmail.com>
+Anatoly Pulyaevskiy
+Hayden Flinner <haydenflinner@gmail.com>
+Stefano Rodriguez <hlsroddy@gmail.com>
+Salvatore Giordano <salvatoregiordanoo@gmail.com>
+Brian Armstrong <brian@flutter.institute>
+Paul DeMarco <paulmdemarco@gmail.com>
+Fabricio Nogueira <feufeu@gmail.com>
+Simon Lightfoot <simon@devangels.london>
+Ashton Thomas <ashton@acrinta.com>
+Thomas Danner <thmsdnnr@gmail.com>
+Diego Velásquez <diego.velasquez.lopez@gmail.com>
+Hajime Nakamura <nkmrhj@gmail.com>
+Tuyển Vũ Xuân <netsoft1985@gmail.com>
+Miguel Ruivo <miguel@miguelruivo.com>
+Sarthak Verma <sarthak@artiosys.com>
+Mike Diarmid <mike@invertase.io>
+Invertase <oss@invertase.io>
+Elliot Hesp <elliot@invertase.io>
+Vince Varga <vince.varga@smaho.com>
+Aawaz Gyawali <awazgyawali@gmail.com>
+EUI Limited <ian.evans3@admiralgroup.co.uk>
+Katarina Sheremet <katarina@sheremet.ch>
+Thomas Stockx <thomas@stockxit.com>
+Sarbagya Dhaubanjar <sarbagyastha@gmail.com>
+Ozkan Eksi <ozeksi@gmail.com>
+Rishab Nayak <rishab@bu.edu>
+ko2ic <ko2ic.dev@gmail.com>
+Jonathan Younger <jonathan@daikini.com>
+Jose Sanchez <josesm82@gmail.com>
+Debkanchan Samadder <debu.samadder@gmail.com>
+Audrius Karosevicius <audrius.karosevicius@gmail.com>
+Lukasz Piliszczuk <lukasz@intheloup.io>
+SoundReply Solutions GmbH <ch@soundreply.com>
+Rafal Wachol <rwachol@gmail.com>
+Pau Picas <pau.picas@gmail.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Alexandru Tuca <salexandru.tuca@outlook.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Rhodes Davis Jr. <rody.davis.jr@gmail.com>
+Luigi Agosti <luigi@tengio.com>
+Quentin Le Guennec <quentin@tengio.com>
+Koushik Ravikumar <koushik@tengio.com>
+Nissim Dsilva <nissim@tengio.com>
+Giancarlo Rocha <giancarloiff@gmail.com>
+Ryo Miyake <ryo@miyake.id>
+Théo Champion <contact.theochampion@gmail.com>
+Kazuki Yamaguchi <y.kazuki0614n@gmail.com>
+Eitan Schwartz <eshvartz@gmail.com>
+Chris Rutkowski <chrisrutkowski89@gmail.com>
+Juan Alvarez <juan.alvarez@resideo.com>
+Aleksandr Yurkovskiy <sanekyy@gmail.com>
+Anton Borries <mail@antonborri.es>
+Alex Li <google@alexv525.com>
+Rahul Raj <64.rahulraj@gmail.com>
+Taha Tesser <tesser@gmail.com>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md
new file mode 100644
index 0000000..e4b243a
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md
@@ -0,0 +1,4 @@
+## 2.1.10
+
+* Splits iOS implementation out of `google_maps_flutter` as a federated
+ implementation.
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/LICENSE b/packages/google_maps_flutter/google_maps_flutter_ios/LICENSE
new file mode 100644
index 0000000..c6823b8
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2013 The Flutter Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/README.md
new file mode 100644
index 0000000..cd5d3f1
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/README.md
@@ -0,0 +1,12 @@
+# google\_maps\_flutter\_ios
+
+The iOS implementation of [`google_maps_flutter`][1].
+
+## Usage
+
+This package is [endorsed][2], which means you can simply use
+`google_maps_flutter` normally. This package will be automatically included in
+your app when you do.
+
+[1]: https://pub.dev/packages/google_maps_flutter
+[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/.metadata b/packages/google_maps_flutter/google_maps_flutter_ios/example/.metadata
new file mode 100644
index 0000000..46e884c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/.metadata
@@ -0,0 +1,8 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 3ea4d06340a97a1e9d7cae97567c64e0569dcaa2
+ channel: beta
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md
new file mode 100644
index 0000000..c885264
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md
@@ -0,0 +1,3 @@
+# google_maps_flutter_example
+
+Demonstrates how to use the google_maps_flutter plugin.
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/2.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/2.0x/red_square.png
new file mode 100644
index 0000000..0f82237
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/2.0x/red_square.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/3.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/3.0x/red_square.png
new file mode 100644
index 0000000..7e27399
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/3.0x/red_square.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/night_mode.json b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/night_mode.json
new file mode 100644
index 0000000..1f16e00
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/night_mode.json
@@ -0,0 +1,162 @@
+[
+ {
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#242f3e"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#746855"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.stroke",
+ "stylers": [
+ {
+ "color": "#242f3e"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative.locality",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#263c3f"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#6b9a76"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#38414e"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry.stroke",
+ "stylers": [
+ {
+ "color": "#212a37"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#9ca5b3"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#746855"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry.stroke",
+ "stylers": [
+ {
+ "color": "#1f2835"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#f3d19c"
+ }
+ ]
+ },
+ {
+ "featureType": "transit",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#2f3948"
+ }
+ ]
+ },
+ {
+ "featureType": "transit.station",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#17263c"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#515c6d"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.stroke",
+ "stylers": [
+ {
+ "color": "#17263c"
+ }
+ ]
+ }
+]
+
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/red_square.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/red_square.png
new file mode 100644
index 0000000..650a2de
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/red_square.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart
new file mode 100644
index 0000000..a2299e6
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart
@@ -0,0 +1,1095 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:typed_data';
+import 'dart:ui' as ui;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:google_maps_flutter_example/example_google_map.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+import 'package:integration_test/integration_test.dart';
+
+const LatLng _kInitialMapCenter = LatLng(0, 0);
+const double _kInitialZoomLevel = 5;
+const CameraPosition _kInitialCameraPosition =
+ CameraPosition(target: _kInitialMapCenter, zoom: _kInitialZoomLevel);
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+ GoogleMapsFlutterPlatform.instance.enableDebugInspection();
+
+ testWidgets('testCompassToggle', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ compassEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool compassEnabled = await inspector.isCompassEnabled(mapId: mapId);
+ expect(compassEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ compassEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ compassEnabled = await inspector.isCompassEnabled(mapId: mapId);
+ expect(compassEnabled, true);
+ });
+
+ testWidgets('testMapToolbar returns false', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ mapToolbarEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool mapToolbarEnabled =
+ await inspector.isMapToolbarEnabled(mapId: mapId);
+ // This is only supported on Android, so should always return false.
+ expect(mapToolbarEnabled, false);
+ });
+
+ testWidgets('updateMinMaxZoomLevels', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ const MinMaxZoomPreference initialZoomLevel = MinMaxZoomPreference(4, 8);
+ const MinMaxZoomPreference finalZoomLevel = MinMaxZoomPreference(6, 10);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ minMaxZoomPreference: initialZoomLevel,
+ onMapCreated: (ExampleGoogleMapController c) async {
+ controllerCompleter.complete(c);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+
+ MinMaxZoomPreference zoomLevel =
+ await inspector.getMinMaxZoomLevels(mapId: controller.mapId);
+ expect(zoomLevel, equals(initialZoomLevel));
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ minMaxZoomPreference: finalZoomLevel,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ zoomLevel = await inspector.getMinMaxZoomLevels(mapId: controller.mapId);
+ expect(zoomLevel, equals(finalZoomLevel));
+ });
+
+ testWidgets('testZoomGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ zoomGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool zoomGesturesEnabled =
+ await inspector.areZoomGesturesEnabled(mapId: mapId);
+ expect(zoomGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ zoomGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ zoomGesturesEnabled = await inspector.areZoomGesturesEnabled(mapId: mapId);
+ expect(zoomGesturesEnabled, true);
+ });
+
+ testWidgets('testZoomControlsEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool zoomControlsEnabled =
+ await inspector.areZoomControlsEnabled(mapId: mapId);
+
+ /// Zoom Controls functionality is not available on iOS at the moment.
+ expect(zoomControlsEnabled, false);
+ });
+
+ testWidgets('testRotateGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ rotateGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool rotateGesturesEnabled =
+ await inspector.areRotateGesturesEnabled(mapId: mapId);
+ expect(rotateGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ rotateGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ rotateGesturesEnabled =
+ await inspector.areRotateGesturesEnabled(mapId: mapId);
+ expect(rotateGesturesEnabled, true);
+ });
+
+ testWidgets('testTiltGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tiltGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool tiltGesturesEnabled =
+ await inspector.areTiltGesturesEnabled(mapId: mapId);
+ expect(tiltGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tiltGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ tiltGesturesEnabled = await inspector.areTiltGesturesEnabled(mapId: mapId);
+ expect(tiltGesturesEnabled, true);
+ });
+
+ testWidgets('testScrollGesturesEnabled', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ scrollGesturesEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool scrollGesturesEnabled =
+ await inspector.areScrollGesturesEnabled(mapId: mapId);
+ expect(scrollGesturesEnabled, false);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ scrollGesturesEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ scrollGesturesEnabled =
+ await inspector.areScrollGesturesEnabled(mapId: mapId);
+ expect(scrollGesturesEnabled, true);
+ });
+
+ testWidgets('testInitialCenterLocationAtCenter', (WidgetTester tester) async {
+ await tester.binding.setSurfaceSize(const Size(800, 600));
+
+ final Completer<ExampleGoogleMapController> mapControllerCompleter =
+ Completer<ExampleGoogleMapController>();
+ final Key key = GlobalKey();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapControllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+ final ExampleGoogleMapController mapController =
+ await mapControllerCompleter.future;
+
+ await tester.pumpAndSettle();
+
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ final ScreenCoordinate coordinate =
+ await mapController.getScreenCoordinate(_kInitialCameraPosition.target);
+ final Rect rect = tester.getRect(find.byKey(key));
+ expect(coordinate.x, (rect.center.dx - rect.topLeft.dx).round());
+ expect(coordinate.y, (rect.center.dy - rect.topLeft.dy).round());
+
+ await tester.binding.setSurfaceSize(null);
+ });
+
+ testWidgets('testGetVisibleRegion', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final LatLngBounds zeroLatLngBounds = LatLngBounds(
+ southwest: const LatLng(0, 0), northeast: const LatLng(0, 0));
+
+ final Completer<ExampleGoogleMapController> mapControllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapControllerCompleter.complete(controller);
+ },
+ ),
+ ));
+ await tester.pumpAndSettle();
+
+ final ExampleGoogleMapController mapController =
+ await mapControllerCompleter.future;
+
+ final LatLngBounds firstVisibleRegion =
+ await mapController.getVisibleRegion();
+
+ expect(firstVisibleRegion, isNotNull);
+ expect(firstVisibleRegion.southwest, isNotNull);
+ expect(firstVisibleRegion.northeast, isNotNull);
+ expect(firstVisibleRegion, isNot(zeroLatLngBounds));
+ expect(firstVisibleRegion.contains(_kInitialMapCenter), isTrue);
+
+ // Making a new `LatLngBounds` about (10, 10) distance south west to the `firstVisibleRegion`.
+ // The size of the `LatLngBounds` is 10 by 10.
+ final LatLng southWest = LatLng(firstVisibleRegion.southwest.latitude - 20,
+ firstVisibleRegion.southwest.longitude - 20);
+ final LatLng northEast = LatLng(firstVisibleRegion.southwest.latitude - 10,
+ firstVisibleRegion.southwest.longitude - 10);
+ final LatLng newCenter = LatLng(
+ (northEast.latitude + southWest.latitude) / 2,
+ (northEast.longitude + southWest.longitude) / 2,
+ );
+
+ expect(firstVisibleRegion.contains(northEast), isFalse);
+ expect(firstVisibleRegion.contains(southWest), isFalse);
+
+ final LatLngBounds latLngBounds =
+ LatLngBounds(southwest: southWest, northeast: northEast);
+
+ // TODO(iskakaushik): non-zero padding is needed for some device configurations
+ // https://github.com/flutter/flutter/issues/30575
+ const double padding = 0;
+ await mapController
+ .moveCamera(CameraUpdate.newLatLngBounds(latLngBounds, padding));
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final LatLngBounds secondVisibleRegion =
+ await mapController.getVisibleRegion();
+
+ expect(secondVisibleRegion, isNotNull);
+ expect(secondVisibleRegion.southwest, isNotNull);
+ expect(secondVisibleRegion.northeast, isNotNull);
+ expect(secondVisibleRegion, isNot(zeroLatLngBounds));
+
+ expect(firstVisibleRegion, isNot(secondVisibleRegion));
+ expect(secondVisibleRegion.contains(newCenter), isTrue);
+ });
+
+ testWidgets('testTraffic', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ trafficEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId);
+ expect(isTrafficEnabled, true);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ trafficEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId);
+ expect(isTrafficEnabled, false);
+ });
+
+ testWidgets('testBuildings', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ buildingsEnabled: true,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool isBuildingsEnabled =
+ await inspector.areBuildingsEnabled(mapId: mapId);
+ expect(isBuildingsEnabled, true);
+ });
+
+ testWidgets('testMyLocationButtonToggle', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: true,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ bool myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, true);
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: false,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ));
+
+ myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, false);
+ });
+
+ testWidgets('testMyLocationButton initial value false',
+ (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: false,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, false);
+ });
+
+ testWidgets('testMyLocationButton initial value true',
+ (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<int> mapIdCompleter = Completer<int>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ myLocationButtonEnabled: true,
+ myLocationEnabled: false,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+ final bool myLocationButtonEnabled =
+ await inspector.isMyLocationButtonEnabled(mapId: mapId);
+ expect(myLocationButtonEnabled, true);
+ });
+
+ testWidgets('testSetMapStyle valid Json String', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+ const String mapStyle =
+ '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]';
+ await controller.setMapStyle(mapStyle);
+ });
+
+ testWidgets('testSetMapStyle invalid Json String',
+ (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ try {
+ await controller.setMapStyle('invalid_value');
+ fail('expected MapStyleException');
+ } on MapStyleException catch (e) {
+ expect(e.cause, isNotNull);
+ }
+ });
+
+ testWidgets('testSetMapStyle null string', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+ await controller.setMapStyle(null);
+ });
+
+ testWidgets('testGetLatLng', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ final LatLngBounds visibleRegion = await controller.getVisibleRegion();
+ final LatLng topLeft =
+ await controller.getLatLng(const ScreenCoordinate(x: 0, y: 0));
+ final LatLng northWest = LatLng(
+ visibleRegion.northeast.latitude,
+ visibleRegion.southwest.longitude,
+ );
+
+ expect(topLeft, northWest);
+ });
+
+ testWidgets('testGetZoomLevel', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ double zoom = await controller.getZoomLevel();
+ expect(zoom, _kInitialZoomLevel);
+
+ await controller.moveCamera(CameraUpdate.zoomTo(7));
+ await tester.pumpAndSettle();
+ zoom = await controller.getZoomLevel();
+ expect(zoom, equals(7));
+ });
+
+ testWidgets('testScreenCoordinate', (WidgetTester tester) async {
+ final Key key = GlobalKey();
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ));
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ final LatLngBounds visibleRegion = await controller.getVisibleRegion();
+ final LatLng northWest = LatLng(
+ visibleRegion.northeast.latitude,
+ visibleRegion.southwest.longitude,
+ );
+ final ScreenCoordinate topLeft =
+ await controller.getScreenCoordinate(northWest);
+ expect(topLeft, const ScreenCoordinate(x: 0, y: 0));
+ });
+
+ testWidgets('testResizeWidget', (WidgetTester tester) async {
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+ final ExampleGoogleMap map = ExampleGoogleMap(
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) async {
+ controllerCompleter.complete(controller);
+ },
+ );
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: MaterialApp(
+ home: Scaffold(
+ body: SizedBox(height: 100, width: 100, child: map)))));
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: MaterialApp(
+ home: Scaffold(
+ body: SizedBox(height: 400, width: 400, child: map)))));
+
+ await tester.pumpAndSettle();
+ // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+ // in `mapRendered`.
+ // https://github.com/flutter/flutter/issues/54758
+ await Future<void>.delayed(const Duration(seconds: 1));
+
+ // Simple call to make sure that the app hasn't crashed.
+ final LatLngBounds bounds1 = await controller.getVisibleRegion();
+ final LatLngBounds bounds2 = await controller.getVisibleRegion();
+ expect(bounds1, bounds2);
+ });
+
+ testWidgets('testToggleInfoWindow', (WidgetTester tester) async {
+ const Marker marker = Marker(
+ markerId: MarkerId('marker'),
+ infoWindow: InfoWindow(title: 'InfoWindow'));
+ final Set<Marker> markers = <Marker>{marker};
+
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
+ markers: markers,
+ onMapCreated: (ExampleGoogleMapController googleMapController) {
+ controllerCompleter.complete(googleMapController);
+ },
+ ),
+ ));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+
+ bool iwVisibleStatus =
+ await controller.isMarkerInfoWindowShown(marker.markerId);
+ expect(iwVisibleStatus, false);
+
+ await controller.showMarkerInfoWindow(marker.markerId);
+ iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId);
+ expect(iwVisibleStatus, true);
+
+ await controller.hideMarkerInfoWindow(marker.markerId);
+ iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId);
+ expect(iwVisibleStatus, false);
+ });
+
+ testWidgets('fromAssetImage', (WidgetTester tester) async {
+ const double pixelRatio = 2;
+ const ImageConfiguration imageConfiguration =
+ ImageConfiguration(devicePixelRatio: pixelRatio);
+ final BitmapDescriptor mip = await BitmapDescriptor.fromAssetImage(
+ imageConfiguration, 'red_square.png');
+ final BitmapDescriptor scaled = await BitmapDescriptor.fromAssetImage(
+ imageConfiguration, 'red_square.png',
+ mipmaps: false);
+ expect((mip.toJson() as List<dynamic>)[2], 1);
+ expect((scaled.toJson() as List<dynamic>)[2], 2);
+ });
+
+ testWidgets('testTakeSnapshot', (WidgetTester tester) async {
+ final Completer<ExampleGoogleMapController> controllerCompleter =
+ Completer<ExampleGoogleMapController>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final ExampleGoogleMapController controller =
+ await controllerCompleter.future;
+ final Uint8List? bytes = await controller.takeSnapshot();
+ expect(bytes?.isNotEmpty, true);
+ });
+
+ testWidgets(
+ 'set tileOverlay correctly',
+ (WidgetTester tester) async {
+ final Completer<int> mapIdCompleter = Completer<int>();
+ final TileOverlay tileOverlay1 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 2,
+ visible: true,
+ transparency: 0.2,
+ fadeIn: true,
+ );
+
+ final TileOverlay tileOverlay2 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_2'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 1,
+ visible: false,
+ transparency: 0.3,
+ fadeIn: false,
+ );
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1, tileOverlay2},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ),
+ );
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+
+ final TileOverlay tileOverlayInfo1 = (await inspector
+ .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!;
+ final TileOverlay tileOverlayInfo2 = (await inspector
+ .getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId))!;
+
+ expect(tileOverlayInfo1.visible, isTrue);
+ expect(tileOverlayInfo1.fadeIn, isTrue);
+ expect(
+ tileOverlayInfo1.transparency, moreOrLessEquals(0.2, epsilon: 0.001));
+ expect(tileOverlayInfo1.zIndex, 2);
+
+ expect(tileOverlayInfo2.visible, isFalse);
+ expect(tileOverlayInfo2.fadeIn, isFalse);
+ expect(
+ tileOverlayInfo2.transparency, moreOrLessEquals(0.3, epsilon: 0.001));
+ expect(tileOverlayInfo2.zIndex, 1);
+ },
+ );
+
+ testWidgets(
+ 'update tileOverlays correctly',
+ (WidgetTester tester) async {
+ final Completer<int> mapIdCompleter = Completer<int>();
+ final Key key = GlobalKey();
+ final TileOverlay tileOverlay1 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 2,
+ visible: true,
+ transparency: 0.2,
+ fadeIn: true,
+ );
+
+ final TileOverlay tileOverlay2 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_2'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 3,
+ visible: true,
+ transparency: 0.5,
+ fadeIn: true,
+ );
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1, tileOverlay2},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ),
+ );
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+
+ final TileOverlay tileOverlay1New = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 1,
+ visible: false,
+ transparency: 0.3,
+ fadeIn: false,
+ );
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1New},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('update: OnMapCreated should get called only once.');
+ },
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ final TileOverlay tileOverlayInfo1 = (await inspector
+ .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!;
+ final TileOverlay? tileOverlayInfo2 =
+ await inspector.getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId);
+
+ expect(tileOverlayInfo1.visible, isFalse);
+ expect(tileOverlayInfo1.fadeIn, isFalse);
+ expect(
+ tileOverlayInfo1.transparency, moreOrLessEquals(0.3, epsilon: 0.001));
+ expect(tileOverlayInfo1.zIndex, 1);
+
+ expect(tileOverlayInfo2, isNull);
+ },
+ );
+
+ testWidgets(
+ 'remove tileOverlays correctly',
+ (WidgetTester tester) async {
+ final Completer<int> mapIdCompleter = Completer<int>();
+ final Key key = GlobalKey();
+ final TileOverlay tileOverlay1 = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ zIndex: 2,
+ visible: true,
+ transparency: 0.2,
+ fadeIn: true,
+ );
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ tileOverlays: <TileOverlay>{tileOverlay1},
+ onMapCreated: (ExampleGoogleMapController controller) {
+ mapIdCompleter.complete(controller.mapId);
+ },
+ ),
+ ),
+ );
+
+ final int mapId = await mapIdCompleter.future;
+ final GoogleMapsInspectorPlatform inspector =
+ GoogleMapsInspectorPlatform.instance!;
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: ExampleGoogleMap(
+ key: key,
+ initialCameraPosition: _kInitialCameraPosition,
+ onMapCreated: (ExampleGoogleMapController controller) {
+ fail('OnMapCreated should get called only once.');
+ },
+ ),
+ ),
+ );
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+ final TileOverlay? tileOverlayInfo1 =
+ await inspector.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId);
+
+ expect(tileOverlayInfo1, isNull);
+ },
+ );
+}
+
+class _DebugTileProvider implements TileProvider {
+ _DebugTileProvider() {
+ boxPaint.isAntiAlias = true;
+ boxPaint.color = Colors.blue;
+ boxPaint.strokeWidth = 2.0;
+ boxPaint.style = PaintingStyle.stroke;
+ }
+
+ static const int width = 100;
+ static const int height = 100;
+ static final Paint boxPaint = Paint();
+ static const TextStyle textStyle = TextStyle(
+ color: Colors.red,
+ fontSize: 20,
+ );
+
+ @override
+ Future<Tile> getTile(int x, int y, int? zoom) async {
+ final ui.PictureRecorder recorder = ui.PictureRecorder();
+ final Canvas canvas = Canvas(recorder);
+ final TextSpan textSpan = TextSpan(
+ text: '$x,$y',
+ style: textStyle,
+ );
+ final TextPainter textPainter = TextPainter(
+ text: textSpan,
+ textDirection: TextDirection.ltr,
+ );
+ textPainter.layout(
+ minWidth: 0.0,
+ maxWidth: width.toDouble(),
+ );
+ const Offset offset = Offset(0, 0);
+ textPainter.paint(canvas, offset);
+ canvas.drawRect(
+ Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint);
+ final ui.Picture picture = recorder.endRecording();
+ final Uint8List byteData = await picture
+ .toImage(width, height)
+ .then((ui.Image image) =>
+ image.toByteData(format: ui.ImageByteFormat.png))
+ .then((ByteData? byteData) => byteData!.buffer.asUint8List());
+ return Tile(width, height, byteData);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..3a9c234
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>App</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.flutter.flutter.app</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>App</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>arm64</string>
+ </array>
+ <key>MinimumOSVersion</key>
+ <string>9.0</string>
+</dict>
+</plist>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Debug.xcconfig b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..e8efba1
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Release.xcconfig b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..399e934
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Podfile
new file mode 100644
index 0000000..14b4bdc
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Podfile
@@ -0,0 +1,49 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+
+ pod 'OCMock', '~> 3.9.1'
+ end
+ target 'RunnerUITests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ target.build_configurations.each do |build_configuration|
+ build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386'
+ end
+ end
+end
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..343e050
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,789 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */; };
+ 4A097997B7B27CE82FFC3AB8 /* libPods-RunnerUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */; };
+ 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; };
+ 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; };
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
+ 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */; };
+ F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */; };
+ F7151F21265D7EE50028CB91 /* GoogleMapsUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */; };
+ FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ F7151F15265D7ED70028CB91 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+ F7151F23265D7EE50028CB91 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = "<group>"; };
+ 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; };
+ 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.release.xcconfig"; sourceTree = "<group>"; };
+ 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
+ 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PartiallyMockedMapView.h; sourceTree = "<group>"; };
+ 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PartiallyMockedMapView.m; sourceTree = "<group>"; };
+ B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+ DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.debug.xcconfig"; sourceTree = "<group>"; };
+ E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
+ EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
+ F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ F7151F10265D7ED70028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsTests.m; sourceTree = "<group>"; };
+ F7151F14265D7ED70028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ F7151F1E265D7EE50028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsUITests.m; sourceTree = "<group>"; };
+ F7151F22265D7EE50028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F7151F0D265D7ED70028CB91 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */,
+ FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F7151F1B265D7EE50028CB91 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4A097997B7B27CE82FFC3AB8 /* libPods-RunnerUITests.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 1E7CF0857EFC88FC263CF3B2 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 68E472692836FF0C00BDDDAC /* MapKit.framework */,
+ 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */,
+ F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */,
+ DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "<group>";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ F7151F11265D7ED70028CB91 /* RunnerTests */,
+ F7151F1F265D7EE50028CB91 /* RunnerUITests */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ A189CFE5474BF8A07908B2E0 /* Pods */,
+ 1E7CF0857EFC88FC263CF3B2 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ F7151F10265D7ED70028CB91 /* RunnerTests.xctest */,
+ F7151F1E265D7EE50028CB91 /* RunnerUITests.xctest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 97C146F11CF9000F007C117D /* Supporting Files */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+ 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146F21CF9000F007C117D /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ A189CFE5474BF8A07908B2E0 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */,
+ EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */,
+ E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */,
+ 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */,
+ DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */,
+ 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */,
+ );
+ name = Pods;
+ sourceTree = "<group>";
+ };
+ F7151F11265D7ED70028CB91 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */,
+ F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */,
+ 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */,
+ 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */,
+ F7151F14265D7ED70028CB91 /* Info.plist */,
+ );
+ path = RunnerTests;
+ sourceTree = "<group>";
+ };
+ F7151F1F265D7EE50028CB91 /* RunnerUITests */ = {
+ isa = PBXGroup;
+ children = (
+ F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */,
+ F7151F22265D7EE50028CB91 /* Info.plist */,
+ );
+ path = RunnerUITests;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 74BF216DF17B0C7F983459BD /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+ F7151F0F265D7ED70028CB91 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F7151F19265D7ED70028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ D067548A17DC238B80D2BD12 /* [CP] Check Pods Manifest.lock */,
+ F7151F0C265D7ED70028CB91 /* Sources */,
+ F7151F0D265D7ED70028CB91 /* Frameworks */,
+ F7151F0E265D7ED70028CB91 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ F7151F16265D7ED70028CB91 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = F7151F10265D7ED70028CB91 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ F7151F1D265D7EE50028CB91 /* RunnerUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F7151F25265D7EE50028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */;
+ buildPhases = (
+ BD39F60794E9A0264D5D3752 /* [CP] Check Pods Manifest.lock */,
+ F7151F1A265D7EE50028CB91 /* Sources */,
+ F7151F1B265D7EE50028CB91 /* Frameworks */,
+ F7151F1C265D7EE50028CB91 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ F7151F24265D7EE50028CB91 /* PBXTargetDependency */,
+ );
+ name = RunnerUITests;
+ productName = RunnerUITests;
+ productReference = F7151F1E265D7EE50028CB91 /* RunnerUITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1320;
+ ORGANIZATIONNAME = "The Flutter Authors";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ };
+ F7151F0F265D7ED70028CB91 = {
+ CreatedOnToolsVersion = 12.5;
+ ProvisioningStyle = Automatic;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
+ F7151F1D265D7EE50028CB91 = {
+ CreatedOnToolsVersion = 12.5;
+ ProvisioningStyle = Automatic;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ F7151F0F265D7ED70028CB91 /* RunnerTests */,
+ F7151F1D265D7EE50028CB91 /* RunnerUITests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F7151F0E265D7ED70028CB91 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F7151F1C265D7EE50028CB91 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 74BF216DF17B0C7F983459BD /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+ BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
+ "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ BD39F60794E9A0264D5D3752 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerUITests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ D067548A17DC238B80D2BD12 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
+ 97C146F31CF9000F007C117D /* main.m in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F7151F0C265D7ED70028CB91 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */,
+ 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */,
+ 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F7151F1A265D7EE50028CB91 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7151F21265D7EE50028CB91 /* GoogleMapsUITests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ F7151F16265D7ED70028CB91 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = F7151F15265D7ED70028CB91 /* PBXContainerItemProxy */;
+ };
+ F7151F24265D7EE50028CB91 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = F7151F23265D7EE50028CB91 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ENABLE_BITCODE = NO;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleMobileMapsExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ENABLE_BITCODE = NO;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleMobileMapsExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ F7151F17265D7ED70028CB91 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
+ INFOPLIST_FILE = RunnerTests/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+ };
+ name = Debug;
+ };
+ F7151F18265D7ED70028CB91 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
+ INFOPLIST_FILE = RunnerTests/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+ };
+ name = Release;
+ };
+ F7151F26265D7EE50028CB91 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ INFOPLIST_FILE = RunnerUITests/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_TARGET_NAME = Runner;
+ };
+ name = Debug;
+ };
+ F7151F27265D7EE50028CB91 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ INFOPLIST_FILE = RunnerUITests/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_TARGET_NAME = Runner;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F7151F19265D7ED70028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F7151F17265D7ED70028CB91 /* Debug */,
+ F7151F18265D7ED70028CB91 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F7151F25265D7EE50028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F7151F26265D7EE50028CB91 /* Debug */,
+ F7151F27265D7EE50028CB91 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..c983bfc
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1320"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "F7151F0F265D7ED70028CB91"
+ BuildableName = "RunnerTests.xctest"
+ BlueprintName = "RunnerTests"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "F7151F1D265D7EE50028CB91"
+ BuildableName = "RunnerUITests.xctest"
+ BlueprintName = "RunnerUITests"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..21a3cc1
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:Pods/Pods.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.h b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.h
new file mode 100644
index 0000000..9bc6c56
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.h
@@ -0,0 +1,9 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Flutter/Flutter.h>
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : FlutterAppDelegate
+@end
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.m
new file mode 100644
index 0000000..5573344
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.m
@@ -0,0 +1,27 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "AppDelegate.h"
+#import "GeneratedPluginRegistrant.h"
+
+@import GoogleMaps;
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // Provide the GoogleMaps API key.
+ NSString *mapsApiKey = [[NSProcessInfo processInfo] environment][@"MAPS_API_KEY"];
+ if ([mapsApiKey length] == 0) {
+ mapsApiKey = @"YOUR KEY HERE";
+ }
+ [GMSServices provideAPIKey:mapsApiKey];
+
+ // Register Flutter plugins.
+ [GeneratedPluginRegistrant registerWithRegistry:self];
+
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+
+@end
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..3d43d11
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
Binary files differ
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+ <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+ </constraints>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+ <resources>
+ <image name="LaunchImage" width="168" height="185"/>
+ </resources>
+</document>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--Flutter View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Info.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Info.plist
new file mode 100644
index 0000000..0fa9c73
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Info.plist
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>google_maps_flutter_example</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>NSLocationWhenInUseUsageDescription</key>
+ <string>This app needs your location to test the location feature of the Google Maps plugin.</string>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>arm64</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UIViewControllerBasedStatusBarAppearance</key>
+ <false/>
+</dict>
+</plist>
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/main.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/main.m
new file mode 100644
index 0000000..f143297
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/main.m
@@ -0,0 +1,13 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Flutter/Flutter.h>
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char *argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m
similarity index 99%
rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m
index bf226fe..bb9020d 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-@import google_maps_flutter;
-@import google_maps_flutter.Test;
+@import google_maps_flutter_ios;
+@import google_maps_flutter_ios.Test;
@import XCTest;
@import MapKit;
@import GoogleMaps;
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m
similarity index 94%
rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m
index f03dca2..a8768e1 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-@import google_maps_flutter;
-@import google_maps_flutter.Test;
+@import google_maps_flutter_ios;
+@import google_maps_flutter_ios.Test;
@import XCTest;
#import <OCMock/OCMock.h>
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/Info.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/Info.plist
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/Info.plist
rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/Info.plist
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/GoogleMapsUITests.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/GoogleMapsUITests.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/Info.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/Info.plist
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/Info.plist
rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/Info.plist
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/animate_camera.dart
new file mode 100644
index 0000000..c34a3ba
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/animate_camera.dart
@@ -0,0 +1,171 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class AnimateCameraPage extends GoogleMapExampleAppPage {
+ const AnimateCameraPage({Key? key})
+ : super(const Icon(Icons.map), 'Camera control, animated', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const AnimateCamera();
+ }
+}
+
+class AnimateCamera extends StatefulWidget {
+ const AnimateCamera({Key? key}) : super(key: key);
+ @override
+ State createState() => AnimateCameraState();
+}
+
+class AnimateCameraState extends State<AnimateCamera> {
+ ExampleGoogleMapController? mapController;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ mapController = controller;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition:
+ const CameraPosition(target: LatLng(0.0, 0.0)),
+ ),
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newCameraPosition(
+ const CameraPosition(
+ bearing: 270.0,
+ target: LatLng(51.5160895, -0.1294527),
+ tilt: 30.0,
+ zoom: 17.0,
+ ),
+ ),
+ );
+ },
+ child: const Text('newCameraPosition'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newLatLng(
+ const LatLng(56.1725505, 10.1850512),
+ ),
+ );
+ },
+ child: const Text('newLatLng'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newLatLngBounds(
+ LatLngBounds(
+ southwest: const LatLng(-38.483935, 113.248673),
+ northeast: const LatLng(-8.982446, 153.823821),
+ ),
+ 10.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngBounds'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.newLatLngZoom(
+ const LatLng(37.4231613, -122.087159),
+ 11.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngZoom'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.scrollBy(150.0, -225.0),
+ );
+ },
+ child: const Text('scrollBy'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomBy(
+ -0.5,
+ const Offset(30.0, 20.0),
+ ),
+ );
+ },
+ child: const Text('zoomBy with focus'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomBy(-0.5),
+ );
+ },
+ child: const Text('zoomBy'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomIn(),
+ );
+ },
+ child: const Text('zoomIn'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomOut(),
+ );
+ },
+ child: const Text('zoomOut'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.animateCamera(
+ CameraUpdate.zoomTo(16.0),
+ );
+ },
+ child: const Text('zoomTo'),
+ ),
+ ],
+ ),
+ ],
+ )
+ ],
+ );
+ }
+}
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
new file mode 100644
index 0000000..e2c7131
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart
@@ -0,0 +1,538 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+// This is a pared down version of the Dart code from the app-facing package,
+// to allow running the same examples for package-local testing.
+// TODO(stuartmorgan): Consider extracting this to a shared package. See also
+// https://github.com/flutter/flutter/issues/46716.
+
+/// Controller for a single ExampleGoogleMap instance running on the host platform.
+class ExampleGoogleMapController {
+ ExampleGoogleMapController._(
+ this._googleMapState, {
+ required this.mapId,
+ }) {
+ _connectStreams(mapId);
+ }
+
+ /// The mapId for this controller
+ final int mapId;
+
+ /// Initialize control of a [ExampleGoogleMap] with [id].
+ ///
+ /// Mainly for internal use when instantiating a [ExampleGoogleMapController] passed
+ /// in [ExampleGoogleMap.onMapCreated] callback.
+ static Future<ExampleGoogleMapController> _init(
+ int id,
+ CameraPosition initialCameraPosition,
+ _ExampleGoogleMapState googleMapState,
+ ) async {
+ await GoogleMapsFlutterPlatform.instance.init(id);
+ return ExampleGoogleMapController._(
+ googleMapState,
+ mapId: id,
+ );
+ }
+
+ final _ExampleGoogleMapState _googleMapState;
+
+ void _connectStreams(int mapId) {
+ if (_googleMapState.widget.onCameraMoveStarted != null) {
+ GoogleMapsFlutterPlatform.instance
+ .onCameraMoveStarted(mapId: mapId)
+ .listen((_) => _googleMapState.widget.onCameraMoveStarted!());
+ }
+ if (_googleMapState.widget.onCameraMove != null) {
+ GoogleMapsFlutterPlatform.instance.onCameraMove(mapId: mapId).listen(
+ (CameraMoveEvent e) => _googleMapState.widget.onCameraMove!(e.value));
+ }
+ if (_googleMapState.widget.onCameraIdle != null) {
+ GoogleMapsFlutterPlatform.instance
+ .onCameraIdle(mapId: mapId)
+ .listen((_) => _googleMapState.widget.onCameraIdle!());
+ }
+ GoogleMapsFlutterPlatform.instance
+ .onMarkerTap(mapId: mapId)
+ .listen((MarkerTapEvent e) => _googleMapState.onMarkerTap(e.value));
+ GoogleMapsFlutterPlatform.instance.onMarkerDragStart(mapId: mapId).listen(
+ (MarkerDragStartEvent e) =>
+ _googleMapState.onMarkerDragStart(e.value, e.position));
+ GoogleMapsFlutterPlatform.instance.onMarkerDrag(mapId: mapId).listen(
+ (MarkerDragEvent e) =>
+ _googleMapState.onMarkerDrag(e.value, e.position));
+ GoogleMapsFlutterPlatform.instance.onMarkerDragEnd(mapId: mapId).listen(
+ (MarkerDragEndEvent e) =>
+ _googleMapState.onMarkerDragEnd(e.value, e.position));
+ GoogleMapsFlutterPlatform.instance.onInfoWindowTap(mapId: mapId).listen(
+ (InfoWindowTapEvent e) => _googleMapState.onInfoWindowTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onPolylineTap(mapId: mapId)
+ .listen((PolylineTapEvent e) => _googleMapState.onPolylineTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onPolygonTap(mapId: mapId)
+ .listen((PolygonTapEvent e) => _googleMapState.onPolygonTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onCircleTap(mapId: mapId)
+ .listen((CircleTapEvent e) => _googleMapState.onCircleTap(e.value));
+ GoogleMapsFlutterPlatform.instance
+ .onTap(mapId: mapId)
+ .listen((MapTapEvent e) => _googleMapState.onTap(e.position));
+ GoogleMapsFlutterPlatform.instance.onLongPress(mapId: mapId).listen(
+ (MapLongPressEvent e) => _googleMapState.onLongPress(e.position));
+ }
+
+ /// Updates configuration options of the map user interface.
+ Future<void> _updateMapConfiguration(MapConfiguration update) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateMapConfiguration(update, mapId: mapId);
+ }
+
+ /// Updates marker configuration.
+ Future<void> _updateMarkers(MarkerUpdates markerUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateMarkers(markerUpdates, mapId: mapId);
+ }
+
+ /// Updates polygon configuration.
+ Future<void> _updatePolygons(PolygonUpdates polygonUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updatePolygons(polygonUpdates, mapId: mapId);
+ }
+
+ /// Updates polyline configuration.
+ Future<void> _updatePolylines(PolylineUpdates polylineUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updatePolylines(polylineUpdates, mapId: mapId);
+ }
+
+ /// Updates circle configuration.
+ Future<void> _updateCircles(CircleUpdates circleUpdates) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateCircles(circleUpdates, mapId: mapId);
+ }
+
+ /// Updates tile overlays configuration.
+ Future<void> _updateTileOverlays(Set<TileOverlay> newTileOverlays) {
+ return GoogleMapsFlutterPlatform.instance
+ .updateTileOverlays(newTileOverlays: newTileOverlays, mapId: mapId);
+ }
+
+ /// Clears the tile cache so that all tiles will be requested again from the
+ /// [TileProvider].
+ Future<void> clearTileCache(TileOverlayId tileOverlayId) async {
+ return GoogleMapsFlutterPlatform.instance
+ .clearTileCache(tileOverlayId, mapId: mapId);
+ }
+
+ /// Starts an animated change of the map camera position.
+ Future<void> animateCamera(CameraUpdate cameraUpdate) {
+ return GoogleMapsFlutterPlatform.instance
+ .animateCamera(cameraUpdate, mapId: mapId);
+ }
+
+ /// Changes the map camera position.
+ Future<void> moveCamera(CameraUpdate cameraUpdate) {
+ return GoogleMapsFlutterPlatform.instance
+ .moveCamera(cameraUpdate, mapId: mapId);
+ }
+
+ /// Sets the styling of the base map.
+ Future<void> setMapStyle(String? mapStyle) {
+ return GoogleMapsFlutterPlatform.instance
+ .setMapStyle(mapStyle, mapId: mapId);
+ }
+
+ /// Return [LatLngBounds] defining the region that is visible in a map.
+ Future<LatLngBounds> getVisibleRegion() {
+ return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId);
+ }
+
+ /// Return [ScreenCoordinate] of the [LatLng] in the current map view.
+ Future<ScreenCoordinate> getScreenCoordinate(LatLng latLng) {
+ return GoogleMapsFlutterPlatform.instance
+ .getScreenCoordinate(latLng, mapId: mapId);
+ }
+
+ /// Returns [LatLng] corresponding to the [ScreenCoordinate] in the current map view.
+ Future<LatLng> getLatLng(ScreenCoordinate screenCoordinate) {
+ return GoogleMapsFlutterPlatform.instance
+ .getLatLng(screenCoordinate, mapId: mapId);
+ }
+
+ /// Programmatically show the Info Window for a [Marker].
+ Future<void> showMarkerInfoWindow(MarkerId markerId) {
+ return GoogleMapsFlutterPlatform.instance
+ .showMarkerInfoWindow(markerId, mapId: mapId);
+ }
+
+ /// Programmatically hide the Info Window for a [Marker].
+ Future<void> hideMarkerInfoWindow(MarkerId markerId) {
+ return GoogleMapsFlutterPlatform.instance
+ .hideMarkerInfoWindow(markerId, mapId: mapId);
+ }
+
+ /// Returns `true` when the [InfoWindow] is showing, `false` otherwise.
+ Future<bool> isMarkerInfoWindowShown(MarkerId markerId) {
+ return GoogleMapsFlutterPlatform.instance
+ .isMarkerInfoWindowShown(markerId, mapId: mapId);
+ }
+
+ /// Returns the current zoom level of the map
+ Future<double> getZoomLevel() {
+ return GoogleMapsFlutterPlatform.instance.getZoomLevel(mapId: mapId);
+ }
+
+ /// Returns the image bytes of the map
+ Future<Uint8List?> takeSnapshot() {
+ return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId);
+ }
+
+ /// Disposes of the platform resources
+ void dispose() {
+ GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId);
+ }
+}
+
+// The next map ID to create.
+int _nextMapCreationId = 0;
+
+/// A widget which displays a map with data obtained from the Google Maps service.
+class ExampleGoogleMap extends StatefulWidget {
+ /// Creates a widget displaying data from Google Maps services.
+ ///
+ /// [AssertionError] will be thrown if [initialCameraPosition] is null;
+ const ExampleGoogleMap({
+ Key? key,
+ required this.initialCameraPosition,
+ this.onMapCreated,
+ this.gestureRecognizers = const <Factory<OneSequenceGestureRecognizer>>{},
+ this.compassEnabled = true,
+ this.mapToolbarEnabled = true,
+ this.cameraTargetBounds = CameraTargetBounds.unbounded,
+ this.mapType = MapType.normal,
+ this.minMaxZoomPreference = MinMaxZoomPreference.unbounded,
+ this.rotateGesturesEnabled = true,
+ this.scrollGesturesEnabled = true,
+ this.zoomControlsEnabled = true,
+ this.zoomGesturesEnabled = true,
+ this.liteModeEnabled = false,
+ this.tiltGesturesEnabled = true,
+ this.myLocationEnabled = false,
+ this.myLocationButtonEnabled = true,
+ this.layoutDirection,
+
+ /// If no padding is specified default padding will be 0.
+ this.padding = const EdgeInsets.all(0),
+ this.indoorViewEnabled = false,
+ this.trafficEnabled = false,
+ this.buildingsEnabled = true,
+ this.markers = const <Marker>{},
+ this.polygons = const <Polygon>{},
+ this.polylines = const <Polyline>{},
+ this.circles = const <Circle>{},
+ this.onCameraMoveStarted,
+ this.tileOverlays = const <TileOverlay>{},
+ this.onCameraMove,
+ this.onCameraIdle,
+ this.onTap,
+ this.onLongPress,
+ }) : super(key: key);
+
+ /// Callback method for when the map is ready to be used.
+ ///
+ /// Used to receive a [ExampleGoogleMapController] for this [ExampleGoogleMap].
+ final void Function(ExampleGoogleMapController controller)? onMapCreated;
+
+ /// The initial position of the map's camera.
+ final CameraPosition initialCameraPosition;
+
+ /// True if the map should show a compass when rotated.
+ final bool compassEnabled;
+
+ /// True if the map should show a toolbar when you interact with the map. Android only.
+ final bool mapToolbarEnabled;
+
+ /// Geographical bounding box for the camera target.
+ final CameraTargetBounds cameraTargetBounds;
+
+ /// Type of map tiles to be rendered.
+ final MapType mapType;
+
+ /// The layout direction to use for the embedded view.
+ final TextDirection? layoutDirection;
+
+ /// Preferred bounds for the camera zoom level.
+ ///
+ /// Actual bounds depend on map data and device.
+ final MinMaxZoomPreference minMaxZoomPreference;
+
+ /// True if the map view should respond to rotate gestures.
+ final bool rotateGesturesEnabled;
+
+ /// True if the map view should respond to scroll gestures.
+ final bool scrollGesturesEnabled;
+
+ /// True if the map view should show zoom controls. This includes two buttons
+ /// to zoom in and zoom out. The default value is to show zoom controls.
+ final bool zoomControlsEnabled;
+
+ /// True if the map view should respond to zoom gestures.
+ final bool zoomGesturesEnabled;
+
+ /// True if the map view should be in lite mode. Android only.
+ final bool liteModeEnabled;
+
+ /// True if the map view should respond to tilt gestures.
+ final bool tiltGesturesEnabled;
+
+ /// Padding to be set on map.
+ final EdgeInsets padding;
+
+ /// Markers to be placed on the map.
+ final Set<Marker> markers;
+
+ /// Polygons to be placed on the map.
+ final Set<Polygon> polygons;
+
+ /// Polylines to be placed on the map.
+ final Set<Polyline> polylines;
+
+ /// Circles to be placed on the map.
+ final Set<Circle> circles;
+
+ /// Tile overlays to be placed on the map.
+ final Set<TileOverlay> tileOverlays;
+
+ /// Called when the camera starts moving.
+ final VoidCallback? onCameraMoveStarted;
+
+ /// Called repeatedly as the camera continues to move after an
+ /// onCameraMoveStarted call.
+ final CameraPositionCallback? onCameraMove;
+
+ /// Called when camera movement has ended, there are no pending
+ /// animations and the user has stopped interacting with the map.
+ final VoidCallback? onCameraIdle;
+
+ /// Called every time a [ExampleGoogleMap] is tapped.
+ final ArgumentCallback<LatLng>? onTap;
+
+ /// Called every time a [ExampleGoogleMap] is long pressed.
+ final ArgumentCallback<LatLng>? onLongPress;
+
+ /// True if a "My Location" layer should be shown on the map.
+ final bool myLocationEnabled;
+
+ /// Enables or disables the my-location button.
+ final bool myLocationButtonEnabled;
+
+ /// Enables or disables the indoor view from the map
+ final bool indoorViewEnabled;
+
+ /// Enables or disables the traffic layer of the map
+ final bool trafficEnabled;
+
+ /// Enables or disables showing 3D buildings where available
+ final bool buildingsEnabled;
+
+ /// Which gestures should be consumed by the map.
+ final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
+
+ /// Creates a [State] for this [ExampleGoogleMap].
+ @override
+ State createState() => _ExampleGoogleMapState();
+}
+
+class _ExampleGoogleMapState extends State<ExampleGoogleMap> {
+ final int _mapId = _nextMapCreationId++;
+
+ final Completer<ExampleGoogleMapController> _controller =
+ Completer<ExampleGoogleMapController>();
+
+ Map<MarkerId, Marker> _markers = <MarkerId, Marker>{};
+ Map<PolygonId, Polygon> _polygons = <PolygonId, Polygon>{};
+ Map<PolylineId, Polyline> _polylines = <PolylineId, Polyline>{};
+ Map<CircleId, Circle> _circles = <CircleId, Circle>{};
+ late MapConfiguration _mapConfiguration;
+
+ @override
+ Widget build(BuildContext context) {
+ return GoogleMapsFlutterPlatform.instance.buildViewWithConfiguration(
+ _mapId,
+ onPlatformViewCreated,
+ widgetConfiguration: MapWidgetConfiguration(
+ textDirection: widget.layoutDirection ??
+ Directionality.maybeOf(context) ??
+ TextDirection.ltr,
+ initialCameraPosition: widget.initialCameraPosition,
+ gestureRecognizers: widget.gestureRecognizers,
+ ),
+ mapObjects: MapObjects(
+ markers: widget.markers,
+ polygons: widget.polygons,
+ polylines: widget.polylines,
+ circles: widget.circles,
+ ),
+ mapConfiguration: _mapConfiguration,
+ );
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _mapConfiguration = _configurationFromMapWidget(widget);
+ _markers = keyByMarkerId(widget.markers);
+ _polygons = keyByPolygonId(widget.polygons);
+ _polylines = keyByPolylineId(widget.polylines);
+ _circles = keyByCircleId(widget.circles);
+ }
+
+ @override
+ void dispose() {
+ _controller.future
+ .then((ExampleGoogleMapController controller) => controller.dispose());
+ super.dispose();
+ }
+
+ @override
+ void didUpdateWidget(ExampleGoogleMap oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ _updateOptions();
+ _updateMarkers();
+ _updatePolygons();
+ _updatePolylines();
+ _updateCircles();
+ _updateTileOverlays();
+ }
+
+ Future<void> _updateOptions() async {
+ final MapConfiguration newConfig = _configurationFromMapWidget(widget);
+ final MapConfiguration updates = newConfig.diffFrom(_mapConfiguration);
+ if (updates.isEmpty) {
+ return;
+ }
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateMapConfiguration(updates);
+ _mapConfiguration = newConfig;
+ }
+
+ Future<void> _updateMarkers() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateMarkers(
+ MarkerUpdates.from(_markers.values.toSet(), widget.markers));
+ _markers = keyByMarkerId(widget.markers);
+ }
+
+ Future<void> _updatePolygons() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updatePolygons(
+ PolygonUpdates.from(_polygons.values.toSet(), widget.polygons));
+ _polygons = keyByPolygonId(widget.polygons);
+ }
+
+ Future<void> _updatePolylines() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updatePolylines(
+ PolylineUpdates.from(_polylines.values.toSet(), widget.polylines));
+ _polylines = keyByPolylineId(widget.polylines);
+ }
+
+ Future<void> _updateCircles() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateCircles(
+ CircleUpdates.from(_circles.values.toSet(), widget.circles));
+ _circles = keyByCircleId(widget.circles);
+ }
+
+ Future<void> _updateTileOverlays() async {
+ final ExampleGoogleMapController controller = await _controller.future;
+ controller._updateTileOverlays(widget.tileOverlays);
+ }
+
+ Future<void> onPlatformViewCreated(int id) async {
+ final ExampleGoogleMapController controller =
+ await ExampleGoogleMapController._init(
+ id,
+ widget.initialCameraPosition,
+ this,
+ );
+ _controller.complete(controller);
+ _updateTileOverlays();
+ widget.onMapCreated?.call(controller);
+ }
+
+ void onMarkerTap(MarkerId markerId) {
+ _markers[markerId]!.onTap?.call();
+ }
+
+ void onMarkerDragStart(MarkerId markerId, LatLng position) {
+ _markers[markerId]!.onDragStart?.call(position);
+ }
+
+ void onMarkerDrag(MarkerId markerId, LatLng position) {
+ _markers[markerId]!.onDrag?.call(position);
+ }
+
+ void onMarkerDragEnd(MarkerId markerId, LatLng position) {
+ _markers[markerId]!.onDragEnd?.call(position);
+ }
+
+ void onPolygonTap(PolygonId polygonId) {
+ _polygons[polygonId]!.onTap?.call();
+ }
+
+ void onPolylineTap(PolylineId polylineId) {
+ _polylines[polylineId]!.onTap?.call();
+ }
+
+ void onCircleTap(CircleId circleId) {
+ _circles[circleId]!.onTap?.call();
+ }
+
+ void onInfoWindowTap(MarkerId markerId) {
+ _markers[markerId]!.infoWindow.onTap?.call();
+ }
+
+ void onTap(LatLng position) {
+ widget.onTap?.call(position);
+ }
+
+ void onLongPress(LatLng position) {
+ widget.onLongPress?.call(position);
+ }
+}
+
+/// Builds a [MapConfiguration] from the given [map].
+MapConfiguration _configurationFromMapWidget(ExampleGoogleMap map) {
+ return MapConfiguration(
+ compassEnabled: map.compassEnabled,
+ mapToolbarEnabled: map.mapToolbarEnabled,
+ cameraTargetBounds: map.cameraTargetBounds,
+ mapType: map.mapType,
+ minMaxZoomPreference: map.minMaxZoomPreference,
+ rotateGesturesEnabled: map.rotateGesturesEnabled,
+ scrollGesturesEnabled: map.scrollGesturesEnabled,
+ tiltGesturesEnabled: map.tiltGesturesEnabled,
+ trackCameraPosition: map.onCameraMove != null,
+ zoomControlsEnabled: map.zoomControlsEnabled,
+ zoomGesturesEnabled: map.zoomGesturesEnabled,
+ liteModeEnabled: map.liteModeEnabled,
+ myLocationEnabled: map.myLocationEnabled,
+ myLocationButtonEnabled: map.myLocationButtonEnabled,
+ padding: map.padding,
+ indoorViewEnabled: map.indoorViewEnabled,
+ trafficEnabled: map.trafficEnabled,
+ buildingsEnabled: map.buildingsEnabled,
+ );
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/lite_mode.dart
new file mode 100644
index 0000000..f7bead9
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/lite_mode.dart
@@ -0,0 +1,47 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class LiteModePage extends GoogleMapExampleAppPage {
+ const LiteModePage({Key? key})
+ : super(const Icon(Icons.map), 'Lite mode', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const _LiteModeBody();
+ }
+}
+
+class _LiteModeBody extends StatelessWidget {
+ const _LiteModeBody();
+
+ @override
+ Widget build(BuildContext context) {
+ return const Card(
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 30.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: _kInitialPosition,
+ liteModeEnabled: true,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart
new file mode 100644
index 0000000..c02e4af
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart
@@ -0,0 +1,73 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_example/lite_mode.dart';
+
+import 'animate_camera.dart';
+import 'map_click.dart';
+import 'map_coordinates.dart';
+import 'map_ui.dart';
+import 'marker_icons.dart';
+import 'move_camera.dart';
+import 'padding.dart';
+import 'page.dart';
+import 'place_circle.dart';
+import 'place_marker.dart';
+import 'place_polygon.dart';
+import 'place_polyline.dart';
+import 'scrolling_map.dart';
+import 'snapshot.dart';
+import 'tile_overlay.dart';
+
+final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
+ const MapUiPage(),
+ const MapCoordinatesPage(),
+ const MapClickPage(),
+ const AnimateCameraPage(),
+ const MoveCameraPage(),
+ const PlaceMarkerPage(),
+ const MarkerIconsPage(),
+ const ScrollingMapPage(),
+ const PlacePolylinePage(),
+ const PlacePolygonPage(),
+ const PlaceCirclePage(),
+ const PaddingPage(),
+ const SnapshotPage(),
+ const LiteModePage(),
+ const TileOverlayPage(),
+];
+
+/// MapsDemo is the Main Application.
+class MapsDemo extends StatelessWidget {
+ /// Default Constructor
+ const MapsDemo({Key? key}) : super(key: key);
+
+ void _pushPage(BuildContext context, GoogleMapExampleAppPage page) {
+ Navigator.of(context).push(MaterialPageRoute<void>(
+ builder: (_) => Scaffold(
+ appBar: AppBar(title: Text(page.title)),
+ body: page,
+ )));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('GoogleMaps examples')),
+ body: ListView.builder(
+ itemCount: _allPages.length,
+ itemBuilder: (_, int index) => ListTile(
+ leading: _allPages[index].leading,
+ title: Text(_allPages[index].title),
+ onTap: () => _pushPage(context, _allPages[index]),
+ ),
+ ),
+ );
+ }
+}
+
+void main() {
+ runApp(const MaterialApp(home: MapsDemo()));
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart
new file mode 100644
index 0000000..20718d9
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart
@@ -0,0 +1,106 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class MapClickPage extends GoogleMapExampleAppPage {
+ const MapClickPage({Key? key})
+ : super(const Icon(Icons.mouse), 'Map click', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const _MapClickBody();
+ }
+}
+
+class _MapClickBody extends StatefulWidget {
+ const _MapClickBody();
+
+ @override
+ State<StatefulWidget> createState() => _MapClickBodyState();
+}
+
+class _MapClickBodyState extends State<_MapClickBody> {
+ _MapClickBodyState();
+
+ ExampleGoogleMapController? mapController;
+ LatLng? _lastTap;
+ LatLng? _lastLongPress;
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ onTap: (LatLng pos) {
+ setState(() {
+ _lastTap = pos;
+ });
+ },
+ onLongPress: (LatLng pos) {
+ setState(() {
+ _lastLongPress = pos;
+ });
+ },
+ );
+
+ final List<Widget> columnChildren = <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ ];
+
+ if (mapController != null) {
+ final String lastTap = 'Tap:\n${_lastTap ?? ""}\n';
+ final String lastLongPress = 'Long press:\n${_lastLongPress ?? ""}';
+ columnChildren.add(Center(
+ child: Text(
+ lastTap,
+ textAlign: TextAlign.center,
+ )));
+ columnChildren.add(Center(
+ child: Text(
+ _lastTap != null ? 'Tapped' : '',
+ textAlign: TextAlign.center,
+ )));
+ columnChildren.add(Center(
+ child: Text(
+ lastLongPress,
+ textAlign: TextAlign.center,
+ )));
+ columnChildren.add(Center(
+ child: Text(
+ _lastLongPress != null ? 'Long pressed' : '',
+ textAlign: TextAlign.center,
+ )));
+ }
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: columnChildren,
+ );
+ }
+
+ Future<void> onMapCreated(ExampleGoogleMapController controller) async {
+ setState(() {
+ mapController = controller;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_coordinates.dart
new file mode 100644
index 0000000..185a97e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_coordinates.dart
@@ -0,0 +1,100 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class MapCoordinatesPage extends GoogleMapExampleAppPage {
+ const MapCoordinatesPage({Key? key})
+ : super(const Icon(Icons.map), 'Map coordinates', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const _MapCoordinatesBody();
+ }
+}
+
+class _MapCoordinatesBody extends StatefulWidget {
+ const _MapCoordinatesBody();
+
+ @override
+ State<StatefulWidget> createState() => _MapCoordinatesBodyState();
+}
+
+class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> {
+ _MapCoordinatesBodyState();
+
+ ExampleGoogleMapController? mapController;
+ LatLngBounds _visibleRegion = LatLngBounds(
+ southwest: const LatLng(0, 0),
+ northeast: const LatLng(0, 0),
+ );
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ onCameraIdle:
+ _updateVisibleRegion, // https://github.com/flutter/flutter/issues/54758
+ );
+
+ return NotificationListener<ScrollNotification>(
+ onNotification: (ScrollNotification scrollState) {
+ _updateVisibleRegion();
+ return true;
+ },
+ child: ListView(
+ children: <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ if (mapController != null)
+ Center(
+ child: Text('VisibleRegion:'
+ '\nnortheast: ${_visibleRegion.northeast},'
+ '\nsouthwest: ${_visibleRegion.southwest}'),
+ ),
+ // Add a block at the bottom of this list to allow validation that the visible region of the map
+ // does not change when scrolled under the safe view on iOS.
+ // https://github.com/flutter/flutter/issues/107913
+ Container(
+ width: 300,
+ height: 1000,
+ ),
+ ],
+ ),
+ );
+ }
+
+ Future<void> onMapCreated(ExampleGoogleMapController controller) async {
+ final LatLngBounds visibleRegion = await controller.getVisibleRegion();
+ setState(() {
+ mapController = controller;
+ _visibleRegion = visibleRegion;
+ });
+ }
+
+ Future<void> _updateVisibleRegion() async {
+ final LatLngBounds visibleRegion = await mapController!.getVisibleRegion();
+ setState(() {
+ _visibleRegion = visibleRegion;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart
new file mode 100644
index 0000000..6c38e14
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart
@@ -0,0 +1,358 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart' show rootBundle;
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+final LatLngBounds sydneyBounds = LatLngBounds(
+ southwest: const LatLng(-34.022631, 150.620685),
+ northeast: const LatLng(-33.571835, 151.325952),
+);
+
+class MapUiPage extends GoogleMapExampleAppPage {
+ const MapUiPage({Key? key})
+ : super(const Icon(Icons.map), 'User interface', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MapUiBody();
+ }
+}
+
+class MapUiBody extends StatefulWidget {
+ const MapUiBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => MapUiBodyState();
+}
+
+class MapUiBodyState extends State<MapUiBody> {
+ MapUiBodyState();
+
+ static const CameraPosition _kInitialPosition = CameraPosition(
+ target: LatLng(-33.852, 151.211),
+ zoom: 11.0,
+ );
+
+ CameraPosition _position = _kInitialPosition;
+ bool _isMapCreated = false;
+ final bool _isMoving = false;
+ bool _compassEnabled = true;
+ bool _mapToolbarEnabled = true;
+ CameraTargetBounds _cameraTargetBounds = CameraTargetBounds.unbounded;
+ MinMaxZoomPreference _minMaxZoomPreference = MinMaxZoomPreference.unbounded;
+ MapType _mapType = MapType.normal;
+ bool _rotateGesturesEnabled = true;
+ bool _scrollGesturesEnabled = true;
+ bool _tiltGesturesEnabled = true;
+ bool _zoomControlsEnabled = false;
+ bool _zoomGesturesEnabled = true;
+ bool _indoorViewEnabled = true;
+ bool _myLocationEnabled = true;
+ bool _myTrafficEnabled = false;
+ bool _myLocationButtonEnabled = true;
+ late ExampleGoogleMapController _controller;
+ bool _nightMode = false;
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ Widget _compassToggler() {
+ return TextButton(
+ child: Text('${_compassEnabled ? 'disable' : 'enable'} compass'),
+ onPressed: () {
+ setState(() {
+ _compassEnabled = !_compassEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _mapToolbarToggler() {
+ return TextButton(
+ child: Text('${_mapToolbarEnabled ? 'disable' : 'enable'} map toolbar'),
+ onPressed: () {
+ setState(() {
+ _mapToolbarEnabled = !_mapToolbarEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _latLngBoundsToggler() {
+ return TextButton(
+ child: Text(
+ _cameraTargetBounds.bounds == null
+ ? 'bound camera target'
+ : 'release camera target',
+ ),
+ onPressed: () {
+ setState(() {
+ _cameraTargetBounds = _cameraTargetBounds.bounds == null
+ ? CameraTargetBounds(sydneyBounds)
+ : CameraTargetBounds.unbounded;
+ });
+ },
+ );
+ }
+
+ Widget _zoomBoundsToggler() {
+ return TextButton(
+ child: Text(_minMaxZoomPreference.minZoom == null
+ ? 'bound zoom'
+ : 'release zoom'),
+ onPressed: () {
+ setState(() {
+ _minMaxZoomPreference = _minMaxZoomPreference.minZoom == null
+ ? const MinMaxZoomPreference(12.0, 16.0)
+ : MinMaxZoomPreference.unbounded;
+ });
+ },
+ );
+ }
+
+ Widget _mapTypeCycler() {
+ final MapType nextType =
+ MapType.values[(_mapType.index + 1) % MapType.values.length];
+ return TextButton(
+ child: Text('change map type to $nextType'),
+ onPressed: () {
+ setState(() {
+ _mapType = nextType;
+ });
+ },
+ );
+ }
+
+ Widget _rotateToggler() {
+ return TextButton(
+ child: Text('${_rotateGesturesEnabled ? 'disable' : 'enable'} rotate'),
+ onPressed: () {
+ setState(() {
+ _rotateGesturesEnabled = !_rotateGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _scrollToggler() {
+ return TextButton(
+ child: Text('${_scrollGesturesEnabled ? 'disable' : 'enable'} scroll'),
+ onPressed: () {
+ setState(() {
+ _scrollGesturesEnabled = !_scrollGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _tiltToggler() {
+ return TextButton(
+ child: Text('${_tiltGesturesEnabled ? 'disable' : 'enable'} tilt'),
+ onPressed: () {
+ setState(() {
+ _tiltGesturesEnabled = !_tiltGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _zoomToggler() {
+ return TextButton(
+ child: Text('${_zoomGesturesEnabled ? 'disable' : 'enable'} zoom'),
+ onPressed: () {
+ setState(() {
+ _zoomGesturesEnabled = !_zoomGesturesEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _zoomControlsToggler() {
+ return TextButton(
+ child:
+ Text('${_zoomControlsEnabled ? 'disable' : 'enable'} zoom controls'),
+ onPressed: () {
+ setState(() {
+ _zoomControlsEnabled = !_zoomControlsEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _indoorViewToggler() {
+ return TextButton(
+ child: Text('${_indoorViewEnabled ? 'disable' : 'enable'} indoor'),
+ onPressed: () {
+ setState(() {
+ _indoorViewEnabled = !_indoorViewEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _myLocationToggler() {
+ return TextButton(
+ child: Text(
+ '${_myLocationEnabled ? 'disable' : 'enable'} my location marker'),
+ onPressed: () {
+ setState(() {
+ _myLocationEnabled = !_myLocationEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _myLocationButtonToggler() {
+ return TextButton(
+ child: Text(
+ '${_myLocationButtonEnabled ? 'disable' : 'enable'} my location button'),
+ onPressed: () {
+ setState(() {
+ _myLocationButtonEnabled = !_myLocationButtonEnabled;
+ });
+ },
+ );
+ }
+
+ Widget _myTrafficToggler() {
+ return TextButton(
+ child: Text('${_myTrafficEnabled ? 'disable' : 'enable'} my traffic'),
+ onPressed: () {
+ setState(() {
+ _myTrafficEnabled = !_myTrafficEnabled;
+ });
+ },
+ );
+ }
+
+ Future<String> _getFileData(String path) async {
+ return await rootBundle.loadString(path);
+ }
+
+ void _setMapStyle(String mapStyle) {
+ setState(() {
+ _nightMode = true;
+ _controller.setMapStyle(mapStyle);
+ });
+ }
+
+ // Should only be called if _isMapCreated is true.
+ Widget _nightModeToggler() {
+ assert(_isMapCreated);
+ return TextButton(
+ child: Text('${_nightMode ? 'disable' : 'enable'} night mode'),
+ onPressed: () {
+ if (_nightMode) {
+ setState(() {
+ _nightMode = false;
+ _controller.setMapStyle(null);
+ });
+ } else {
+ _getFileData('assets/night_mode.json').then(_setMapStyle);
+ }
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ compassEnabled: _compassEnabled,
+ mapToolbarEnabled: _mapToolbarEnabled,
+ cameraTargetBounds: _cameraTargetBounds,
+ minMaxZoomPreference: _minMaxZoomPreference,
+ mapType: _mapType,
+ rotateGesturesEnabled: _rotateGesturesEnabled,
+ scrollGesturesEnabled: _scrollGesturesEnabled,
+ tiltGesturesEnabled: _tiltGesturesEnabled,
+ zoomGesturesEnabled: _zoomGesturesEnabled,
+ zoomControlsEnabled: _zoomControlsEnabled,
+ indoorViewEnabled: _indoorViewEnabled,
+ myLocationEnabled: _myLocationEnabled,
+ myLocationButtonEnabled: _myLocationButtonEnabled,
+ trafficEnabled: _myTrafficEnabled,
+ onCameraMove: _updateCameraPosition,
+ );
+
+ final List<Widget> columnChildren = <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ ];
+
+ if (_isMapCreated) {
+ columnChildren.add(
+ Expanded(
+ child: ListView(
+ children: <Widget>[
+ Text('camera bearing: ${_position.bearing}'),
+ Text(
+ 'camera target: ${_position.target.latitude.toStringAsFixed(4)},'
+ '${_position.target.longitude.toStringAsFixed(4)}'),
+ Text('camera zoom: ${_position.zoom}'),
+ Text('camera tilt: ${_position.tilt}'),
+ Text(_isMoving ? '(Camera moving)' : '(Camera idle)'),
+ _compassToggler(),
+ _mapToolbarToggler(),
+ _latLngBoundsToggler(),
+ _mapTypeCycler(),
+ _zoomBoundsToggler(),
+ _rotateToggler(),
+ _scrollToggler(),
+ _tiltToggler(),
+ _zoomToggler(),
+ _zoomControlsToggler(),
+ _indoorViewToggler(),
+ _myLocationToggler(),
+ _myLocationButtonToggler(),
+ _myTrafficToggler(),
+ _nightModeToggler(),
+ ],
+ ),
+ ),
+ );
+ }
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: columnChildren,
+ );
+ }
+
+ void _updateCameraPosition(CameraPosition position) {
+ setState(() {
+ _position = position;
+ });
+ }
+
+ void onMapCreated(ExampleGoogleMapController controller) {
+ setState(() {
+ _controller = controller;
+ _isMapCreated = true;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/marker_icons.dart
new file mode 100644
index 0000000..fe28eb6
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/marker_icons.dart
@@ -0,0 +1,98 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+// ignore_for_file: unawaited_futures
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class MarkerIconsPage extends GoogleMapExampleAppPage {
+ const MarkerIconsPage({Key? key})
+ : super(const Icon(Icons.image), 'Marker icons', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MarkerIconsBody();
+ }
+}
+
+class MarkerIconsBody extends StatefulWidget {
+ const MarkerIconsBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => MarkerIconsBodyState();
+}
+
+const LatLng _kMapCenter = LatLng(52.4478, -3.5402);
+
+class MarkerIconsBodyState extends State<MarkerIconsBody> {
+ ExampleGoogleMapController? controller;
+ BitmapDescriptor? _markerIcon;
+
+ @override
+ Widget build(BuildContext context) {
+ _createMarkerImageFromAsset(context);
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: _kMapCenter,
+ zoom: 7.0,
+ ),
+ markers: <Marker>{_createMarker()},
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ )
+ ],
+ );
+ }
+
+ Marker _createMarker() {
+ if (_markerIcon != null) {
+ return Marker(
+ markerId: const MarkerId('marker_1'),
+ position: _kMapCenter,
+ icon: _markerIcon!,
+ );
+ } else {
+ return const Marker(
+ markerId: MarkerId('marker_1'),
+ position: _kMapCenter,
+ );
+ }
+ }
+
+ Future<void> _createMarkerImageFromAsset(BuildContext context) async {
+ if (_markerIcon == null) {
+ final ImageConfiguration imageConfiguration =
+ createLocalImageConfiguration(context, size: const Size.square(48));
+ BitmapDescriptor.fromAssetImage(
+ imageConfiguration, 'assets/red_square.png')
+ .then(_updateBitmap);
+ }
+ }
+
+ void _updateBitmap(BitmapDescriptor bitmap) {
+ setState(() {
+ _markerIcon = bitmap;
+ });
+ }
+
+ void _onMapCreated(ExampleGoogleMapController controllerParam) {
+ setState(() {
+ controller = controllerParam;
+ });
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/move_camera.dart
new file mode 100644
index 0000000..7f44d89
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/move_camera.dart
@@ -0,0 +1,171 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class MoveCameraPage extends GoogleMapExampleAppPage {
+ const MoveCameraPage({Key? key})
+ : super(const Icon(Icons.map), 'Camera control', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MoveCamera();
+ }
+}
+
+class MoveCamera extends StatefulWidget {
+ const MoveCamera({Key? key}) : super(key: key);
+ @override
+ State createState() => MoveCameraState();
+}
+
+class MoveCameraState extends State<MoveCamera> {
+ ExampleGoogleMapController? mapController;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ mapController = controller;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition:
+ const CameraPosition(target: LatLng(0.0, 0.0)),
+ ),
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newCameraPosition(
+ const CameraPosition(
+ bearing: 270.0,
+ target: LatLng(51.5160895, -0.1294527),
+ tilt: 30.0,
+ zoom: 17.0,
+ ),
+ ),
+ );
+ },
+ child: const Text('newCameraPosition'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newLatLng(
+ const LatLng(56.1725505, 10.1850512),
+ ),
+ );
+ },
+ child: const Text('newLatLng'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newLatLngBounds(
+ LatLngBounds(
+ southwest: const LatLng(-38.483935, 113.248673),
+ northeast: const LatLng(-8.982446, 153.823821),
+ ),
+ 10.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngBounds'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.newLatLngZoom(
+ const LatLng(37.4231613, -122.087159),
+ 11.0,
+ ),
+ );
+ },
+ child: const Text('newLatLngZoom'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.scrollBy(150.0, -225.0),
+ );
+ },
+ child: const Text('scrollBy'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomBy(
+ -0.5,
+ const Offset(30.0, 20.0),
+ ),
+ );
+ },
+ child: const Text('zoomBy with focus'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomBy(-0.5),
+ );
+ },
+ child: const Text('zoomBy'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomIn(),
+ );
+ },
+ child: const Text('zoomIn'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomOut(),
+ );
+ },
+ child: const Text('zoomOut'),
+ ),
+ TextButton(
+ onPressed: () {
+ mapController?.moveCamera(
+ CameraUpdate.zoomTo(16.0),
+ );
+ },
+ child: const Text('zoomTo'),
+ ),
+ ],
+ ),
+ ],
+ )
+ ],
+ );
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart
new file mode 100644
index 0000000..d346549
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart
@@ -0,0 +1,181 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PaddingPage extends GoogleMapExampleAppPage {
+ const PaddingPage({Key? key})
+ : super(const Icon(Icons.map), 'Add padding to the map', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const MarkerIconsBody();
+ }
+}
+
+class MarkerIconsBody extends StatefulWidget {
+ const MarkerIconsBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => MarkerIconsBodyState();
+}
+
+const LatLng _kMapCenter = LatLng(52.4478, -3.5402);
+
+class MarkerIconsBodyState extends State<MarkerIconsBody> {
+ ExampleGoogleMapController? controller;
+
+ EdgeInsets _padding = const EdgeInsets.all(0);
+
+ @override
+ Widget build(BuildContext context) {
+ final ExampleGoogleMap googleMap = ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition: const CameraPosition(
+ target: _kMapCenter,
+ zoom: 7.0,
+ ),
+ padding: _padding,
+ );
+
+ final List<Widget> columnChildren = <Widget>[
+ Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 200.0,
+ child: googleMap,
+ ),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.only(top: 20),
+ child: Center(
+ child: Text(
+ 'Enter Padding Below',
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ ),
+ ),
+ ];
+
+ columnChildren.addAll(<Widget>[_paddingInput(), _buttons()]);
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: columnChildren,
+ );
+ }
+
+ void _onMapCreated(ExampleGoogleMapController controllerParam) {
+ setState(() {
+ controller = controllerParam;
+ });
+ }
+
+ final TextEditingController _topController = TextEditingController();
+ final TextEditingController _bottomController = TextEditingController();
+ final TextEditingController _leftController = TextEditingController();
+ final TextEditingController _rightController = TextEditingController();
+
+ Widget _paddingInput() {
+ return Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Row(
+ children: <Widget>[
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _topController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Top',
+ ),
+ ),
+ ),
+ const Spacer(),
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _bottomController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Bottom',
+ ),
+ ),
+ ),
+ const Spacer(),
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _leftController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Left',
+ ),
+ ),
+ ),
+ const Spacer(),
+ Flexible(
+ flex: 2,
+ child: TextField(
+ controller: _rightController,
+ keyboardType: TextInputType.number,
+ textAlign: TextAlign.center,
+ decoration: const InputDecoration(
+ hintText: 'Right',
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buttons() {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ TextButton(
+ child: const Text('Set Padding'),
+ onPressed: () {
+ setState(() {
+ _padding = EdgeInsets.fromLTRB(
+ double.tryParse(_leftController.value.text) ?? 0,
+ double.tryParse(_topController.value.text) ?? 0,
+ double.tryParse(_rightController.value.text) ?? 0,
+ double.tryParse(_bottomController.value.text) ?? 0);
+ });
+ },
+ ),
+ TextButton(
+ child: const Text('Reset Padding'),
+ onPressed: () {
+ setState(() {
+ _topController.clear();
+ _bottomController.clear();
+ _leftController.clear();
+ _rightController.clear();
+ _padding = const EdgeInsets.all(0);
+ });
+ },
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/page.dart
new file mode 100644
index 0000000..eb01ab0
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/page.dart
@@ -0,0 +1,15 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+
+abstract class GoogleMapExampleAppPage extends StatelessWidget {
+ const GoogleMapExampleAppPage(this.leading, this.title, {Key? key})
+ : super(key: key);
+
+ final Widget leading;
+ final String title;
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_circle.dart
new file mode 100644
index 0000000..9dc5760
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_circle.dart
@@ -0,0 +1,232 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlaceCirclePage extends GoogleMapExampleAppPage {
+ const PlaceCirclePage({Key? key})
+ : super(const Icon(Icons.linear_scale), 'Place circle', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlaceCircleBody();
+ }
+}
+
+class PlaceCircleBody extends StatefulWidget {
+ const PlaceCircleBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlaceCircleBodyState();
+}
+
+class PlaceCircleBodyState extends State<PlaceCircleBody> {
+ PlaceCircleBodyState();
+
+ ExampleGoogleMapController? controller;
+ Map<CircleId, Circle> circles = <CircleId, Circle>{};
+ int _circleIdCounter = 1;
+ CircleId? selectedCircle;
+
+ // Values when toggling circle color
+ int fillColorsIndex = 0;
+ int strokeColorsIndex = 0;
+ List<Color> colors = <Color>[
+ Colors.purple,
+ Colors.red,
+ Colors.green,
+ Colors.pink,
+ ];
+
+ // Values when toggling circle stroke width
+ int widthsIndex = 0;
+ List<int> widths = <int>[10, 20, 5];
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onCircleTapped(CircleId circleId) {
+ setState(() {
+ selectedCircle = circleId;
+ });
+ }
+
+ void _remove(CircleId circleId) {
+ setState(() {
+ if (circles.containsKey(circleId)) {
+ circles.remove(circleId);
+ }
+ if (circleId == selectedCircle) {
+ selectedCircle = null;
+ }
+ });
+ }
+
+ void _add() {
+ final int circleCount = circles.length;
+
+ if (circleCount == 12) {
+ return;
+ }
+
+ final String circleIdVal = 'circle_id_$_circleIdCounter';
+ _circleIdCounter++;
+ final CircleId circleId = CircleId(circleIdVal);
+
+ final Circle circle = Circle(
+ circleId: circleId,
+ consumeTapEvents: true,
+ strokeColor: Colors.orange,
+ fillColor: Colors.green,
+ strokeWidth: 5,
+ center: _createCenter(),
+ radius: 50000,
+ onTap: () {
+ _onCircleTapped(circleId);
+ },
+ );
+
+ setState(() {
+ circles[circleId] = circle;
+ });
+ }
+
+ void _toggleVisible(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ visibleParam: !circle.visible,
+ );
+ });
+ }
+
+ void _changeFillColor(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ fillColorParam: colors[++fillColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeStrokeColor(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ strokeColorParam: colors[++strokeColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeStrokeWidth(CircleId circleId) {
+ final Circle circle = circles[circleId]!;
+ setState(() {
+ circles[circleId] = circle.copyWith(
+ strokeWidthParam: widths[++widthsIndex % widths.length],
+ );
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final CircleId? selectedId = selectedCircle;
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(52.4478, -3.5402),
+ zoom: 7.0,
+ ),
+ circles: Set<Circle>.of(circles.values),
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Row(
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('add'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _remove(selectedId),
+ child: const Text('remove'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeStrokeWidth(selectedId),
+ child: const Text('change stroke width'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeStrokeColor(selectedId),
+ child: const Text('change stroke color'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeFillColor(selectedId),
+ child: const Text('change fill color'),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ LatLng _createCenter() {
+ final double offset = _circleIdCounter.ceilToDouble();
+ return _createLatLng(51.4816 + offset * 0.2, -3.1791);
+ }
+
+ LatLng _createLatLng(double lat, double lng) {
+ return LatLng(lat, lng);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart
new file mode 100644
index 0000000..b7d2a69
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart
@@ -0,0 +1,422 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:async';
+import 'dart:math';
+import 'dart:typed_data';
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlaceMarkerPage extends GoogleMapExampleAppPage {
+ const PlaceMarkerPage({Key? key})
+ : super(const Icon(Icons.place), 'Place marker', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlaceMarkerBody();
+ }
+}
+
+class PlaceMarkerBody extends StatefulWidget {
+ const PlaceMarkerBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlaceMarkerBodyState();
+}
+
+typedef MarkerUpdateAction = Marker Function(Marker marker);
+
+class PlaceMarkerBodyState extends State<PlaceMarkerBody> {
+ PlaceMarkerBodyState();
+ static const LatLng center = LatLng(-33.86711, 151.1947171);
+
+ ExampleGoogleMapController? controller;
+ Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
+ MarkerId? selectedMarker;
+ int _markerIdCounter = 1;
+ LatLng? markerPosition;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onMarkerTapped(MarkerId markerId) {
+ final Marker? tappedMarker = markers[markerId];
+ if (tappedMarker != null) {
+ setState(() {
+ final MarkerId? previousMarkerId = selectedMarker;
+ if (previousMarkerId != null && markers.containsKey(previousMarkerId)) {
+ final Marker resetOld = markers[previousMarkerId]!
+ .copyWith(iconParam: BitmapDescriptor.defaultMarker);
+ markers[previousMarkerId] = resetOld;
+ }
+ selectedMarker = markerId;
+ final Marker newMarker = tappedMarker.copyWith(
+ iconParam: BitmapDescriptor.defaultMarkerWithHue(
+ BitmapDescriptor.hueGreen,
+ ),
+ );
+ markers[markerId] = newMarker;
+
+ markerPosition = null;
+ });
+ }
+ }
+
+ Future<void> _onMarkerDrag(MarkerId markerId, LatLng newPosition) async {
+ setState(() {
+ markerPosition = newPosition;
+ });
+ }
+
+ Future<void> _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async {
+ final Marker? tappedMarker = markers[markerId];
+ if (tappedMarker != null) {
+ setState(() {
+ markerPosition = null;
+ });
+ await showDialog<void>(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ actions: <Widget>[
+ TextButton(
+ child: const Text('OK'),
+ onPressed: () => Navigator.of(context).pop(),
+ )
+ ],
+ content: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 66),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: <Widget>[
+ Text('Old position: ${tappedMarker.position}'),
+ Text('New position: $newPosition'),
+ ],
+ )));
+ });
+ }
+ }
+
+ void _add() {
+ final int markerCount = markers.length;
+
+ if (markerCount == 12) {
+ return;
+ }
+
+ final String markerIdVal = 'marker_id_$_markerIdCounter';
+ _markerIdCounter++;
+ final MarkerId markerId = MarkerId(markerIdVal);
+
+ final Marker marker = Marker(
+ markerId: markerId,
+ position: LatLng(
+ center.latitude + sin(_markerIdCounter * pi / 6.0) / 20.0,
+ center.longitude + cos(_markerIdCounter * pi / 6.0) / 20.0,
+ ),
+ infoWindow: InfoWindow(title: markerIdVal, snippet: '*'),
+ onTap: () => _onMarkerTapped(markerId),
+ onDragEnd: (LatLng position) => _onMarkerDragEnd(markerId, position),
+ onDrag: (LatLng position) => _onMarkerDrag(markerId, position),
+ );
+
+ setState(() {
+ markers[markerId] = marker;
+ });
+ }
+
+ void _remove(MarkerId markerId) {
+ setState(() {
+ if (markers.containsKey(markerId)) {
+ markers.remove(markerId);
+ }
+ });
+ }
+
+ void _changePosition(MarkerId markerId) {
+ final Marker marker = markers[markerId]!;
+ final LatLng current = marker.position;
+ final Offset offset = Offset(
+ center.latitude - current.latitude,
+ center.longitude - current.longitude,
+ );
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ positionParam: LatLng(
+ center.latitude + offset.dy,
+ center.longitude + offset.dx,
+ ),
+ );
+ });
+ }
+
+ void _changeAnchor(MarkerId markerId) {
+ final Marker marker = markers[markerId]!;
+ final Offset currentAnchor = marker.anchor;
+ final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx);
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ anchorParam: newAnchor,
+ );
+ });
+ }
+
+ Future<void> _changeInfoAnchor(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final Offset currentAnchor = marker.infoWindow.anchor;
+ final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx);
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ infoWindowParam: marker.infoWindow.copyWith(
+ anchorParam: newAnchor,
+ ),
+ );
+ });
+ }
+
+ Future<void> _toggleDraggable(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ draggableParam: !marker.draggable,
+ );
+ });
+ }
+
+ Future<void> _toggleFlat(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ flatParam: !marker.flat,
+ );
+ });
+ }
+
+ Future<void> _changeInfo(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final String newSnippet = '${marker.infoWindow.snippet!}*';
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ infoWindowParam: marker.infoWindow.copyWith(
+ snippetParam: newSnippet,
+ ),
+ );
+ });
+ }
+
+ Future<void> _changeAlpha(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final double current = marker.alpha;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ alphaParam: current < 0.1 ? 1.0 : current * 0.75,
+ );
+ });
+ }
+
+ Future<void> _changeRotation(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final double current = marker.rotation;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ rotationParam: current == 330.0 ? 0.0 : current + 30.0,
+ );
+ });
+ }
+
+ Future<void> _toggleVisible(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ visibleParam: !marker.visible,
+ );
+ });
+ }
+
+ Future<void> _changeZIndex(MarkerId markerId) async {
+ final Marker marker = markers[markerId]!;
+ final double current = marker.zIndex;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ zIndexParam: current == 12.0 ? 0.0 : current + 1.0,
+ );
+ });
+ }
+
+ void _setMarkerIcon(MarkerId markerId, BitmapDescriptor assetIcon) {
+ final Marker marker = markers[markerId]!;
+ setState(() {
+ markers[markerId] = marker.copyWith(
+ iconParam: assetIcon,
+ );
+ });
+ }
+
+ Future<BitmapDescriptor> _getAssetIcon(BuildContext context) async {
+ final Completer<BitmapDescriptor> bitmapIcon =
+ Completer<BitmapDescriptor>();
+ final ImageConfiguration config = createLocalImageConfiguration(context);
+
+ const AssetImage('assets/red_square.png')
+ .resolve(config)
+ .addListener(ImageStreamListener((ImageInfo image, bool sync) async {
+ final ByteData? bytes =
+ await image.image.toByteData(format: ImageByteFormat.png);
+ if (bytes == null) {
+ bitmapIcon.completeError(Exception('Unable to encode icon'));
+ return;
+ }
+ final BitmapDescriptor bitmap =
+ BitmapDescriptor.fromBytes(bytes.buffer.asUint8List());
+ bitmapIcon.complete(bitmap);
+ }));
+
+ return await bitmapIcon.future;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final MarkerId? selectedId = selectedMarker;
+ return Stack(children: <Widget>[
+ Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Expanded(
+ child: ExampleGoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(-33.852, 151.211),
+ zoom: 11.0,
+ ),
+ markers: Set<Marker>.of(markers.values),
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('Add'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _remove(selectedId),
+ child: const Text('Remove'),
+ ),
+ ],
+ ),
+ Wrap(
+ alignment: WrapAlignment.spaceEvenly,
+ children: <Widget>[
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeInfo(selectedId),
+ child: const Text('change info'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _changeInfoAnchor(selectedId),
+ child: const Text('change info anchor'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeAlpha(selectedId),
+ child: const Text('change alpha'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeAnchor(selectedId),
+ child: const Text('change anchor'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _toggleDraggable(selectedId),
+ child: const Text('toggle draggable'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _toggleFlat(selectedId),
+ child: const Text('toggle flat'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _changePosition(selectedId),
+ child: const Text('change position'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _changeRotation(selectedId),
+ child: const Text('change rotation'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ TextButton(
+ onPressed:
+ selectedId == null ? null : () => _changeZIndex(selectedId),
+ child: const Text('change zIndex'),
+ ),
+ TextButton(
+ onPressed: selectedId == null
+ ? null
+ : () {
+ _getAssetIcon(context).then(
+ (BitmapDescriptor icon) {
+ _setMarkerIcon(selectedId, icon);
+ },
+ );
+ },
+ child: const Text('set marker icon'),
+ ),
+ ],
+ ),
+ ],
+ ),
+ Visibility(
+ visible: markerPosition != null,
+ child: Container(
+ color: Colors.white70,
+ height: 30,
+ padding: const EdgeInsets.only(left: 12, right: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ mainAxisSize: MainAxisSize.max,
+ children: <Widget>[
+ if (markerPosition == null)
+ Container()
+ else
+ Expanded(child: Text('lat: ${markerPosition!.latitude}')),
+ if (markerPosition == null)
+ Container()
+ else
+ Expanded(child: Text('lng: ${markerPosition!.longitude}')),
+ ],
+ ),
+ ),
+ ),
+ ]);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polygon.dart
new file mode 100644
index 0000000..b41cb5d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polygon.dart
@@ -0,0 +1,306 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlacePolygonPage extends GoogleMapExampleAppPage {
+ const PlacePolygonPage({Key? key})
+ : super(const Icon(Icons.linear_scale), 'Place polygon', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlacePolygonBody();
+ }
+}
+
+class PlacePolygonBody extends StatefulWidget {
+ const PlacePolygonBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlacePolygonBodyState();
+}
+
+class PlacePolygonBodyState extends State<PlacePolygonBody> {
+ PlacePolygonBodyState();
+
+ ExampleGoogleMapController? controller;
+ Map<PolygonId, Polygon> polygons = <PolygonId, Polygon>{};
+ Map<PolygonId, double> polygonOffsets = <PolygonId, double>{};
+ int _polygonIdCounter = 0;
+ PolygonId? selectedPolygon;
+
+ // Values when toggling polygon color
+ int strokeColorsIndex = 0;
+ int fillColorsIndex = 0;
+ List<Color> colors = <Color>[
+ Colors.purple,
+ Colors.red,
+ Colors.green,
+ Colors.pink,
+ ];
+
+ // Values when toggling polygon width
+ int widthsIndex = 0;
+ List<int> widths = <int>[10, 20, 5];
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onPolygonTapped(PolygonId polygonId) {
+ setState(() {
+ selectedPolygon = polygonId;
+ });
+ }
+
+ void _remove(PolygonId polygonId) {
+ setState(() {
+ if (polygons.containsKey(polygonId)) {
+ polygons.remove(polygonId);
+ }
+ selectedPolygon = null;
+ });
+ }
+
+ void _add() {
+ final int polygonCount = polygons.length;
+
+ if (polygonCount == 12) {
+ return;
+ }
+
+ final String polygonIdVal = 'polygon_id_$_polygonIdCounter';
+ final PolygonId polygonId = PolygonId(polygonIdVal);
+
+ final Polygon polygon = Polygon(
+ polygonId: polygonId,
+ consumeTapEvents: true,
+ strokeColor: Colors.orange,
+ strokeWidth: 5,
+ fillColor: Colors.green,
+ points: _createPoints(),
+ onTap: () {
+ _onPolygonTapped(polygonId);
+ },
+ );
+
+ setState(() {
+ polygons[polygonId] = polygon;
+ polygonOffsets[polygonId] = _polygonIdCounter.ceilToDouble();
+ // increment _polygonIdCounter to have unique polygon id each time
+ _polygonIdCounter++;
+ });
+ }
+
+ void _toggleGeodesic(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ geodesicParam: !polygon.geodesic,
+ );
+ });
+ }
+
+ void _toggleVisible(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ visibleParam: !polygon.visible,
+ );
+ });
+ }
+
+ void _changeStrokeColor(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ strokeColorParam: colors[++strokeColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeFillColor(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ fillColorParam: colors[++fillColorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeWidth(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ strokeWidthParam: widths[++widthsIndex % widths.length],
+ );
+ });
+ }
+
+ void _addHoles(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] =
+ polygon.copyWith(holesParam: _createHoles(polygonId));
+ });
+ }
+
+ void _removeHoles(PolygonId polygonId) {
+ final Polygon polygon = polygons[polygonId]!;
+ setState(() {
+ polygons[polygonId] = polygon.copyWith(
+ holesParam: <List<LatLng>>[],
+ );
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final PolygonId? selectedId = selectedPolygon;
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(52.4478, -3.5402),
+ zoom: 7.0,
+ ),
+ polygons: Set<Polygon>.of(polygons.values),
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Row(
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('add'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _remove(selectedId),
+ child: const Text('remove'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleGeodesic(selectedId),
+ child: const Text('toggle geodesic'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : ((polygons[selectedId]!.holes.isNotEmpty)
+ ? null
+ : () => _addHoles(selectedId)),
+ child: const Text('add holes'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : ((polygons[selectedId]!.holes.isEmpty)
+ ? null
+ : () => _removeHoles(selectedId)),
+ child: const Text('remove holes'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeWidth(selectedId),
+ child: const Text('change stroke width'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeStrokeColor(selectedId),
+ child: const Text('change stroke color'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeFillColor(selectedId),
+ child: const Text('change fill color'),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ List<LatLng> _createPoints() {
+ final List<LatLng> points = <LatLng>[];
+ final double offset = _polygonIdCounter.ceilToDouble();
+ points.add(_createLatLng(51.2395 + offset, -3.4314));
+ points.add(_createLatLng(53.5234 + offset, -3.5314));
+ points.add(_createLatLng(52.4351 + offset, -4.5235));
+ points.add(_createLatLng(52.1231 + offset, -5.0829));
+ return points;
+ }
+
+ List<List<LatLng>> _createHoles(PolygonId polygonId) {
+ final List<List<LatLng>> holes = <List<LatLng>>[];
+ final double offset = polygonOffsets[polygonId]!;
+
+ final List<LatLng> hole1 = <LatLng>[];
+ hole1.add(_createLatLng(51.8395 + offset, -3.8814));
+ hole1.add(_createLatLng(52.0234 + offset, -3.9914));
+ hole1.add(_createLatLng(52.1351 + offset, -4.4435));
+ hole1.add(_createLatLng(52.0231 + offset, -4.5829));
+ holes.add(hole1);
+
+ final List<LatLng> hole2 = <LatLng>[];
+ hole2.add(_createLatLng(52.2395 + offset, -3.6814));
+ hole2.add(_createLatLng(52.4234 + offset, -3.7914));
+ hole2.add(_createLatLng(52.5351 + offset, -4.2435));
+ hole2.add(_createLatLng(52.4231 + offset, -4.3829));
+ holes.add(hole2);
+
+ return holes;
+ }
+
+ LatLng _createLatLng(double lat, double lng) {
+ return LatLng(lat, lng);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polyline.dart
new file mode 100644
index 0000000..004206b
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polyline.dart
@@ -0,0 +1,325 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class PlacePolylinePage extends GoogleMapExampleAppPage {
+ const PlacePolylinePage({Key? key})
+ : super(const Icon(Icons.linear_scale), 'Place polyline', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const PlacePolylineBody();
+ }
+}
+
+class PlacePolylineBody extends StatefulWidget {
+ const PlacePolylineBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => PlacePolylineBodyState();
+}
+
+class PlacePolylineBodyState extends State<PlacePolylineBody> {
+ PlacePolylineBodyState();
+
+ ExampleGoogleMapController? controller;
+ Map<PolylineId, Polyline> polylines = <PolylineId, Polyline>{};
+ int _polylineIdCounter = 0;
+ PolylineId? selectedPolyline;
+
+ // Values when toggling polyline color
+ int colorsIndex = 0;
+ List<Color> colors = <Color>[
+ Colors.purple,
+ Colors.red,
+ Colors.green,
+ Colors.pink,
+ ];
+
+ // Values when toggling polyline width
+ int widthsIndex = 0;
+ List<int> widths = <int>[10, 20, 5];
+
+ int jointTypesIndex = 0;
+ List<JointType> jointTypes = <JointType>[
+ JointType.mitered,
+ JointType.bevel,
+ JointType.round
+ ];
+
+ // Values when toggling polyline end cap type
+ int endCapsIndex = 0;
+ List<Cap> endCaps = <Cap>[Cap.buttCap, Cap.squareCap, Cap.roundCap];
+
+ // Values when toggling polyline start cap type
+ int startCapsIndex = 0;
+ List<Cap> startCaps = <Cap>[Cap.buttCap, Cap.squareCap, Cap.roundCap];
+
+ // Values when toggling polyline pattern
+ int patternsIndex = 0;
+ List<List<PatternItem>> patterns = <List<PatternItem>>[
+ <PatternItem>[],
+ <PatternItem>[
+ PatternItem.dash(30.0),
+ PatternItem.gap(20.0),
+ PatternItem.dot,
+ PatternItem.gap(20.0)
+ ],
+ <PatternItem>[PatternItem.dash(30.0), PatternItem.gap(20.0)],
+ <PatternItem>[PatternItem.dot, PatternItem.gap(10.0)],
+ ];
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _onPolylineTapped(PolylineId polylineId) {
+ setState(() {
+ selectedPolyline = polylineId;
+ });
+ }
+
+ void _remove(PolylineId polylineId) {
+ setState(() {
+ if (polylines.containsKey(polylineId)) {
+ polylines.remove(polylineId);
+ }
+ selectedPolyline = null;
+ });
+ }
+
+ void _add() {
+ final int polylineCount = polylines.length;
+
+ if (polylineCount == 12) {
+ return;
+ }
+
+ final String polylineIdVal = 'polyline_id_$_polylineIdCounter';
+ _polylineIdCounter++;
+ final PolylineId polylineId = PolylineId(polylineIdVal);
+
+ final Polyline polyline = Polyline(
+ polylineId: polylineId,
+ consumeTapEvents: true,
+ color: Colors.orange,
+ width: 5,
+ points: _createPoints(),
+ onTap: () {
+ _onPolylineTapped(polylineId);
+ },
+ );
+
+ setState(() {
+ polylines[polylineId] = polyline;
+ });
+ }
+
+ void _toggleGeodesic(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ geodesicParam: !polyline.geodesic,
+ );
+ });
+ }
+
+ void _toggleVisible(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ visibleParam: !polyline.visible,
+ );
+ });
+ }
+
+ void _changeColor(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ colorParam: colors[++colorsIndex % colors.length],
+ );
+ });
+ }
+
+ void _changeWidth(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ widthParam: widths[++widthsIndex % widths.length],
+ );
+ });
+ }
+
+ void _changeJointType(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ jointTypeParam: jointTypes[++jointTypesIndex % jointTypes.length],
+ );
+ });
+ }
+
+ void _changeEndCap(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ endCapParam: endCaps[++endCapsIndex % endCaps.length],
+ );
+ });
+ }
+
+ void _changeStartCap(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ startCapParam: startCaps[++startCapsIndex % startCaps.length],
+ );
+ });
+ }
+
+ void _changePattern(PolylineId polylineId) {
+ final Polyline polyline = polylines[polylineId]!;
+ setState(() {
+ polylines[polylineId] = polyline.copyWith(
+ patternsParam: patterns[++patternsIndex % patterns.length],
+ );
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final bool isIOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;
+
+ final PolylineId? selectedId = selectedPolyline;
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(53.1721, -3.5402),
+ zoom: 7.0,
+ ),
+ polylines: Set<Polyline>.of(polylines.values),
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: <Widget>[
+ Row(
+ children: <Widget>[
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: _add,
+ child: const Text('add'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _remove(selectedId),
+ child: const Text('remove'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleVisible(selectedId),
+ child: const Text('toggle visible'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _toggleGeodesic(selectedId),
+ child: const Text('toggle geodesic'),
+ ),
+ ],
+ ),
+ Column(
+ children: <Widget>[
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeWidth(selectedId),
+ child: const Text('change width'),
+ ),
+ TextButton(
+ onPressed: (selectedId == null)
+ ? null
+ : () => _changeColor(selectedId),
+ child: const Text('change color'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changeStartCap(selectedId),
+ child: const Text('change start cap [Android only]'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changeEndCap(selectedId),
+ child: const Text('change end cap [Android only]'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changeJointType(selectedId),
+ child: const Text('change joint type [Android only]'),
+ ),
+ TextButton(
+ onPressed: isIOS || (selectedId == null)
+ ? null
+ : () => _changePattern(selectedId),
+ child: const Text('change pattern [Android only]'),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ List<LatLng> _createPoints() {
+ final List<LatLng> points = <LatLng>[];
+ final double offset = _polylineIdCounter.ceilToDouble();
+ points.add(_createLatLng(51.4816 + offset, -3.1791));
+ points.add(_createLatLng(53.0430 + offset, -2.9925));
+ points.add(_createLatLng(53.1396 + offset, -4.2739));
+ points.add(_createLatLng(52.4153 + offset, -4.0829));
+ return points;
+ }
+
+ LatLng _createLatLng(double lat, double lng) {
+ return LatLng(lat, lng);
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/scrolling_map.dart
new file mode 100644
index 0000000..7a9b75c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/scrolling_map.dart
@@ -0,0 +1,114 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const LatLng _center = LatLng(32.080664, 34.9563837);
+
+class ScrollingMapPage extends GoogleMapExampleAppPage {
+ const ScrollingMapPage({Key? key})
+ : super(const Icon(Icons.map), 'Scrolling map', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const ScrollingMapBody();
+ }
+}
+
+class ScrollingMapBody extends StatelessWidget {
+ const ScrollingMapBody({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return ListView(
+ children: <Widget>[
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 30.0),
+ child: Column(
+ children: <Widget>[
+ const Padding(
+ padding: EdgeInsets.only(bottom: 12.0),
+ child: Text('This map consumes all touch events.'),
+ ),
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: _center,
+ zoom: 11.0,
+ ),
+ gestureRecognizers: //
+ <Factory<OneSequenceGestureRecognizer>>{
+ Factory<OneSequenceGestureRecognizer>(
+ () => EagerGestureRecognizer(),
+ ),
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 30.0),
+ child: Column(
+ children: <Widget>[
+ const Text("This map doesn't consume the vertical drags."),
+ const Padding(
+ padding: EdgeInsets.only(bottom: 12.0),
+ child:
+ Text('It still gets other gestures (e.g scale or tap).'),
+ ),
+ Center(
+ child: SizedBox(
+ width: 300.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: _center,
+ zoom: 11.0,
+ ),
+ markers: <Marker>{
+ Marker(
+ markerId: const MarkerId('test_marker_id'),
+ position: LatLng(
+ _center.latitude,
+ _center.longitude,
+ ),
+ infoWindow: const InfoWindow(
+ title: 'An interesting location',
+ snippet: '*',
+ ),
+ ),
+ },
+ gestureRecognizers: <
+ Factory<OneSequenceGestureRecognizer>>{
+ Factory<OneSequenceGestureRecognizer>(
+ () => ScaleGestureRecognizer(),
+ ),
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/snapshot.dart
new file mode 100644
index 0000000..56a90a8
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/snapshot.dart
@@ -0,0 +1,76 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+const CameraPosition _kInitialPosition =
+ CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0);
+
+class SnapshotPage extends GoogleMapExampleAppPage {
+ const SnapshotPage({Key? key})
+ : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map',
+ key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return _SnapshotBody();
+ }
+}
+
+class _SnapshotBody extends StatefulWidget {
+ @override
+ _SnapshotBodyState createState() => _SnapshotBodyState();
+}
+
+class _SnapshotBodyState extends State<_SnapshotBody> {
+ ExampleGoogleMapController? _mapController;
+ Uint8List? _imageBytes;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ SizedBox(
+ height: 180,
+ child: ExampleGoogleMap(
+ onMapCreated: onMapCreated,
+ initialCameraPosition: _kInitialPosition,
+ ),
+ ),
+ TextButton(
+ child: const Text('Take a snapshot'),
+ onPressed: () async {
+ final Uint8List? imageBytes =
+ await _mapController?.takeSnapshot();
+ setState(() {
+ _imageBytes = imageBytes;
+ });
+ },
+ ),
+ Container(
+ decoration: BoxDecoration(color: Colors.blueGrey[50]),
+ height: 180,
+ child: _imageBytes != null ? Image.memory(_imageBytes!) : null,
+ ),
+ ],
+ ),
+ );
+ }
+
+ // ignore: use_setters_to_change_properties
+ void onMapCreated(ExampleGoogleMapController controller) {
+ _mapController = controller;
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart
new file mode 100644
index 0000000..555f037
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart
@@ -0,0 +1,156 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:typed_data';
+import 'dart:ui' as ui;
+
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+import 'example_google_map.dart';
+import 'page.dart';
+
+class TileOverlayPage extends GoogleMapExampleAppPage {
+ const TileOverlayPage({Key? key})
+ : super(const Icon(Icons.map), 'Tile overlay', key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const TileOverlayBody();
+ }
+}
+
+class TileOverlayBody extends StatefulWidget {
+ const TileOverlayBody({Key? key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => TileOverlayBodyState();
+}
+
+class TileOverlayBodyState extends State<TileOverlayBody> {
+ TileOverlayBodyState();
+
+ ExampleGoogleMapController? controller;
+ TileOverlay? _tileOverlay;
+
+ // ignore: use_setters_to_change_properties
+ void _onMapCreated(ExampleGoogleMapController controller) {
+ this.controller = controller;
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _removeTileOverlay() {
+ setState(() {
+ _tileOverlay = null;
+ });
+ }
+
+ void _addTileOverlay() {
+ final TileOverlay tileOverlay = TileOverlay(
+ tileOverlayId: const TileOverlayId('tile_overlay_1'),
+ tileProvider: _DebugTileProvider(),
+ );
+ setState(() {
+ _tileOverlay = tileOverlay;
+ });
+ }
+
+ void _clearTileCache() {
+ if (_tileOverlay != null && controller != null) {
+ controller!.clearTileCache(_tileOverlay!.tileOverlayId);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final Set<TileOverlay> overlays = <TileOverlay>{
+ if (_tileOverlay != null) _tileOverlay!,
+ };
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ Center(
+ child: SizedBox(
+ width: 350.0,
+ height: 300.0,
+ child: ExampleGoogleMap(
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(59.935460, 30.325177),
+ zoom: 7.0,
+ ),
+ tileOverlays: overlays,
+ onMapCreated: _onMapCreated,
+ ),
+ ),
+ ),
+ TextButton(
+ onPressed: _addTileOverlay,
+ child: const Text('Add tile overlay'),
+ ),
+ TextButton(
+ onPressed: _removeTileOverlay,
+ child: const Text('Remove tile overlay'),
+ ),
+ TextButton(
+ onPressed: _clearTileCache,
+ child: const Text('Clear tile cache'),
+ ),
+ ],
+ );
+ }
+}
+
+class _DebugTileProvider implements TileProvider {
+ _DebugTileProvider() {
+ boxPaint.isAntiAlias = true;
+ boxPaint.color = Colors.blue;
+ boxPaint.strokeWidth = 2.0;
+ boxPaint.style = PaintingStyle.stroke;
+ }
+
+ static const int width = 100;
+ static const int height = 100;
+ static final Paint boxPaint = Paint();
+ static const TextStyle textStyle = TextStyle(
+ color: Colors.red,
+ fontSize: 20,
+ );
+
+ @override
+ Future<Tile> getTile(int x, int y, int? zoom) async {
+ final ui.PictureRecorder recorder = ui.PictureRecorder();
+ final Canvas canvas = Canvas(recorder);
+ final TextSpan textSpan = TextSpan(
+ text: '$x,$y',
+ style: textStyle,
+ );
+ final TextPainter textPainter = TextPainter(
+ text: textSpan,
+ textDirection: TextDirection.ltr,
+ );
+ textPainter.layout(
+ minWidth: 0.0,
+ maxWidth: width.toDouble(),
+ );
+ const Offset offset = Offset(0, 0);
+ textPainter.paint(canvas, offset);
+ canvas.drawRect(
+ Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint);
+ final ui.Picture picture = recorder.endRecording();
+ final Uint8List byteData = await picture
+ .toImage(width, height)
+ .then((ui.Image image) =>
+ image.toByteData(format: ui.ImageByteFormat.png))
+ .then((ByteData? byteData) => byteData!.buffer.asUint8List());
+ return Tile(width, height, byteData);
+ }
+}
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
new file mode 100644
index 0000000..b4fc5db
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml
@@ -0,0 +1,31 @@
+name: google_maps_flutter_example
+description: Demonstrates how to use the google_maps_flutter plugin.
+publish_to: none
+
+environment:
+ sdk: ">=2.14.0 <3.0.0"
+ flutter: ">=2.8.0"
+
+dependencies:
+ cupertino_icons: ^0.1.0
+ flutter:
+ sdk: flutter
+ flutter_plugin_android_lifecycle: ^2.0.1
+ google_maps_flutter_ios:
+ # When depending on this package from a real application you should use:
+ # google_maps_flutter_ios: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # 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: ../
+
+dev_dependencies:
+ flutter_driver:
+ sdk: flutter
+ integration_test:
+ sdk: flutter
+
+flutter:
+ uses-material-design: true
+ assets:
+ - assets/
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/test_driver/integration_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/test_driver/integration_test.dart
new file mode 100644
index 0000000..4f10f2a
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/test_driver/integration_test.dart
@@ -0,0 +1,7 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:integration_test/integration_test_driver.dart';
+
+Future<void> main() => integrationDriver();
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Assets/.gitkeep b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Assets/.gitkeep
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Assets/.gitkeep
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Assets/.gitkeep
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.m
similarity index 89%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.m
index d62f19c..70bde90 100644
--- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.m
@@ -11,7 +11,7 @@
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
FLTGoogleMapFactory *googleMapFactory = [[FLTGoogleMapFactory alloc] initWithRegistrar:registrar];
[registrar registerViewFactory:googleMapFactory
- withId:@"plugins.flutter.io/google_maps"
+ withId:@"plugins.flutter.dev/google_maps_ios"
gestureRecognizersBlockingPolicy:
FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded];
}
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m
similarity index 99%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m
index 3538cc4..41c5d77 100644
--- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m
@@ -76,7 +76,7 @@
// https://github.com/flutter/flutter/issues/104121
[self interpretMapOptions:args[@"options"]];
NSString *channelName =
- [NSString stringWithFormat:@"plugins.flutter.io/google_maps_%lld", viewId];
+ [NSString stringWithFormat:@"plugins.flutter.dev/google_maps_ios_%lld", viewId];
_channel = [FlutterMethodChannel methodChannelWithName:channelName
binaryMessenger:registrar.messenger];
__weak __typeof__(self) weakSelf = self;
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m
similarity index 100%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h
similarity index 63%
rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h
index 9969e71..791c3aa 100644
--- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h
@@ -3,9 +3,9 @@
// found in the LICENSE file.
#import <Foundation/Foundation.h>
-#import <google_maps_flutter/FLTGoogleMapJSONConversions.h>
-#import <google_maps_flutter/FLTGoogleMapTileOverlayController.h>
-#import <google_maps_flutter/FLTGoogleMapsPlugin.h>
+#import <google_maps_flutter_ios/FLTGoogleMapJSONConversions.h>
+#import <google_maps_flutter_ios/FLTGoogleMapTileOverlayController.h>
+#import <google_maps_flutter_ios/FLTGoogleMapsPlugin.h>
FOUNDATION_EXPORT double google_maps_flutterVersionNumber;
FOUNDATION_EXPORT const unsigned char google_maps_flutterVersionString[];
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap
new file mode 100644
index 0000000..699e675
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap
@@ -0,0 +1,10 @@
+framework module google_maps_flutter_ios {
+ umbrella header "google_maps_flutter_ios-umbrella.h"
+
+ export *
+ module * { export * }
+
+ explicit module Test {
+ header "GoogleMapController_Test.h"
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec
similarity index 88%
rename from packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec
rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec
index e34919c..14be02f 100644
--- a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec
@@ -2,7 +2,7 @@
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
- s.name = 'google_maps_flutter'
+ s.name = 'google_maps_flutter_ios'
s.version = '0.0.1'
s.summary = 'Google Maps for Flutter'
s.description = <<-DESC
@@ -12,11 +12,11 @@
s.homepage = 'https://github.com/flutter/plugins'
s.license = { :type => 'BSD', :file => '../LICENSE' }
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
- s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter' }
- s.documentation_url = 'https://pub.dev/packages/google_maps_flutter'
+ s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter/ios' }
+ s.documentation_url = 'https://pub.dev/packages/google_maps_flutter_ios'
s.source_files = 'Classes/**/*.{h,m}'
s.public_header_files = 'Classes/**/*.h'
- s.module_map = 'Classes/google_maps_flutter.modulemap'
+ s.module_map = 'Classes/google_maps_flutter_ios.modulemap'
s.dependency 'Flutter'
s.dependency 'GoogleMaps'
s.static_framework = true
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/google_maps_flutter_ios.dart
new file mode 100644
index 0000000..c4aabbb
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/google_maps_flutter_ios.dart
@@ -0,0 +1,5 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'src/google_maps_flutter_ios.dart';
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart
similarity index 67%
copy from packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart
copy to packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart
index 95ca969..8fae1a3 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart
@@ -2,66 +2,59 @@
// 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';
+import 'package:flutter/services.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
-/// A method-channel-based implementation of [GoogleMapsInspectorPlatform], for
-/// use in tests in conjunction with [MethodChannelGoogleMapsFlutter].
-// TODO(stuartmorgan): Move this into the platform implementations when
-// federating the mobile implementations.
-class MethodChannelGoogleMapsInspector extends GoogleMapsInspectorPlatform {
+/// An Android of implementation of [GoogleMapsInspectorPlatform].
+@visibleForTesting
+class GoogleMapsInspectorIOS extends GoogleMapsInspectorPlatform {
/// Creates a method-channel-based inspector instance that gets the channel
- /// for a given map ID from [mapsPlatform].
- MethodChannelGoogleMapsInspector(MethodChannelGoogleMapsFlutter mapsPlatform)
- : _mapsPlatform = mapsPlatform;
+ /// for a given map ID from [channelProvider].
+ GoogleMapsInspectorIOS(MethodChannel? Function(int mapId) channelProvider)
+ : _channelProvider = channelProvider;
- final MethodChannelGoogleMapsFlutter _mapsPlatform;
+ final MethodChannel? Function(int mapId) _channelProvider;
@override
Future<bool> areBuildingsEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isBuildingsEnabled'))!;
}
@override
Future<bool> areRotateGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isRotateGesturesEnabled'))!;
}
@override
Future<bool> areScrollGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isScrollGesturesEnabled'))!;
}
@override
Future<bool> areTiltGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isTiltGesturesEnabled'))!;
}
@override
Future<bool> areZoomControlsEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isZoomControlsEnabled'))!;
}
@override
Future<bool> areZoomGesturesEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isZoomGesturesEnabled'))!;
}
@override
Future<MinMaxZoomPreference> getMinMaxZoomLevels({required int mapId}) async {
- final List<double> zoomLevels = (await _mapsPlatform
- .channel(mapId)
+ final List<double> zoomLevels = (await _channelProvider(mapId)!
.invokeMethod<List<dynamic>>('map#getMinMaxZoomLevels'))!
.cast<double>();
return MinMaxZoomPreference(zoomLevels[0], zoomLevels[1]);
@@ -70,8 +63,7 @@
@override
Future<TileOverlay?> getTileOverlayInfo(TileOverlayId tileOverlayId,
{required int mapId}) async {
- final Map<String, Object?>? tileInfo = await _mapsPlatform
- .channel(mapId)
+ final Map<String, Object?>? tileInfo = await _channelProvider(mapId)!
.invokeMapMethod<String, dynamic>(
'map#getTileOverlayInfo', <String, String>{
'tileOverlayId': tileOverlayId.value,
@@ -91,36 +83,31 @@
@override
Future<bool> isCompassEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isCompassEnabled'))!;
}
@override
Future<bool> isLiteModeEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isLiteModeEnabled'))!;
}
@override
Future<bool> isMapToolbarEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isMapToolbarEnabled'))!;
}
@override
Future<bool> isMyLocationButtonEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isMyLocationButtonEnabled'))!;
}
@override
Future<bool> isTrafficEnabled({required int mapId}) async {
- return (await _mapsPlatform
- .channel(mapId)
+ return (await _channelProvider(mapId)!
.invokeMethod<bool>('map#isTrafficEnabled'))!;
}
}
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
new file mode 100644
index 0000000..5298377
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart
@@ -0,0 +1,644 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+import 'package:stream_transform/stream_transform.dart';
+
+import 'google_map_inspector_ios.dart';
+
+// TODO(stuartmorgan): Remove the dependency on platform interface toJson
+// methods. Channel serialization details should all be package-internal.
+
+/// Error thrown when an unknown map ID is provided to a method channel API.
+class UnknownMapIDError extends Error {
+ /// Creates an assertion error with the provided [mapId] and optional
+ /// [message].
+ UnknownMapIDError(this.mapId, [this.message]);
+
+ /// The unknown ID.
+ final int mapId;
+
+ /// Message describing the assertion error.
+ final Object? message;
+
+ @override
+ String toString() {
+ if (message != null) {
+ return 'Unknown map ID $mapId: ${Error.safeToString(message)}';
+ }
+ return 'Unknown map ID $mapId';
+ }
+}
+
+/// An implementation of [GoogleMapsFlutterPlatform] for iOS.
+class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform {
+ /// Registers the iOS implementation of GoogleMapsFlutterPlatform.
+ static void registerWith() {
+ GoogleMapsFlutterPlatform.instance = GoogleMapsFlutterIOS();
+ }
+
+ // Keep a collection of id -> channel
+ // Every method call passes the int mapId
+ final Map<int, MethodChannel> _channels = <int, MethodChannel>{};
+
+ /// Accesses the MethodChannel associated to the passed mapId.
+ MethodChannel _channel(int mapId) {
+ final MethodChannel? channel = _channels[mapId];
+ if (channel == null) {
+ throw UnknownMapIDError(mapId);
+ }
+ return channel;
+ }
+
+ // Keep a collection of mapId to a map of TileOverlays.
+ final Map<int, Map<TileOverlayId, TileOverlay>> _tileOverlays =
+ <int, Map<TileOverlayId, TileOverlay>>{};
+
+ /// Returns the channel for [mapId], creating it if it doesn't already exist.
+ @visibleForTesting
+ MethodChannel ensureChannelInitialized(int mapId) {
+ MethodChannel? channel = _channels[mapId];
+ if (channel == null) {
+ channel = MethodChannel('plugins.flutter.dev/google_maps_ios_$mapId');
+ channel.setMethodCallHandler(
+ (MethodCall call) => _handleMethodCall(call, mapId));
+ _channels[mapId] = channel;
+ }
+ return channel;
+ }
+
+ @override
+ Future<void> init(int mapId) {
+ final MethodChannel channel = ensureChannelInitialized(mapId);
+ return channel.invokeMethod<void>('map#waitForMap');
+ }
+
+ @override
+ void dispose({required int mapId}) {
+ // Noop!
+ }
+
+ // The controller we need to broadcast the different events coming
+ // from handleMethodCall.
+ //
+ // It is a `broadcast` because multiple controllers will connect to
+ // different stream views of this Controller.
+ final StreamController<MapEvent<Object?>> _mapEventStreamController =
+ StreamController<MapEvent<Object?>>.broadcast();
+
+ // Returns a filtered view of the events in the _controller, by mapId.
+ Stream<MapEvent<Object?>> _events(int mapId) =>
+ _mapEventStreamController.stream
+ .where((MapEvent<Object?> event) => event.mapId == mapId);
+
+ @override
+ Stream<CameraMoveStartedEvent> onCameraMoveStarted({required int mapId}) {
+ return _events(mapId).whereType<CameraMoveStartedEvent>();
+ }
+
+ @override
+ Stream<CameraMoveEvent> onCameraMove({required int mapId}) {
+ return _events(mapId).whereType<CameraMoveEvent>();
+ }
+
+ @override
+ Stream<CameraIdleEvent> onCameraIdle({required int mapId}) {
+ return _events(mapId).whereType<CameraIdleEvent>();
+ }
+
+ @override
+ Stream<MarkerTapEvent> onMarkerTap({required int mapId}) {
+ return _events(mapId).whereType<MarkerTapEvent>();
+ }
+
+ @override
+ Stream<InfoWindowTapEvent> onInfoWindowTap({required int mapId}) {
+ return _events(mapId).whereType<InfoWindowTapEvent>();
+ }
+
+ @override
+ Stream<MarkerDragStartEvent> onMarkerDragStart({required int mapId}) {
+ return _events(mapId).whereType<MarkerDragStartEvent>();
+ }
+
+ @override
+ Stream<MarkerDragEvent> onMarkerDrag({required int mapId}) {
+ return _events(mapId).whereType<MarkerDragEvent>();
+ }
+
+ @override
+ Stream<MarkerDragEndEvent> onMarkerDragEnd({required int mapId}) {
+ return _events(mapId).whereType<MarkerDragEndEvent>();
+ }
+
+ @override
+ Stream<PolylineTapEvent> onPolylineTap({required int mapId}) {
+ return _events(mapId).whereType<PolylineTapEvent>();
+ }
+
+ @override
+ Stream<PolygonTapEvent> onPolygonTap({required int mapId}) {
+ return _events(mapId).whereType<PolygonTapEvent>();
+ }
+
+ @override
+ Stream<CircleTapEvent> onCircleTap({required int mapId}) {
+ return _events(mapId).whereType<CircleTapEvent>();
+ }
+
+ @override
+ Stream<MapTapEvent> onTap({required int mapId}) {
+ return _events(mapId).whereType<MapTapEvent>();
+ }
+
+ @override
+ Stream<MapLongPressEvent> onLongPress({required int mapId}) {
+ return _events(mapId).whereType<MapLongPressEvent>();
+ }
+
+ Future<dynamic> _handleMethodCall(MethodCall call, int mapId) async {
+ switch (call.method) {
+ case 'camera#onMoveStarted':
+ _mapEventStreamController.add(CameraMoveStartedEvent(mapId));
+ break;
+ case 'camera#onMove':
+ _mapEventStreamController.add(CameraMoveEvent(
+ mapId,
+ CameraPosition.fromMap(call.arguments['position'])!,
+ ));
+ break;
+ case 'camera#onIdle':
+ _mapEventStreamController.add(CameraIdleEvent(mapId));
+ break;
+ case 'marker#onTap':
+ _mapEventStreamController.add(MarkerTapEvent(
+ mapId,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'marker#onDragStart':
+ _mapEventStreamController.add(MarkerDragStartEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'marker#onDrag':
+ _mapEventStreamController.add(MarkerDragEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'marker#onDragEnd':
+ _mapEventStreamController.add(MarkerDragEndEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'infoWindow#onTap':
+ _mapEventStreamController.add(InfoWindowTapEvent(
+ mapId,
+ MarkerId(call.arguments['markerId'] as String),
+ ));
+ break;
+ case 'polyline#onTap':
+ _mapEventStreamController.add(PolylineTapEvent(
+ mapId,
+ PolylineId(call.arguments['polylineId'] as String),
+ ));
+ break;
+ case 'polygon#onTap':
+ _mapEventStreamController.add(PolygonTapEvent(
+ mapId,
+ PolygonId(call.arguments['polygonId'] as String),
+ ));
+ break;
+ case 'circle#onTap':
+ _mapEventStreamController.add(CircleTapEvent(
+ mapId,
+ CircleId(call.arguments['circleId'] as String),
+ ));
+ break;
+ case 'map#onTap':
+ _mapEventStreamController.add(MapTapEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ ));
+ break;
+ case 'map#onLongPress':
+ _mapEventStreamController.add(MapLongPressEvent(
+ mapId,
+ LatLng.fromJson(call.arguments['position'])!,
+ ));
+ break;
+ case 'tileOverlay#getTile':
+ final Map<TileOverlayId, TileOverlay>? tileOverlaysForThisMap =
+ _tileOverlays[mapId];
+ final String tileOverlayId = call.arguments['tileOverlayId'] as String;
+ final TileOverlay? tileOverlay =
+ tileOverlaysForThisMap?[TileOverlayId(tileOverlayId)];
+ final TileProvider? tileProvider = tileOverlay?.tileProvider;
+ if (tileProvider == null) {
+ return TileProvider.noTile.toJson();
+ }
+ final Tile tile = await tileProvider.getTile(
+ call.arguments['x'] as int,
+ call.arguments['y'] as int,
+ call.arguments['zoom'] as int?,
+ );
+ return tile.toJson();
+ default:
+ throw MissingPluginException();
+ }
+ }
+
+ @override
+ Future<void> updateMapOptions(
+ Map<String, dynamic> optionsUpdate, {
+ required int mapId,
+ }) {
+ assert(optionsUpdate != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'map#update',
+ <String, dynamic>{
+ 'options': optionsUpdate,
+ },
+ );
+ }
+
+ @override
+ Future<void> updateMarkers(
+ MarkerUpdates markerUpdates, {
+ required int mapId,
+ }) {
+ assert(markerUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'markers#update',
+ markerUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updatePolygons(
+ PolygonUpdates polygonUpdates, {
+ required int mapId,
+ }) {
+ assert(polygonUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'polygons#update',
+ polygonUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updatePolylines(
+ PolylineUpdates polylineUpdates, {
+ required int mapId,
+ }) {
+ assert(polylineUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'polylines#update',
+ polylineUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updateCircles(
+ CircleUpdates circleUpdates, {
+ required int mapId,
+ }) {
+ assert(circleUpdates != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'circles#update',
+ circleUpdates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> updateTileOverlays({
+ required Set<TileOverlay> newTileOverlays,
+ required int mapId,
+ }) {
+ final Map<TileOverlayId, TileOverlay>? currentTileOverlays =
+ _tileOverlays[mapId];
+ final Set<TileOverlay> previousSet = currentTileOverlays != null
+ ? currentTileOverlays.values.toSet()
+ : <TileOverlay>{};
+ final _TileOverlayUpdates updates =
+ _TileOverlayUpdates.from(previousSet, newTileOverlays);
+ _tileOverlays[mapId] = keyTileOverlayId(newTileOverlays);
+ return _channel(mapId).invokeMethod<void>(
+ 'tileOverlays#update',
+ updates.toJson(),
+ );
+ }
+
+ @override
+ Future<void> clearTileCache(
+ TileOverlayId tileOverlayId, {
+ required int mapId,
+ }) {
+ return _channel(mapId)
+ .invokeMethod<void>('tileOverlays#clearTileCache', <String, Object>{
+ 'tileOverlayId': tileOverlayId.value,
+ });
+ }
+
+ @override
+ Future<void> animateCamera(
+ CameraUpdate cameraUpdate, {
+ required int mapId,
+ }) {
+ return _channel(mapId)
+ .invokeMethod<void>('camera#animate', <String, Object>{
+ 'cameraUpdate': cameraUpdate.toJson(),
+ });
+ }
+
+ @override
+ Future<void> moveCamera(
+ CameraUpdate cameraUpdate, {
+ required int mapId,
+ }) {
+ return _channel(mapId).invokeMethod<void>('camera#move', <String, dynamic>{
+ 'cameraUpdate': cameraUpdate.toJson(),
+ });
+ }
+
+ @override
+ Future<void> setMapStyle(
+ String? mapStyle, {
+ required int mapId,
+ }) async {
+ final List<dynamic> successAndError = (await _channel(mapId)
+ .invokeMethod<List<dynamic>>('map#setStyle', mapStyle))!;
+ final bool success = successAndError[0] as bool;
+ if (!success) {
+ throw MapStyleException(successAndError[1] as String);
+ }
+ }
+
+ @override
+ Future<LatLngBounds> getVisibleRegion({
+ required int mapId,
+ }) async {
+ final Map<String, dynamic> latLngBounds = (await _channel(mapId)
+ .invokeMapMethod<String, dynamic>('map#getVisibleRegion'))!;
+ final LatLng southwest = LatLng.fromJson(latLngBounds['southwest'])!;
+ final LatLng northeast = LatLng.fromJson(latLngBounds['northeast'])!;
+
+ return LatLngBounds(northeast: northeast, southwest: southwest);
+ }
+
+ @override
+ Future<ScreenCoordinate> getScreenCoordinate(
+ LatLng latLng, {
+ required int mapId,
+ }) async {
+ final Map<String, int> point = (await _channel(mapId)
+ .invokeMapMethod<String, int>(
+ 'map#getScreenCoordinate', latLng.toJson()))!;
+
+ return ScreenCoordinate(x: point['x']!, y: point['y']!);
+ }
+
+ @override
+ Future<LatLng> getLatLng(
+ ScreenCoordinate screenCoordinate, {
+ required int mapId,
+ }) async {
+ final List<dynamic> latLng = (await _channel(mapId)
+ .invokeMethod<List<dynamic>>(
+ 'map#getLatLng', screenCoordinate.toJson()))!;
+ return LatLng(latLng[0] as double, latLng[1] as double);
+ }
+
+ @override
+ Future<void> showMarkerInfoWindow(
+ MarkerId markerId, {
+ required int mapId,
+ }) {
+ assert(markerId != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'markers#showInfoWindow', <String, String>{'markerId': markerId.value});
+ }
+
+ @override
+ Future<void> hideMarkerInfoWindow(
+ MarkerId markerId, {
+ required int mapId,
+ }) {
+ assert(markerId != null);
+ return _channel(mapId).invokeMethod<void>(
+ 'markers#hideInfoWindow', <String, String>{'markerId': markerId.value});
+ }
+
+ @override
+ Future<bool> isMarkerInfoWindowShown(
+ MarkerId markerId, {
+ required int mapId,
+ }) async {
+ assert(markerId != null);
+ return (await _channel(mapId).invokeMethod<bool>(
+ 'markers#isInfoWindowShown',
+ <String, String>{'markerId': markerId.value}))!;
+ }
+
+ @override
+ Future<double> getZoomLevel({
+ required int mapId,
+ }) async {
+ return (await _channel(mapId).invokeMethod<double>('map#getZoomLevel'))!;
+ }
+
+ @override
+ Future<Uint8List?> takeSnapshot({
+ required int mapId,
+ }) {
+ return _channel(mapId).invokeMethod<Uint8List>('map#takeSnapshot');
+ }
+
+ Widget _buildView(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required MapWidgetConfiguration widgetConfiguration,
+ MapObjects mapObjects = const MapObjects(),
+ Map<String, dynamic> mapOptions = const <String, dynamic>{},
+ }) {
+ final Map<String, dynamic> creationParams = <String, dynamic>{
+ 'initialCameraPosition':
+ widgetConfiguration.initialCameraPosition.toMap(),
+ 'options': mapOptions,
+ 'markersToAdd': serializeMarkerSet(mapObjects.markers),
+ 'polygonsToAdd': serializePolygonSet(mapObjects.polygons),
+ 'polylinesToAdd': serializePolylineSet(mapObjects.polylines),
+ 'circlesToAdd': serializeCircleSet(mapObjects.circles),
+ 'tileOverlaysToAdd': serializeTileOverlaySet(mapObjects.tileOverlays),
+ };
+
+ return UiKitView(
+ viewType: 'plugins.flutter.dev/google_maps_ios',
+ onPlatformViewCreated: onPlatformViewCreated,
+ gestureRecognizers: widgetConfiguration.gestureRecognizers,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ );
+ }
+
+ @override
+ Widget buildViewWithConfiguration(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required MapWidgetConfiguration widgetConfiguration,
+ MapConfiguration mapConfiguration = const MapConfiguration(),
+ MapObjects mapObjects = const MapObjects(),
+ }) {
+ return _buildView(
+ creationId,
+ onPlatformViewCreated,
+ widgetConfiguration: widgetConfiguration,
+ mapObjects: mapObjects,
+ mapOptions: _jsonForMapConfiguration(mapConfiguration),
+ );
+ }
+
+ @override
+ Widget buildViewWithTextDirection(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required CameraPosition initialCameraPosition,
+ required TextDirection textDirection,
+ Set<Marker> markers = const <Marker>{},
+ Set<Polygon> polygons = const <Polygon>{},
+ Set<Polyline> polylines = const <Polyline>{},
+ Set<Circle> circles = const <Circle>{},
+ Set<TileOverlay> tileOverlays = const <TileOverlay>{},
+ Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
+ Map<String, dynamic> mapOptions = const <String, dynamic>{},
+ }) {
+ return _buildView(
+ creationId,
+ onPlatformViewCreated,
+ widgetConfiguration: MapWidgetConfiguration(
+ initialCameraPosition: initialCameraPosition,
+ textDirection: textDirection),
+ mapObjects: MapObjects(
+ markers: markers,
+ polygons: polygons,
+ polylines: polylines,
+ circles: circles,
+ tileOverlays: tileOverlays),
+ mapOptions: mapOptions,
+ );
+ }
+
+ @override
+ Widget buildView(
+ int creationId,
+ PlatformViewCreatedCallback onPlatformViewCreated, {
+ required CameraPosition initialCameraPosition,
+ Set<Marker> markers = const <Marker>{},
+ Set<Polygon> polygons = const <Polygon>{},
+ Set<Polyline> polylines = const <Polyline>{},
+ Set<Circle> circles = const <Circle>{},
+ Set<TileOverlay> tileOverlays = const <TileOverlay>{},
+ Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
+ Map<String, dynamic> mapOptions = const <String, dynamic>{},
+ }) {
+ return buildViewWithTextDirection(
+ creationId,
+ onPlatformViewCreated,
+ initialCameraPosition: initialCameraPosition,
+ textDirection: TextDirection.ltr,
+ markers: markers,
+ polygons: polygons,
+ polylines: polylines,
+ circles: circles,
+ tileOverlays: tileOverlays,
+ gestureRecognizers: gestureRecognizers,
+ mapOptions: mapOptions,
+ );
+ }
+
+ @override
+ @visibleForTesting
+ void enableDebugInspection() {
+ GoogleMapsInspectorPlatform.instance =
+ GoogleMapsInspectorIOS((int mapId) => _channel(mapId));
+ }
+}
+
+Map<String, Object> _jsonForMapConfiguration(MapConfiguration config) {
+ final EdgeInsets? padding = config.padding;
+ return <String, Object>{
+ if (config.compassEnabled != null) 'compassEnabled': config.compassEnabled!,
+ if (config.mapToolbarEnabled != null)
+ 'mapToolbarEnabled': config.mapToolbarEnabled!,
+ if (config.cameraTargetBounds != null)
+ 'cameraTargetBounds': config.cameraTargetBounds!.toJson(),
+ if (config.mapType != null) 'mapType': config.mapType!.index,
+ if (config.minMaxZoomPreference != null)
+ 'minMaxZoomPreference': config.minMaxZoomPreference!.toJson(),
+ if (config.rotateGesturesEnabled != null)
+ 'rotateGesturesEnabled': config.rotateGesturesEnabled!,
+ if (config.scrollGesturesEnabled != null)
+ 'scrollGesturesEnabled': config.scrollGesturesEnabled!,
+ if (config.tiltGesturesEnabled != null)
+ 'tiltGesturesEnabled': config.tiltGesturesEnabled!,
+ if (config.zoomControlsEnabled != null)
+ 'zoomControlsEnabled': config.zoomControlsEnabled!,
+ if (config.zoomGesturesEnabled != null)
+ 'zoomGesturesEnabled': config.zoomGesturesEnabled!,
+ if (config.liteModeEnabled != null)
+ 'liteModeEnabled': config.liteModeEnabled!,
+ if (config.trackCameraPosition != null)
+ 'trackCameraPosition': config.trackCameraPosition!,
+ if (config.myLocationEnabled != null)
+ 'myLocationEnabled': config.myLocationEnabled!,
+ if (config.myLocationButtonEnabled != null)
+ 'myLocationButtonEnabled': config.myLocationButtonEnabled!,
+ if (padding != null)
+ 'padding': <double>[
+ padding.top,
+ padding.left,
+ padding.bottom,
+ padding.right,
+ ],
+ if (config.indoorViewEnabled != null)
+ 'indoorEnabled': config.indoorViewEnabled!,
+ if (config.trafficEnabled != null) 'trafficEnabled': config.trafficEnabled!,
+ if (config.buildingsEnabled != null)
+ 'buildingsEnabled': config.buildingsEnabled!,
+ };
+}
+
+/// Update specification for a set of [TileOverlay]s.
+// TODO(stuartmorgan): Fix the missing export of this class in the platform
+// interface, and remove this copy.
+class _TileOverlayUpdates extends MapsObjectUpdates<TileOverlay> {
+ /// Computes [TileOverlayUpdates] given previous and current [TileOverlay]s.
+ _TileOverlayUpdates.from(Set<TileOverlay> previous, Set<TileOverlay> current)
+ : super.from(previous, current, objectName: 'tileOverlay');
+
+ /// Set of TileOverlays to be added in this update.
+ Set<TileOverlay> get tileOverlaysToAdd => objectsToAdd;
+
+ /// Set of TileOverlayIds to be removed in this update.
+ Set<TileOverlayId> get tileOverlayIdsToRemove =>
+ objectIdsToRemove.cast<TileOverlayId>();
+
+ /// Set of TileOverlays to be changed in this update.
+ Set<TileOverlay> get tileOverlaysToChange => objectsToChange;
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml
new file mode 100644
index 0000000..f55da6e
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml
@@ -0,0 +1,29 @@
+name: google_maps_flutter_ios
+description: iOS implementation of the google_maps_flutter plugin.
+repository: https://github.com/flutter/plugins/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.1.10
+
+environment:
+ sdk: ">=2.14.0 <3.0.0"
+ flutter: ">=2.8.0"
+
+flutter:
+ plugin:
+ implements: google_maps_flutter
+ platforms:
+ ios:
+ pluginClass: FLTGoogleMapsPlugin
+ dartPluginClass: GoogleMapsFlutterIOS
+
+dependencies:
+ flutter:
+ sdk: flutter
+ google_maps_flutter_platform_interface: ^2.2.1
+ stream_transform: ^2.0.0
+
+dev_dependencies:
+ async: ^2.5.0
+ flutter_test:
+ sdk: flutter
+ plugin_platform_interface: ^2.0.0
diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart
new file mode 100644
index 0000000..136481c
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart
@@ -0,0 +1,124 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:async/async.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:google_maps_flutter_ios/google_maps_flutter_ios.dart';
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ late List<String> log;
+
+ setUp(() async {
+ log = <String>[];
+ });
+
+ /// Initializes a map with the given ID and canned responses, logging all
+ /// calls to [log].
+ void configureMockMap(
+ GoogleMapsFlutterIOS maps, {
+ required int mapId,
+ required Future<dynamic>? Function(MethodCall call) handler,
+ }) {
+ maps
+ .ensureChannelInitialized(mapId)
+ .setMockMethodCallHandler((MethodCall methodCall) {
+ log.add(methodCall.method);
+ return handler(methodCall);
+ });
+ }
+
+ Future<void> sendPlatformMessage(
+ int mapId, String method, Map<dynamic, dynamic> data) async {
+ final ByteData byteData =
+ const StandardMethodCodec().encodeMethodCall(MethodCall(method, data));
+ await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
+ .handlePlatformMessage('plugins.flutter.dev/google_maps_ios_$mapId',
+ byteData, (ByteData? data) {});
+ }
+
+ test('registers instance', () async {
+ GoogleMapsFlutterIOS.registerWith();
+ expect(GoogleMapsFlutterPlatform.instance, isA<GoogleMapsFlutterIOS>());
+ });
+
+ // Calls each method that uses invokeMethod with a return type other than
+ // void to ensure that the casting/nullability handling succeeds.
+ //
+ // TODO(stuartmorgan): Remove this once there is real test coverage of
+ // each method, since that would cover this issue.
+ test('non-void invokeMethods handle types correctly', () async {
+ const int mapId = 0;
+ final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS();
+ configureMockMap(maps, mapId: mapId,
+ handler: (MethodCall methodCall) async {
+ switch (methodCall.method) {
+ case 'map#getLatLng':
+ return <dynamic>[1.0, 2.0];
+ case 'markers#isInfoWindowShown':
+ return true;
+ case 'map#getZoomLevel':
+ return 2.5;
+ case 'map#takeSnapshot':
+ return null;
+ }
+ });
+
+ await maps.getLatLng(const ScreenCoordinate(x: 0, y: 0), mapId: mapId);
+ await maps.isMarkerInfoWindowShown(const MarkerId(''), mapId: mapId);
+ await maps.getZoomLevel(mapId: mapId);
+ await maps.takeSnapshot(mapId: mapId);
+ // Check that all the invokeMethod calls happened.
+ expect(log, <String>[
+ 'map#getLatLng',
+ 'markers#isInfoWindowShown',
+ 'map#getZoomLevel',
+ 'map#takeSnapshot',
+ ]);
+ });
+
+ test('markers send drag event to correct streams', () async {
+ const int mapId = 1;
+ final Map<dynamic, dynamic> jsonMarkerDragStartEvent = <dynamic, dynamic>{
+ 'mapId': mapId,
+ 'markerId': 'drag-start-marker',
+ 'position': <double>[1.0, 1.0]
+ };
+ final Map<dynamic, dynamic> jsonMarkerDragEvent = <dynamic, dynamic>{
+ 'mapId': mapId,
+ 'markerId': 'drag-marker',
+ 'position': <double>[1.0, 1.0]
+ };
+ final Map<dynamic, dynamic> jsonMarkerDragEndEvent = <dynamic, dynamic>{
+ 'mapId': mapId,
+ 'markerId': 'drag-end-marker',
+ 'position': <double>[1.0, 1.0]
+ };
+
+ final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS();
+ maps.ensureChannelInitialized(mapId);
+
+ final StreamQueue<MarkerDragStartEvent> markerDragStartStream =
+ StreamQueue<MarkerDragStartEvent>(maps.onMarkerDragStart(mapId: mapId));
+ final StreamQueue<MarkerDragEvent> markerDragStream =
+ StreamQueue<MarkerDragEvent>(maps.onMarkerDrag(mapId: mapId));
+ final StreamQueue<MarkerDragEndEvent> markerDragEndStream =
+ StreamQueue<MarkerDragEndEvent>(maps.onMarkerDragEnd(mapId: mapId));
+
+ await sendPlatformMessage(
+ mapId, 'marker#onDragStart', jsonMarkerDragStartEvent);
+ await sendPlatformMessage(mapId, 'marker#onDrag', jsonMarkerDragEvent);
+ await sendPlatformMessage(
+ mapId, 'marker#onDragEnd', jsonMarkerDragEndEvent);
+
+ expect((await markerDragStartStream.next).value.value,
+ equals('drag-start-marker'));
+ expect((await markerDragStream.next).value.value, equals('drag-marker'));
+ expect((await markerDragEndStream.next).value.value,
+ equals('drag-end-marker'));
+ });
+}