[google_maps_flutter] Switch web to structured options (#5965)

diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
index 95dfaed..0f040ec 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
@@ -1,5 +1,10 @@
-## NEXT
+## 0.4.0
 
+* Implements the new platform interface versions of `buildView` and
+  `updateOptions` with structured option types.
+* **BREAKING CHANGE**: No longer implements the unstructured option dictionary
+  versions of those methods, so this version can only be used with
+  `google_maps_flutter` 2.1.8 or later.
 * Adds `const` constructor parameters in example tests.
 
 ## 0.3.3
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart
index ef9136a..dd2520d 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart
@@ -40,21 +40,17 @@
     GoogleMapController _createController({
       CameraPosition initialCameraPosition =
           const CameraPosition(target: LatLng(0, 0)),
-      Set<Marker> markers = const <Marker>{},
-      Set<Polygon> polygons = const <Polygon>{},
-      Set<Polyline> polylines = const <Polyline>{},
-      Set<Circle> circles = const <Circle>{},
-      Map<String, dynamic> options = const <String, dynamic>{},
+      MapObjects mapObjects = const MapObjects(),
+      MapConfiguration mapConfiguration = const MapConfiguration(),
     }) {
       return GoogleMapController(
         mapId: mapId,
         streamController: stream,
-        initialCameraPosition: initialCameraPosition,
-        markers: markers,
-        polygons: polygons,
-        polylines: polylines,
-        circles: circles,
-        mapOptions: options,
+        widgetConfiguration: MapWidgetConfiguration(
+            initialCameraPosition: initialCameraPosition,
+            textDirection: TextDirection.ltr),
+        mapObjects: mapObjects,
+        mapConfiguration: mapConfiguration,
       );
     }
 
@@ -284,7 +280,8 @@
       });
 
       testWidgets('renders initial geometry', (WidgetTester tester) async {
-        controller = _createController(circles: <Circle>{
+        controller = _createController(
+            mapObjects: MapObjects(circles: <Circle>{
           const Circle(
             circleId: CircleId('circle-1'),
             zIndex: 1234,
@@ -327,7 +324,7 @@
             LatLng(43.354469, -5.851318),
             LatLng(43.354762, -5.850824),
           ])
-        });
+        }));
 
         controller.debugSetOverrides(
           circles: circles,
@@ -363,9 +360,10 @@
 
       testWidgets('empty infoWindow does not create InfoWindow instance.',
           (WidgetTester tester) async {
-        controller = _createController(markers: <Marker>{
+        controller = _createController(
+            mapObjects: MapObjects(markers: <Marker>{
           const Marker(markerId: MarkerId('marker-1')),
-        });
+        }));
 
         controller.debugSetOverrides(
           markers: markers,
@@ -385,10 +383,11 @@
           capturedOptions = null;
         });
         testWidgets('translates initial options', (WidgetTester tester) async {
-          controller = _createController(options: <String, dynamic>{
-            'mapType': 2,
-            'zoomControlsEnabled': true,
-          });
+          controller = _createController(
+              mapConfiguration: const MapConfiguration(
+            mapType: MapType.satellite,
+            zoomControlsEnabled: true,
+          ));
           controller.debugSetOverrides(
               createMap: (_, gmaps.MapOptions options) {
             capturedOptions = options;
@@ -407,9 +406,10 @@
 
         testWidgets('disables gestureHandling with scrollGesturesEnabled false',
             (WidgetTester tester) async {
-          controller = _createController(options: <String, dynamic>{
-            'scrollGesturesEnabled': false,
-          });
+          controller = _createController(
+              mapConfiguration: const MapConfiguration(
+            scrollGesturesEnabled: false,
+          ));
           controller.debugSetOverrides(
               createMap: (_, gmaps.MapOptions options) {
             capturedOptions = options;
@@ -426,9 +426,10 @@
 
         testWidgets('disables gestureHandling with zoomGesturesEnabled false',
             (WidgetTester tester) async {
-          controller = _createController(options: <String, dynamic>{
-            'zoomGesturesEnabled': false,
-          });
+          controller = _createController(
+              mapConfiguration: const MapConfiguration(
+            zoomGesturesEnabled: false,
+          ));
           controller.debugSetOverrides(
               createMap: (_, gmaps.MapOptions options) {
             capturedOptions = options;
@@ -477,9 +478,10 @@
 
         testWidgets('initializes with traffic layer',
             (WidgetTester tester) async {
-          controller = _createController(options: <String, dynamic>{
-            'trafficEnabled': true,
-          });
+          controller = _createController(
+              mapConfiguration: const MapConfiguration(
+            trafficEnabled: true,
+          ));
           controller.debugSetOverrides(createMap: (_, __) => map);
           controller.init();
           expect(controller.trafficLayer, isNotNull);
@@ -505,9 +507,9 @@
 
       group('updateRawOptions', () {
         testWidgets('can update `options`', (WidgetTester tester) async {
-          controller.updateRawOptions(<String, dynamic>{
-            'mapType': 2,
-          });
+          controller.updateMapConfiguration(const MapConfiguration(
+            mapType: MapType.satellite,
+          ));
 
           expect(map.mapTypeId, gmaps.MapTypeId.SATELLITE);
         });
@@ -515,15 +517,15 @@
         testWidgets('can turn on/off traffic', (WidgetTester tester) async {
           expect(controller.trafficLayer, isNull);
 
-          controller.updateRawOptions(<String, dynamic>{
-            'trafficEnabled': true,
-          });
+          controller.updateMapConfiguration(const MapConfiguration(
+            trafficEnabled: true,
+          ));
 
           expect(controller.trafficLayer, isNotNull);
 
-          controller.updateRawOptions(<String, dynamic>{
-            'trafficEnabled': false,
-          });
+          controller.updateMapConfiguration(const MapConfiguration(
+            trafficEnabled: false,
+          ));
 
           expect(controller.trafficLayer, isNull);
         });
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart
index f28a604..e66a3f4 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart
@@ -79,10 +79,13 @@
             <int, GoogleMapController>{};
         plugin.debugSetMapById(cache);
 
-        final Widget widget = plugin.buildView(
+        final Widget widget = plugin.buildViewWithConfiguration(
           testMapId,
           onPlatformViewCreated,
-          initialCameraPosition: initialCameraPosition,
+          widgetConfiguration: const MapWidgetConfiguration(
+            initialCameraPosition: initialCameraPosition,
+            textDirection: TextDirection.ltr,
+          ),
         );
 
         expect(widget, isA<HtmlElementView>());
@@ -114,10 +117,13 @@
           testMapId: controller,
         });
 
-        final Widget widget = plugin.buildView(
+        final Widget widget = plugin.buildViewWithConfiguration(
           testMapId,
           onPlatformViewCreated,
-          initialCameraPosition: initialCameraPosition,
+          widgetConfiguration: const MapWidgetConfiguration(
+            initialCameraPosition: initialCameraPosition,
+            textDirection: TextDirection.ltr,
+          ),
         );
 
         expect(widget, equals(expected));
@@ -130,10 +136,13 @@
             <int, GoogleMapController>{};
         plugin.debugSetMapById(cache);
 
-        plugin.buildView(
+        plugin.buildViewWithConfiguration(
           testMapId,
           onPlatformViewCreated,
-          initialCameraPosition: initialCameraPosition,
+          widgetConfiguration: const MapWidgetConfiguration(
+            initialCameraPosition: initialCameraPosition,
+            textDirection: TextDirection.ltr,
+          ),
         );
 
         // Simulate Google Maps JS SDK being "ready"
@@ -176,11 +185,10 @@
         await plugin.setMapStyle(mapStyle, mapId: 0);
 
         final dynamic captured =
-            verify(controller.updateRawOptions(captureThat(isMap))).captured[0];
+            verify(controller.updateStyles(captureThat(isList))).captured[0];
 
-        expect(captured, contains('styles'));
         final List<gmaps.MapTypeStyle> styles =
-            captured['styles'] as List<gmaps.MapTypeStyle>;
+            captured as List<gmaps.MapTypeStyle>;
         expect(styles.length, 1);
         // Let's peek inside the styles...
         final gmaps.MapTypeStyle style = styles[0];
@@ -221,14 +229,13 @@
         plugin.debugSetMapById(<int, GoogleMapController>{mapId: controller});
       });
       // Options
-      testWidgets('updateMapOptions', (WidgetTester tester) async {
-        final Map<String, dynamic> expectedMapOptions = <String, dynamic>{
-          'someOption': 12345
-        };
+      testWidgets('updateMapConfiguration', (WidgetTester tester) async {
+        const MapConfiguration configuration =
+            MapConfiguration(mapType: MapType.satellite);
 
-        await plugin.updateMapOptions(expectedMapOptions, mapId: mapId);
+        await plugin.updateMapConfiguration(configuration, mapId: mapId);
 
-        verify(controller.updateRawOptions(expectedMapOptions));
+        verify(controller.updateMapConfiguration(configuration));
       });
       // Geometry
       testWidgets('updateMarkers', (WidgetTester tester) async {
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart
index bbc92ff..744552f 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async' as _i2;
 
+import 'package:google_maps/google_maps.dart' as _i5;
 import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'
     as _i3;
 import 'package:google_maps_flutter_web/google_maps_flutter_web.dart' as _i4;
@@ -68,8 +69,12 @@
   void init() => super.noSuchMethod(Invocation.method(#init, []),
       returnValueForMissingStub: null);
   @override
-  void updateRawOptions(Map<String, dynamic>? optionsUpdate) =>
-      super.noSuchMethod(Invocation.method(#updateRawOptions, [optionsUpdate]),
+  void updateMapConfiguration(_i3.MapConfiguration? update) =>
+      super.noSuchMethod(Invocation.method(#updateMapConfiguration, [update]),
+          returnValueForMissingStub: null);
+  @override
+  void updateStyles(List<_i5.MapTypeStyle>? styles) =>
+      super.noSuchMethod(Invocation.method(#updateStyles, [styles]),
           returnValueForMissingStub: null);
   @override
   _i2.Future<_i3.LatLngBounds> getVisibleRegion() => (super.noSuchMethod(
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart
index 7ae6466..0650184 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart
@@ -10,7 +10,6 @@
 import 'dart:js_util';
 
 import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
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
index c6f3164..250bb54 100644
--- 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
@@ -13,16 +13,6 @@
 const String _defaultCssColor = '#000000';
 const double _defaultCssOpacity = 0.0;
 
-// Indices in the plugin side don't match with the ones
-// in the gmaps lib. This translates from plugin -> gmaps.
-final Map<int, gmaps.MapTypeId> _mapTypeToMapTypeId = <int, gmaps.MapTypeId>{
-  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 a [Color] into a valid CSS value #RRGGBB.
 String _getCssColor(Color color) {
   if (color == null) {
@@ -55,49 +45,64 @@
 // 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<String, Object?> rawOptions) {
+gmaps.MapOptions _configurationAndStyleToGmapsOptions(
+    MapConfiguration configuration, List<gmaps.MapTypeStyle> styles) {
   final gmaps.MapOptions options = gmaps.MapOptions();
 
-  if (_mapTypeToMapTypeId.containsKey(rawOptions['mapType'])) {
-    options.mapTypeId = _mapTypeToMapTypeId[rawOptions['mapType']];
+  if (configuration.mapType != null) {
+    options.mapTypeId = _gmapTypeIDForPluginType(configuration.mapType!);
   }
 
-  if (rawOptions['minMaxZoomPreference'] != null) {
-    final List<Object?> minMaxPreference =
-        rawOptions['minMaxZoomPreference']! as List<Object?>;
+  final MinMaxZoomPreference? zoomPreference =
+      configuration.minMaxZoomPreference;
+  if (zoomPreference != null) {
     options
-      ..minZoom = minMaxPreference[0] as num?
-      ..maxZoom = minMaxPreference[1] as num?;
+      ..minZoom = zoomPreference.minZoom
+      ..maxZoom = zoomPreference.maxZoom;
   }
 
-  if (rawOptions['cameraTargetBounds'] != null) {
+  if (configuration.cameraTargetBounds != null) {
     // Needs gmaps.MapOptions.restriction and gmaps.MapRestriction
     // see: https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.restriction
   }
 
-  if (rawOptions['zoomControlsEnabled'] != null) {
-    options.zoomControl = rawOptions['zoomControlsEnabled'] as bool?;
+  if (configuration.zoomControlsEnabled != null) {
+    options.zoomControl = configuration.zoomControlsEnabled;
   }
 
-  if (rawOptions['styles'] != null) {
-    options.styles = rawOptions['styles'] as List<gmaps.MapTypeStyle?>?;
-  }
-
-  if (rawOptions['scrollGesturesEnabled'] == false ||
-      rawOptions['zoomGesturesEnabled'] == false) {
+  if (configuration.scrollGesturesEnabled == false ||
+      configuration.zoomGesturesEnabled == false) {
     options.gestureHandling = 'none';
   } else {
     options.gestureHandling = 'auto';
   }
 
-  // These don't have any rawOptions entry, but they seem to be off in the native maps.
+  // These don't have any configuration entries, but they seem to be off in the
+  // native maps.
   options.mapTypeControl = false;
   options.fullscreenControl = false;
   options.streetViewControl = false;
 
+  options.styles = styles;
+
   return options;
 }
 
+gmaps.MapTypeId _gmapTypeIDForPluginType(MapType type) {
+  switch (type) {
+    case MapType.satellite:
+      return gmaps.MapTypeId.SATELLITE;
+    case MapType.terrain:
+      return gmaps.MapTypeId.TERRAIN;
+    case MapType.hybrid:
+      return gmaps.MapTypeId.HYBRID;
+    case MapType.normal:
+    case MapType.none:
+    default:
+      return gmaps.MapTypeId.ROADMAP;
+  }
+}
+
 gmaps.MapOptions _applyInitialPosition(
   CameraPosition initialPosition,
   gmaps.MapOptions options,
@@ -111,11 +116,6 @@
   return options;
 }
 
-// Extracts the status of the traffic layer from the rawOptions map.
-bool _isTrafficLayerEnabled(Map<String, Object?> rawOptions) {
-  return rawOptions['trafficEnabled'] as bool? ?? false;
-}
-
 // The keys we'd expect to see in a serialized MapTypeStyle JSON object.
 final Set<String> _mapStyleKeys = <String>{
   'elementType',
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart
index b7e9020..a659fb2 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart
@@ -15,20 +15,17 @@
   GoogleMapController({
     required int mapId,
     required StreamController<MapEvent<Object?>> streamController,
-    required CameraPosition initialCameraPosition,
-    Set<Marker> markers = const <Marker>{},
-    Set<Polygon> polygons = const <Polygon>{},
-    Set<Polyline> polylines = const <Polyline>{},
-    Set<Circle> circles = const <Circle>{},
-    Map<String, dynamic> mapOptions = const <String, dynamic>{},
+    required MapWidgetConfiguration widgetConfiguration,
+    MapObjects mapObjects = const MapObjects(),
+    MapConfiguration mapConfiguration = const MapConfiguration(),
   })  : _mapId = mapId,
         _streamController = streamController,
-        _initialCameraPosition = initialCameraPosition,
-        _markers = markers,
-        _polygons = polygons,
-        _polylines = polylines,
-        _circles = circles,
-        _rawMapOptions = mapOptions {
+        _initialCameraPosition = widgetConfiguration.initialCameraPosition,
+        _markers = mapObjects.markers,
+        _polygons = mapObjects.polygons,
+        _polylines = mapObjects.polylines,
+        _circles = mapObjects.circles,
+        _lastMapConfiguration = mapConfiguration {
     _circlesController = CirclesController(stream: _streamController);
     _polygonsController = PolygonsController(stream: _streamController);
     _polylinesController = PolylinesController(stream: _streamController);
@@ -56,9 +53,10 @@
   final Set<Polygon> _polygons;
   final Set<Polyline> _polylines;
   final Set<Circle> _circles;
-  // The raw options passed by the user, before converting to gmaps.
+  // The configuraiton passed by the user, before converting to gmaps.
   // Caching this allows us to re-create the map faithfully when needed.
-  Map<String, dynamic> _rawMapOptions = <String, dynamic>{};
+  MapConfiguration _lastMapConfiguration = const MapConfiguration();
+  List<gmaps.MapTypeStyle> _lastStyles = const <gmaps.MapTypeStyle>[];
 
   // Creates the 'viewType' for the _widget
   String _getViewType(int mapId) => 'plugins.flutter.io/google_maps_$mapId';
@@ -158,7 +156,8 @@
   /// Failure to call this method would result in the GMap not rendering at all,
   /// and most of the public methods on this class no-op'ing.
   void init() {
-    gmaps.MapOptions options = _rawOptionsToGmapsOptions(_rawMapOptions);
+    gmaps.MapOptions options = _configurationAndStyleToGmapsOptions(
+        _lastMapConfiguration, _lastStyles);
     // Initial position can only to be set here!
     options = _applyInitialPosition(_initialCameraPosition, options);
 
@@ -177,7 +176,7 @@
       polylines: _polylines,
     );
 
-    _setTrafficLayer(map, _isTrafficLayerEnabled(_rawMapOptions));
+    _setTrafficLayer(map, _lastMapConfiguration.trafficEnabled ?? false);
   }
 
   // Funnels map gmap events into the plugin's stream controller.
@@ -260,27 +259,33 @@
     _polylinesController!.addPolylines(polylines);
   }
 
-  // Merges new options coming from the plugin into the _rawMapOptions map.
+  // Merges new options coming from the plugin into _lastConfiguration.
   //
-  // Returns the updated _rawMapOptions object.
-  Map<String, dynamic> _mergeRawOptions(Map<String, dynamic> newOptions) {
-    _rawMapOptions = <String, dynamic>{
-      ..._rawMapOptions,
-      ...newOptions,
-    };
-    return _rawMapOptions;
+  // Returns the updated _lastConfiguration object.
+  MapConfiguration _mergeConfigurations(MapConfiguration update) {
+    _lastMapConfiguration = _lastMapConfiguration.applyDiff(update);
+    return _lastMapConfiguration;
   }
 
-  /// Updates the map options from a `Map<String, dynamic>`.
+  /// Updates the map options from a [MapConfiguration].
   ///
-  /// This method converts the map into the proper [gmaps.MapOptions]
-  void updateRawOptions(Map<String, dynamic> optionsUpdate) {
+  /// This method converts the map into the proper [gmaps.MapOptions].
+  void updateMapConfiguration(MapConfiguration update) {
     assert(_googleMap != null, 'Cannot update options on a null map.');
 
-    final Map<String, dynamic> newOptions = _mergeRawOptions(optionsUpdate);
+    final MapConfiguration newConfiguration = _mergeConfigurations(update);
+    final gmaps.MapOptions newOptions =
+        _configurationAndStyleToGmapsOptions(newConfiguration, _lastStyles);
 
-    _setOptions(_rawOptionsToGmapsOptions(newOptions));
-    _setTrafficLayer(_googleMap!, _isTrafficLayerEnabled(newOptions));
+    _setOptions(newOptions);
+    _setTrafficLayer(_googleMap!, newConfiguration.trafficEnabled ?? false);
+  }
+
+  /// Updates the map options with a new list of [styles].
+  void updateStyles(List<gmaps.MapTypeStyle> styles) {
+    _lastStyles = styles;
+    _setOptions(
+        _configurationAndStyleToGmapsOptions(_lastMapConfiguration, styles));
   }
 
   // Sets new [gmaps.MapOptions] on the wrapped map.
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart
index 043952d..c2085a2 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart
@@ -47,11 +47,11 @@
   /// This attempts to merge the new `optionsUpdate` passed in, with the previous
   /// options passed to the map (in other updates, or when creating it).
   @override
-  Future<void> updateMapOptions(
-    Map<String, dynamic> optionsUpdate, {
+  Future<void> updateMapConfiguration(
+    MapConfiguration update, {
     required int mapId,
   }) async {
-    _map(mapId).updateRawOptions(optionsUpdate);
+    _map(mapId).updateMapConfiguration(update);
   }
 
   /// Applies the passed in `markerUpdates` to the `mapId`.
@@ -135,9 +135,7 @@
     String? mapStyle, {
     required int mapId,
   }) async {
-    _map(mapId).updateRawOptions(<String, dynamic>{
-      'styles': _mapStyles(mapStyle),
-    });
+    _map(mapId).updateStyles(_mapStyles(mapStyle));
   }
 
   /// Returns the bounds of the current viewport.
@@ -289,18 +287,12 @@
   }
 
   @override
-  Widget buildView(
+  Widget buildViewWithConfiguration(
     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 =
-        const <Factory<OneSequenceGestureRecognizer>>{},
-    Map<String, dynamic> mapOptions = const <String, dynamic>{},
+    required MapWidgetConfiguration widgetConfiguration,
+    MapObjects mapObjects = const MapObjects(),
+    MapConfiguration mapConfiguration = const MapConfiguration(),
   }) {
     // Bail fast if we've already rendered this map ID...
     if (_mapById[creationId]?.widget != null) {
@@ -311,14 +303,11 @@
         StreamController<MapEvent<Object?>>.broadcast();
 
     final GoogleMapController mapController = GoogleMapController(
-      initialCameraPosition: initialCameraPosition,
       mapId: creationId,
       streamController: controller,
-      markers: markers,
-      polygons: polygons,
-      polylines: polylines,
-      circles: circles,
-      mapOptions: mapOptions,
+      widgetConfiguration: widgetConfiguration,
+      mapObjects: mapObjects,
+      mapConfiguration: mapConfiguration,
     )..init(); // Initialize the controller
 
     _mapById[creationId] = mapController;
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
index b46f756..a05be66 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Web platform implementation of google_maps_flutter
 repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_web
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
-version: 0.3.3
+version: 0.4.0
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
@@ -22,7 +22,7 @@
   flutter_web_plugins:
     sdk: flutter
   google_maps: ^6.1.0
-  google_maps_flutter_platform_interface: ^2.1.2
+  google_maps_flutter_platform_interface: ^2.2.0
   sanitize_html: ^2.0.0
   stream_transform: ^2.0.0