[google_maps_flutter_web] Render custom Marker icons. (#3273)

* Render markers fromBytes. Ensure initial icon is also preserved. Add test.
* Opt-out tests from null-safety until plugin is migrated.
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 0770015..dcafa12 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.1.0+8
+
+* Update `package:google_maps_flutter_platform_interface` to `^1.0.5`.
+* Add support for `fromBitmap` BitmapDescriptors. [Issue](https://github.com/flutter/flutter/issues/66622).
+
 ## 0.1.0+7
 
 * Substitute `undefined_prefixed_name: ignore` analyzer setting by a `dart:ui` shim with conditional exports. [Issue](https://github.com/flutter/flutter/issues/69309).
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 e8847fd..551c157 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
@@ -264,6 +264,7 @@
         Offset offset;
         LatLng position;
         InfoWindow infoWindow;
+        BitmapDescriptor icon;
         if (rawMarker['anchor'] != null) {
           offset = Offset((rawMarker['anchor'][0]), (rawMarker['anchor'][1]));
         }
@@ -280,6 +281,9 @@
             );
           }
         }
+        if (rawMarker['icon'] != null) {
+          icon = BitmapDescriptor.fromJson(rawMarker['icon']);
+        }
         return Marker(
           markerId: MarkerId(rawMarker['markerId']),
           alpha: rawMarker['alpha'],
@@ -287,8 +291,7 @@
           consumeTapEvents: rawMarker['consumeTapEvents'],
           draggable: rawMarker['draggable'],
           flat: rawMarker['flat'],
-          // TODO: Doesn't this support custom icons?
-          icon: BitmapDescriptor.defaultMarker,
+          icon: icon,
           infoWindow: infoWindow,
           position: position ?? _nullLatLng,
           rotation: rawMarker['rotation'],
@@ -432,6 +435,11 @@
           ..size = size
           ..scaledSize = size;
       }
+    } else if (iconConfig[0] == 'fromBytes') {
+      // Grab the bytes, and put them into a blob
+      List<int> bytes = iconConfig[1];
+      final blob = Blob([bytes]); // Let the browser figure out the encoding
+      icon = gmaps.Icon()..url = Url.createObjectUrlFromBlob(blob);
     }
   }
   return gmaps.MarkerOptions()
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 bbbfcfa..2374153 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
@@ -1,7 +1,7 @@
 name: google_maps_flutter_web
 description: Web platform implementation of google_maps_flutter
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter
-version: 0.1.0+7
+version: 0.1.0+8
 
 flutter:
   plugin:
@@ -16,7 +16,7 @@
   flutter_web_plugins:
     sdk: flutter
   meta: ^1.1.7
-  google_maps_flutter_platform_interface: ^1.0.4
+  google_maps_flutter_platform_interface: ^1.0.5
   google_maps: ^3.4.5
   stream_transform: ^1.2.0
   sanitize_html: ^1.4.1
@@ -24,6 +24,7 @@
 dev_dependencies:
   flutter_test:
     sdk: flutter
+  http: ^0.12.2
   url_launcher: ^5.2.5
   pedantic: ^1.8.0
   mockito: ^4.1.1
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart
index dfd01c6..70d4452 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.9
+
 import 'dart:async';
 
 import 'package:integration_test/integration_test.dart';
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_integration.dart
index 59b5de4..6276d26 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_integration.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.9
+
 import 'dart:async';
 
 import 'package:integration_test/integration_test.dart';
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_integration.dart
index 1cada32..c83e92d 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_integration.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.9
+
 import 'dart:async';
 
 import 'package:integration_test/integration_test.dart';
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart
index cad8cd8..9c3ed8e 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart
@@ -2,14 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.9
+
 import 'dart:async';
+import 'dart:convert';
 import 'dart:html';
 
+import 'package:http/http.dart' as http;
 import 'package:integration_test/integration_test.dart';
 import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
 import 'package:google_maps_flutter_web/google_maps_flutter_web.dart';
 import 'package:flutter_test/flutter_test.dart';
 
+import 'resources/icon_image_base64.dart';
+
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
@@ -141,6 +147,30 @@
       expect(controller.markers[MarkerId('1')].marker.icon, isNull);
     });
 
