diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
new file mode 100644
index 000000000000..d989279aac42
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.1.0
+
+* First open-source version
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/LICENSE b/packages/google_maps_flutter/google_maps_flutter_web/LICENSE
new file mode 100644
index 000000000000..282a0f51aa4a
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2017, the Flutter project 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_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md
new file mode 100644
index 000000000000..e1c1a5330c56
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md
@@ -0,0 +1,51 @@
+# google_maps_flutter_web
+
+This is an implementation of the [google_maps_flutter](https://pub.dev/packages/google_maps_flutter) plugin for web. Behind the scenes, it uses a14n's [google_maps](https://pub.dev/packages/google_maps) dart JS interop layer.
+
+## Usage
+
+### Depend on the package
+
+This package is not an endorsed implementation of the google_maps_flutter plugin yet, so you'll need to modify the `pubspec.yaml` file of your app to depend on this package:
+
+```yaml
+dependencies:
+ google_maps_flutter: ^0.5.28
+ google_maps_flutter_web: ^0.1.0
+```
+
+### Modify web/index.html
+
+Get an API Key for Google Maps JavaScript API. Get started [here](https://developers.google.com/maps/documentation/javascript/get-api-key).
+
+Modify the `
` tag of your `web/index.html` to load the Google Maps JavaScript API, like so:
+
+```html
+
+
+
+
+
+
+```
+
+Now you should be able to use the Google Maps plugin normally.
+
+## Limitations of the web version
+
+The following map options are not available in web, because the map doesn't rotate there:
+
+* `compassEnabled`
+* `rotateGesturesEnabled`
+* `tiltGesturesEnabled`
+
+There's no "Map Toolbar" in web, so the `mapToolbarEnabled` option is unused.
+
+There's no "My Location" widget in web ([tracking issue](https://github.com/flutter/flutter/issues/64073)), so the following options are ignored, for now:
+
+* `myLocationButtonEnabled`
+* `myLocationEnabled`
+
+There's no `defaultMarkerWithHue` in web. If you need colored pins/markers, you may need to use your own asset images.
+
+Indoor and building layers are still not available on the web. Traffic is.
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml
new file mode 100644
index 000000000000..443b16551ec9
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml
@@ -0,0 +1,10 @@
+# This is a temporary file to allow us to unblock the flutter/plugins repo CI.
+# It disables some of lints that were disabled inline. Disabling lints inline
+# is no longer possible, so this file is required.
+# TODO(ditman) https://github.com/flutter/flutter/issues/55000 (clean this up)
+
+include: ../../../analysis_options.yaml
+
+analyzer:
+ errors:
+ undefined_prefixed_name: ignore
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/ios/google_maps_flutter_web.podspec b/packages/google_maps_flutter/google_maps_flutter_web/ios/google_maps_flutter_web.podspec
new file mode 100644
index 000000000000..18db6ced01b6
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/ios/google_maps_flutter_web.podspec
@@ -0,0 +1,23 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint google_maps_flutter_web.podspec' to validate before publishing.
+#
+Pod::Spec.new do |s|
+ s.name = 'google_maps_flutter_web'
+ s.version = '0.1.0'
+ s.summary = 'No-op implementation of google maps flutter web plugin to avoid build issues on iOS'
+ s.description = <<-DESC
+temp fake google_maps_flutter_web plugin
+ DESC
+ s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_web'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'Classes/**/*'
+ s.dependency 'Flutter'
+ s.platform = :ios, '8.0'
+
+ # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
+ s.swift_version = '5.0'
+end
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart
new file mode 100644
index 000000000000..cf133fb9e533
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library google_maps_flutter_web;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:ui' as ui;
+import 'dart:convert';
+
+import 'package:flutter/rendering.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/gestures.dart';
+
+import 'package:sanitize_html/sanitize_html.dart';
+
+import 'package:stream_transform/stream_transform.dart';
+
+import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
+import 'package:flutter_web_plugins/flutter_web_plugins.dart';
+import 'package:google_maps/google_maps.dart' as gmaps;
+
+import 'src/types.dart';
+
+part 'src/google_maps_flutter_web.dart';
+part 'src/google_maps_controller.dart';
+part 'src/circle.dart';
+part 'src/circles.dart';
+part 'src/polygon.dart';
+part 'src/polygons.dart';
+part 'src/polyline.dart';
+part 'src/polylines.dart';
+part 'src/marker.dart';
+part 'src/markers.dart';
+part 'src/convert.dart';
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart
new file mode 100644
index 000000000000..96f9be7aa001
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of google_maps_flutter_web;
+
+/// The `CircleController` class wraps a [gmaps.Circle] and its `onTap` behavior.
+class CircleController {
+ gmaps.Circle _circle;
+
+ final bool _consumeTapEvents;
+
+ /// Creates a `CircleController`, which wraps a [gmaps.Circle] object and its `onTap` behavior.
+ CircleController({
+ @required gmaps.Circle circle,
+ bool consumeTapEvents = false,
+ ui.VoidCallback onTap,
+ }) : _circle = circle,
+ _consumeTapEvents = consumeTapEvents {
+ if (onTap != null) {
+ circle.onClick.listen((_) {
+ onTap.call();
+ });
+ }
+ }
+
+ /// Returns the wrapped [gmaps.Circle]. Only used for testing.
+ @visibleForTesting
+ gmaps.Circle get circle => _circle;
+
+ /// Returns `true` if this Controller will use its own `onTap` handler to consume events.
+ bool get consumeTapEvents => _consumeTapEvents;
+
+ /// Updates the options of the wrapped [gmaps.Circle] object.
+ void update(gmaps.CircleOptions options) {
+ _circle.options = options;
+ }
+
+ /// Disposes of the currently wrapped [gmaps.Circle].
+ void remove() {
+ _circle.visible = false;
+ _circle.radius = 0;
+ _circle.map = null;
+ _circle = null;
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart
new file mode 100644
index 000000000000..c7c33ed1811f
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of google_maps_flutter_web;
+
+/// This class manages all the [CircleController]s associated to a [GoogleMapController].
+class CirclesController extends GeometryController {
+ // A cache of [CircleController]s indexed by their [CircleId].
+ final Map _circleIdToController;
+
+ // The stream over which circles broadcast their events
+ StreamController _streamController;
+
+ /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers.
+ CirclesController({
+ @required StreamController stream,
+ }) : _streamController = stream,
+ _circleIdToController = Map();
+
+ /// Returns the cache of [CircleController]s. Test only.
+ @visibleForTesting
+ Map get circles => _circleIdToController;
+
+ /// Adds a set of [Circle] objects to the cache.
+ ///
+ /// Wraps each [Circle] into its corresponding [CircleController].
+ void addCircles(Set circlesToAdd) {
+ circlesToAdd?.forEach((circle) {
+ _addCircle(circle);
+ });
+ }
+
+ void _addCircle(Circle circle) {
+ if (circle == null) {
+ return;
+ }
+
+ final populationOptions = _circleOptionsFromCircle(circle);
+ gmaps.Circle gmCircle = gmaps.Circle(populationOptions);
+ gmCircle.map = googleMap;
+ CircleController controller = CircleController(
+ circle: gmCircle,
+ consumeTapEvents: circle.consumeTapEvents,
+ onTap: () {
+ _onCircleTap(circle.circleId);
+ });
+ _circleIdToController[circle.circleId] = controller;
+ }
+
+ /// Updates a set of [Circle] objects with new options.
+ void changeCircles(Set circlesToChange) {
+ circlesToChange?.forEach((circleToChange) {
+ _changeCircle(circleToChange);
+ });
+ }
+
+ void _changeCircle(Circle circle) {
+ final circleController = _circleIdToController[circle?.circleId];
+ circleController?.update(_circleOptionsFromCircle(circle));
+ }
+
+ /// Removes a set of [CircleId]s from the cache.
+ void removeCircles(Set circleIdsToRemove) {
+ circleIdsToRemove?.forEach((circleId) {
+ final CircleController circleController = _circleIdToController[circleId];
+ circleController?.remove();
+ _circleIdToController.remove(circleId);
+ });
+ }
+
+ // Handles the global onCircleTap function to funnel events from circles into the stream.
+ bool _onCircleTap(CircleId circleId) {
+ // Have you ended here on your debugging? Is this wrong?
+ // Comment here: https://github.com/flutter/flutter/issues/64084
+ _streamController.add(CircleTapEvent(mapId, circleId));
+ return _circleIdToController[circleId]?.consumeTapEvents ?? false;
+ }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
new file mode 100644
index 000000000000..2eeaa0202995
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
@@ -0,0 +1,544 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of google_maps_flutter_web;
+
+final _nullLatLng = LatLng(0, 0);
+final _nullLatLngBounds = LatLngBounds(
+ northeast: _nullLatLng,
+ southwest: _nullLatLng,
+);
+
+// Defaults taken from the Google Maps Platform SDK documentation.
+final _defaultStrokeColor = Colors.black.value;
+final _defaultFillColor = Colors.transparent.value;
+
+// Indices in the plugin side don't match with the ones
+// in the gmaps lib. This translates from plugin -> gmaps.
+final _mapTypeToMapTypeId = {
+ 0: gmaps.MapTypeId.ROADMAP, // "none" in the plugin
+ 1: gmaps.MapTypeId.ROADMAP,
+ 2: gmaps.MapTypeId.SATELLITE,
+ 3: gmaps.MapTypeId.TERRAIN,
+ 4: gmaps.MapTypeId.HYBRID,
+};
+
+// Converts options from the plugin into gmaps.MapOptions that can be used by the JS SDK.
+// The following options are not handled here, for various reasons:
+// The following are not available in web, because the map doesn't rotate there:
+// compassEnabled
+// rotateGesturesEnabled
+// tiltGesturesEnabled
+// mapToolbarEnabled is unused in web, there's no "map toolbar"
+// myLocationButtonEnabled Widget not available in web yet, it needs to be built on top of the maps widget
+// See: https://developers.google.com/maps/documentation/javascript/examples/control-custom
+// myLocationEnabled needs to be built through dart:html navigator.geolocation
+// See: https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html
+// trafficEnabled is handled when creating the GMap object, since it needs to be added as a layer.
+// trackCameraPosition is just a boolan value that indicates if the map has an onCameraMove handler.
+// indoorViewEnabled seems to not have an equivalent in web
+// buildingsEnabled seems to not have an equivalent in web
+// padding seems to behave differently in web than mobile. You can't move UI elements in web.
+gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) {
+ Map optionsUpdate = rawOptions['options'] ?? {};
+
+ gmaps.MapOptions options = gmaps.MapOptions();
+
+ if (_mapTypeToMapTypeId.containsKey(optionsUpdate['mapType'])) {
+ options.mapTypeId = _mapTypeToMapTypeId[optionsUpdate['mapType']];
+ }
+
+ if (optionsUpdate['minMaxZoomPreference'] != null) {
+ options
+ ..minZoom = optionsUpdate['minMaxZoomPreference'][0]
+ ..maxZoom = optionsUpdate['minMaxZoomPreference'][1];
+ }
+
+ if (optionsUpdate['cameraTargetBounds'] != null) {
+ // Needs gmaps.MapOptions.restriction and gmaps.MapRestriction
+ // see: https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.restriction
+ }
+
+ if (optionsUpdate['zoomControlsEnabled'] != null) {
+ options.zoomControl = optionsUpdate['zoomControlsEnabled'];
+ }
+
+ if (optionsUpdate['styles'] != null) {
+ options.styles = optionsUpdate['styles'];
+ }
+
+ if (optionsUpdate['scrollGesturesEnabled'] == false ||
+ optionsUpdate['zoomGesturesEnabled'] == false) {
+ options.gestureHandling = 'none';
+ } else {
+ options.gestureHandling = 'auto';
+ }
+
+ // These don't have any optionUpdate entry, but they seem to be off in the native maps.
+ options.mapTypeControl = false;
+ options.fullscreenControl = false;
+ options.streetViewControl = false;
+
+ return options;
+}
+
+gmaps.MapOptions _applyInitialPosition(
+ Map rawOptions,
+ gmaps.MapOptions options,
+) {
+ // Adjust the initial position, if passed...
+ Map initialPosition = rawOptions['initialCameraPosition'];
+ if (initialPosition != null) {
+ final position = CameraPosition.fromMap(initialPosition);
+ options.zoom = position.zoom;
+ options.center =
+ gmaps.LatLng(position.target.latitude, position.target.longitude);
+ }
+ return options;
+}
+
+// Extracts the status of the traffic layer from the rawOptions map.
+bool _isTrafficLayerEnabled(Map rawOptions) {
+ if (rawOptions['options'] == null) {
+ return false;
+ }
+ return rawOptions['options']['trafficEnabled'] ?? false;
+}
+
+// Coverts the incoming JSON object into a List of MapTypeStyler objects.
+List _parseStylers(List stylerJsons) {
+ return stylerJsons?.map((styler) {
+ return gmaps.MapTypeStyler()
+ ..color = styler['color']
+ ..gamma = styler['gamma']
+ ..hue = styler['hue']
+ ..invertLightness = styler['invertLightness']
+ ..lightness = styler['lightness']
+ ..saturation = styler['saturation']
+ ..visibility = styler['visibility']
+ ..weight = styler['weight'];
+ })?.toList();
+}
+
+// Converts a String to its corresponding MapTypeStyleElementType enum value.
+final _elementTypeToEnum = {
+ 'all': gmaps.MapTypeStyleElementType.ALL,
+ 'geometry': gmaps.MapTypeStyleElementType.GEOMETRY,
+ 'geometry.fill': gmaps.MapTypeStyleElementType.GEOMETRY_FILL,
+ 'geometry.stroke': gmaps.MapTypeStyleElementType.GEOMETRY_STROKE,
+ 'labels': gmaps.MapTypeStyleElementType.LABELS,
+ 'labels.icon': gmaps.MapTypeStyleElementType.LABELS_ICON,
+ 'labels.text': gmaps.MapTypeStyleElementType.LABELS_TEXT,
+ 'labels.text.fill': gmaps.MapTypeStyleElementType.LABELS_TEXT_FILL,
+ 'labels.text.stroke': gmaps.MapTypeStyleElementType.LABELS_TEXT_STROKE,
+};
+
+// Converts a String to its corresponding MapTypeStyleFeatureType enum value.
+final _featureTypeToEnum = {
+ 'administrative': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE,
+ 'administrative.country':
+ gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY,
+ 'administrative.land_parcel':
+ gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL,
+ 'administrative.locality':
+ gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY,
+ 'administrative.neighborhood':
+ gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD,
+ 'administrative.province':
+ gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE,
+ 'all': gmaps.MapTypeStyleFeatureType.ALL,
+ 'landscape': gmaps.MapTypeStyleFeatureType.LANDSCAPE,
+ 'landscape.man_made': gmaps.MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE,
+ 'landscape.natural': gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL,
+ 'landscape.natural.landcover':
+ gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER,
+ 'landscape.natural.terrain':
+ gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN,
+ 'poi': gmaps.MapTypeStyleFeatureType.POI,
+ 'poi.attraction': gmaps.MapTypeStyleFeatureType.POI_ATTRACTION,
+ 'poi.business': gmaps.MapTypeStyleFeatureType.POI_BUSINESS,
+ 'poi.government': gmaps.MapTypeStyleFeatureType.POI_GOVERNMENT,
+ 'poi.medical': gmaps.MapTypeStyleFeatureType.POI_MEDICAL,
+ 'poi.park': gmaps.MapTypeStyleFeatureType.POI_PARK,
+ 'poi.place_of_worship': gmaps.MapTypeStyleFeatureType.POI_PLACE_OF_WORSHIP,
+ 'poi.school': gmaps.MapTypeStyleFeatureType.POI_SCHOOL,
+ 'poi.sports_complex': gmaps.MapTypeStyleFeatureType.POI_SPORTS_COMPLEX,
+ 'road': gmaps.MapTypeStyleFeatureType.ROAD,
+ 'road.arterial': gmaps.MapTypeStyleFeatureType.ROAD_ARTERIAL,
+ 'road.highway': gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY,
+ 'road.highway.controlled_access':
+ gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS,
+ 'road.local': gmaps.MapTypeStyleFeatureType.ROAD_LOCAL,
+ 'transit': gmaps.MapTypeStyleFeatureType.TRANSIT,
+ 'transit.line': gmaps.MapTypeStyleFeatureType.TRANSIT_LINE,
+ 'transit.station': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION,
+ 'transit.station.airport':
+ gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT,
+ 'transit.station.bus': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_BUS,
+ 'transit.station.rail': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_RAIL,
+ 'water': gmaps.MapTypeStyleFeatureType.WATER,
+};
+
+// The keys we'd expect to see in a serialized MapTypeStyle JSON object.
+final _mapStyleKeys = {
+ 'elementType',
+ 'featureType',
+ 'stylers',
+};
+
+// Checks if the passed in Map contains some of the _mapStyleKeys.
+bool _isJsonMapStyle(Map value) {
+ return _mapStyleKeys.intersection(value.keys.toSet()).isNotEmpty;
+}
+
+// Converts an incoming JSON-encoded Style info, into the correct gmaps array.
+List _mapStyles(String mapStyleJson) {
+ List styles = [];
+ if (mapStyleJson != null) {
+ styles = json.decode(mapStyleJson, reviver: (key, value) {
+ if (value is Map && _isJsonMapStyle(value)) {
+ return gmaps.MapTypeStyle()
+ ..elementType = _elementTypeToEnum[value['elementType']]
+ ..featureType = _featureTypeToEnum[value['featureType']]
+ ..stylers = _parseStylers(value['stylers']);
+ }
+ return value;
+ }).cast();
+ }
+ return styles;
+}
+
+gmaps.LatLng _latLngToGmLatLng(LatLng latLng) {
+ if (latLng == null) return null;
+ return gmaps.LatLng(latLng.latitude, latLng.longitude);
+}
+
+LatLng _gmLatLngToLatLng(gmaps.LatLng latLng) {
+ if (latLng == null) return _nullLatLng;
+ return LatLng(latLng.lat, latLng.lng);
+}
+
+LatLngBounds _gmLatLngBoundsTolatLngBounds(gmaps.LatLngBounds latLngBounds) {
+ if (latLngBounds == null) {
+ return _nullLatLngBounds;
+ }
+
+ return LatLngBounds(
+ southwest: _gmLatLngToLatLng(latLngBounds.southWest),
+ northeast: _gmLatLngToLatLng(latLngBounds.northEast),
+ );
+}
+
+CameraPosition _gmViewportToCameraPosition(gmaps.GMap map) {
+ return CameraPosition(
+ target: _gmLatLngToLatLng(map.center),
+ bearing: map.heading ?? 0,
+ tilt: map.tilt ?? 0,
+ zoom: map.zoom?.toDouble() ?? 10,
+ );
+}
+
+Set _rawOptionsToInitialMarkers(Map rawOptions) {
+ final List