[google_maps_flutter + platform] Tweaks to enable a web implementation. (#2903)

This change tweaks slightly the core maps plugin and the platform interface package to make the web implementation possible.

The most important changes are two:

* The core plugin now passes a constant identifier to the buildView platform call, so the web version can cache effectively the contents of the platform view, so it doesn't repaint. This might go away once Scenelets for web come online.
* The platform interface now encodes the (optional) width and height of custom Icons for Markers, so the web can render High DPI assets at the correct size.

The rest are some examples to the 'example' app so it can be run on web.
diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
index 8ad0c02..5d62a51 100644
--- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.5.29
+
+* Pass a constant `_web_only_mapCreationId` to `platform.buildView`, so web can return a cached widget DOM when flutter attempts to repaint there.
+* Modify some examples slightly so they're more web-friendly.
+
 ## 0.5.28+2
 
 * Move test introduced in #2449 to its right location.
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart
index e0fcc42..b62d898 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart
+++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart
@@ -73,7 +73,7 @@
   Future<void> _createMarkerImageFromAsset(BuildContext context) async {
     if (_markerIcon == null) {
       final ImageConfiguration imageConfiguration =
-          createLocalImageConfiguration(context);
+          createLocalImageConfiguration(context, size: Size.square(48));
       BitmapDescriptor.fromAssetImage(
               imageConfiguration, 'assets/red_square.png')
           .then(_updateBitmap);
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart
index 0c9da63..35ffd33 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart
+++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart
@@ -4,8 +4,7 @@
 
 // ignore_for_file: public_member_api_docs
 
-import 'dart:io' show Platform;
-
+import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:google_maps_flutter/google_maps_flutter.dart';
 
@@ -202,7 +201,9 @@
 
   @override
   Widget build(BuildContext context) {
-    final bool iOSorNotSelected = Platform.isIOS || (selectedPolyline == null);
+    final bool iOSorNotSelected =
+        (!kIsWeb && defaultTargetPlatform == TargetPlatform.iOS) ||
+            (selectedPolyline == null);
 
     return Column(
       mainAxisAlignment: MainAxisAlignment.spaceEvenly,
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 a45a1c8..5cf3db1 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
@@ -10,6 +10,12 @@
 /// map is created.
 typedef void MapCreatedCallback(GoogleMapController controller);
 
+// This counter is used to provide a stable "constant" initialization id
+// to the buildView function, so the web implementation can use it as a
+// cache key. This needs to be provided from the outside, because web
+// views seem to re-render much more often that mobile platform views.
+int _webOnlyMapId = 0;
+
 /// A widget which displays a map with data obtained from the Google Maps service.
 class GoogleMap extends StatefulWidget {
   /// Creates a widget displaying data from Google Maps services.
@@ -205,6 +211,8 @@
 }
 
 class _GoogleMapState extends State<GoogleMap> {
+  final _webOnlyMapCreationId = _webOnlyMapId++;
+
   final Completer<GoogleMapController> _controller =
       Completer<GoogleMapController>();
 
@@ -223,7 +231,9 @@
       'polygonsToAdd': serializePolygonSet(widget.polygons),
       'polylinesToAdd': serializePolylineSet(widget.polylines),
       'circlesToAdd': serializeCircleSet(widget.circles),
+      '_webOnlyMapCreationId': _webOnlyMapCreationId,
     };
+
     return _googleMapsFlutterPlatform.buildView(
       creationParams,
       widget.gestureRecognizers,
diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
index 5e3cf22..120600a 100644
--- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
@@ -1,7 +1,7 @@
 name: google_maps_flutter
 description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter
-version: 0.5.28+2
+version: 0.5.29
 
 dependencies:
   flutter:
@@ -19,6 +19,8 @@
     sdk: flutter
   test: ^1.6.0
   pedantic: ^1.8.0
+  plugin_platform_interface: ^1.0.2
+  mockito: ^4.1.1
 
 flutter:
   plugin:
diff --git a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart
new file mode 100644
index 0000000..7861c86
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart
@@ -0,0 +1,64 @@
+// Copyright 2018 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.
+
+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 'package:plugin_platform_interface/plugin_platform_interface.dart';
+import 'package:mockito/mockito.dart';
+
+class MockGoogleMapsFlutterPlatform extends Mock
+    with MockPlatformInterfaceMixin
+    implements GoogleMapsFlutterPlatform {}
+
+void main() {
+  TestWidgetsFlutterBinding.ensureInitialized();
+  final platform = MockGoogleMapsFlutterPlatform();
+
+  setUp(() {
+    // Use a mock platform so we never need to hit the MethodChannel code.
+    GoogleMapsFlutterPlatform.instance = platform;
+    when(platform.buildView(any, any, any)).thenReturn(Container());
+  });
+
+  testWidgets('_webOnlyMapCreationId increments with each GoogleMap widget', (
+    WidgetTester tester,
+  ) async {
+    // Inject two map widgets...
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: Column(
+          children: const [
+            GoogleMap(
+              initialCameraPosition: CameraPosition(
+                target: LatLng(43.362, -5.849),
+              ),
+            ),
+            GoogleMap(
+              initialCameraPosition: CameraPosition(
+                target: LatLng(47.649, -122.350),
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+
+    // Verify that each one was created with a different _webOnlyMapCreationId.
+    verifyInOrder([
+      platform.buildView(
+        argThat(containsPair('_webOnlyMapCreationId', 0)),
+        any,
+        any,
+      ),
+      platform.buildView(
+        argThat(containsPair('_webOnlyMapCreationId', 1)),
+        any,
+        any,
+      ),
+    ]);
+  });
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md
index eca5c91..47cb8ec 100644
--- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.0.3
+
+* Pass icon width/height if present on `fromAssetImage` BitmapDescriptors (web only)
+
 ## 1.0.2
 
 * Update lower bound of dart dependency to 2.1.0.
diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart
index 40581b4..a6fdcc1 100644
--- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart
@@ -9,6 +9,8 @@
     show ImageConfiguration, AssetImage, AssetBundleImageKey;
 import 'package:flutter/services.dart' show AssetBundle;
 
+import 'package:flutter/foundation.dart' show kIsWeb;
+
 /// Defines a bitmap image. For a marker, this class can be used to set the
 /// image of the marker icon. For a ground overlay, it can be used to set the
 /// image to place on the surface of the earth.
@@ -100,6 +102,11 @@
       'fromAssetImage',
       assetBundleImageKey.name,
       assetBundleImageKey.scale,
+      if (kIsWeb && configuration?.size != null)
+        [
+          configuration.size.width,
+          configuration.size.height,
+        ],
     ]);
   }
 
diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml
index b28b7f4..f062b25 100644
--- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml
@@ -3,7 +3,7 @@
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_platform_interface
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.0.2
+version: 1.0.3
 
 dependencies:
   flutter:
@@ -19,5 +19,5 @@
   pedantic: ^1.8.0
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: ">=2.3.0 <3.0.0"
   flutter: ">=1.9.1+hotfix.4 <2.0.0"