+    //
+    testWidgets('markers with custom bitmap icon work',
+        (WidgetTester tester) async {
+      final bytes = Base64Decoder().convert(iconImageBase64);
+      final markers = {
+        Marker(
+            markerId: MarkerId('1'), icon: BitmapDescriptor.fromBytes(bytes)),
+      };
+
+      controller.addMarkers(markers);
+
+      expect(controller.markers.length, 1);
+      expect(controller.markers[MarkerId('1')].marker.icon, isNotNull);
+      expect(controller.markers[MarkerId('1')].marker.icon.url,
+          startsWith('blob:'));
+
+      final blobUrl = controller.markers[MarkerId('1')].marker.icon.url;
+      final response = await http.get(blobUrl);
+
+      expect(response.bodyBytes, bytes,
+          reason:
+              'Bytes from the Icon blob must match bytes used to create Marker');
+    });
+
     // https://github.com/flutter/flutter/issues/67854
     testWidgets('InfoWindow snippet can have links',
         (WidgetTester tester) async {
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/resources/icon_image_base64.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/resources/icon_image_base64.dart
new file mode 100644
index 0000000..aa4a80b
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/resources/icon_image_base64.dart
@@ -0,0 +1,24 @@
+final iconImageBase64 =
+    'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAIRlWElmTU'
+    '0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIA'
+    'AIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQ'
+    'AAABCgAwAEAAAAAQAAABAAAAAAx28c8QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1M'
+    'OmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIH'
+    'g6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8v'
+    'd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcm'
+    'lwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFk'
+    'b2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk'
+    '9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6'
+    'eG1wbWV0YT4KTMInWQAAAplJREFUOBF1k01ME1EQx2fe7tIPoGgTE6AJgQQSPaiH9oAtkFbsgX'
+    'jygFcT0XjSkxcTDxtPJh6MR28ePMHBBA8cNLSIony0oBhEMVETP058tE132+7uG3cW24DAXN57'
+    '2fn9/zPz3iIcEdEl0nIxtNLr1IlVeoMadkubKmoL+u2SzAV8IjV5Ekt4GN+A8+VOUPwLarOI2G'
+    'Vpqq0i4JQorwQxPtWHVZ1IKP8LNGDXGaSyqARFxDGo7MJBy4XVf3AyQ+qTHnTEXoF9cFUy3OkY'
+    '0oWxmWFtD5xNoc1sQ6AOn1+hCNTkkhKow8KFZV77tVs2O9dhFvBm0IA/U0RhZ7/ocEx23oUDlh'
+    'h8HkNjZIN8Lb3gOU8gOp7AKJHCB2/aNZkTftHumNzzbtl2CBPZHqxw8mHhVZBeoz6w5DvhE2FZ'
+    'lQYPjKdd2/qRyKZ6KsPv7TEk7EYEk0A0EUmJduHRy1i4oLKqgmC59ZggAdwrC9pFuWy1iUT2rA'
+    'uv0h2UdNtNqxCBBkgqorjOMOgksN7CxQ90vEb00U3c3LIwyo9o8FXxQVNr8Coqyk+S5EPBXnjt'
+    'xRmc4TegI7qWbvBkeeUbGMnTCd4nZnYeDOWIEtlC6cKK/JJepY3hZSvN33jovO6L0XFqPKqBTO'
+    'FuapUoPr1lxDM7cmC2TAOz25cYSGa++feBew/cjpc0V+mNT29/HZp3KDFTNLvuTRPEHy5065lj'
+    'Xn4y41XM+wP/AlcycRmdc3MUhvLm/J/ceu/3qUVT62oP2EZpjSylHybHSpDUVcjq9gEBVo0+Xt'
+    'JyN2IWRO+3QUforRoKnZLVsglaMECW+YmMSj9M3SrC6Lg71CMiqWfUrJ6ywzefhnZ+G69BaKdB'
+    'WhXQAn6wzDUpfUPw7MrmX/WhbfmKblw+AAAAAElFTkSuQmCC';
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_integration.dart
index a05d704..b1a691d 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_integration.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.9
+
 import 'dart:async';
 
 import 'package:integration_test/integration_test.dart';
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_integration.dart
index 4345843..0c92c6a 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_integration.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.9
+
 import 'dart:async';
 import 'dart:ui';