[google_maps_flutter_web] Fix convert.dart issues (#3093)

* Convert initial Polyline/Polygon points correctly.
* Convert Flutter Color to CSS correctly.

Fixes https://github.com/flutter/flutter/issues/65152
Fixes https://github.com/flutter/flutter/issues/67032
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 0121042..42805c3 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+3
+
+* Fix crash when converting initial polylines and polygons. [Issue](https://github.com/flutter/flutter/issues/65152).
+* Correctly convert Colors when rendering polylines, polygons and circles. [Issue](https://github.com/flutter/flutter/issues/67032).
+
 ## 0.1.0+2
 
 * Fix crash when converting Markers with icon explicitly set to null. [Issue](https://github.com/flutter/flutter/issues/64938).
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 0ec0087..3e1b1f1 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,6 +13,8 @@
 // Defaults taken from the Google Maps Platform SDK documentation.
 final _defaultStrokeColor = Colors.black.value;
 final _defaultFillColor = Colors.transparent.value;
+final _defaultCssColor = '#000000';
+final _defaultCssOpacity = 0.0;
 
 // Indices in the plugin side don't match with the ones
 // in the gmaps lib. This translates from plugin -> gmaps.
@@ -24,6 +26,22 @@
   4: gmaps.MapTypeId.HYBRID,
 };
 
+// Converts a [Color] into a valid CSS value #RRGGBB.
+String _getCssColor(Color color) {
+  if (color == null) {
+    return _defaultCssColor;
+  }
+  return '#' + color.value.toRadixString(16).padLeft(8, '0').substring(2);
+}
+
+// Extracts the opacity from a [Color].
+double _getCssOpacity(Color color) {
+  if (color == null) {
+    return _defaultCssOpacity;
+  }
+  return color.opacity;
+}
+
 // 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:
@@ -319,7 +337,7 @@
           zIndex: rawPolyline['zIndex'],
           width: rawPolyline['width'],
           points: rawPolyline['points']
-              ?.map((rawPoint) => LatLng.fromJson(rawPoint))
+              ?.map<LatLng>((rawPoint) => LatLng.fromJson(rawPoint))
               ?.toList(),
         );
       }) ??
@@ -342,7 +360,7 @@
           visible: rawPolygon['visible'],
           zIndex: rawPolygon['zIndex'],
           points: rawPolygon['points']
-              ?.map((rawPoint) => LatLng.fromJson(rawPoint))
+              ?.map<LatLng>((rawPoint) => LatLng.fromJson(rawPoint))
               ?.toList(),
         );
       }) ??
@@ -418,11 +436,11 @@
 
 gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) {
   final populationOptions = gmaps.CircleOptions()
-    ..strokeColor = '#' + circle.strokeColor.value.toRadixString(16)
-    ..strokeOpacity = 0.8
+    ..strokeColor = _getCssColor(circle.strokeColor)
+    ..strokeOpacity = _getCssOpacity(circle.strokeColor)
     ..strokeWeight = circle.strokeWidth
-    ..fillColor = '#' + circle.fillColor.value.toRadixString(16)
-    ..fillOpacity = 0.6
+    ..fillColor = _getCssColor(circle.fillColor)
+    ..fillOpacity = _getCssOpacity(circle.fillColor)
     ..center = gmaps.LatLng(circle.center.latitude, circle.center.longitude)
     ..radius = circle.radius
     ..visible = circle.visible;
@@ -437,11 +455,11 @@
   });
   return gmaps.PolygonOptions()
     ..paths = paths
-    ..strokeColor = '#' + polygon.strokeColor.value.toRadixString(16)
-    ..strokeOpacity = 0.8
+    ..strokeColor = _getCssColor(polygon.strokeColor)
+    ..strokeOpacity = _getCssOpacity(polygon.strokeColor)
     ..strokeWeight = polygon.strokeWidth
-    ..fillColor = '#' + polygon.fillColor.value.toRadixString(16)
-    ..fillOpacity = 0.35
+    ..fillColor = _getCssColor(polygon.fillColor)
+    ..fillOpacity = _getCssOpacity(polygon.fillColor)
     ..visible = polygon.visible
     ..zIndex = polygon.zIndex
     ..geodesic = polygon.geodesic;
@@ -456,9 +474,9 @@
 
   return gmaps.PolylineOptions()
     ..path = paths
-    ..strokeOpacity = 1.0
     ..strokeWeight = polyline.width
-    ..strokeColor = '#' + polyline.color.value.toRadixString(16).substring(0, 6)
+    ..strokeColor = _getCssColor(polyline.color)
+    ..strokeOpacity = _getCssOpacity(polyline.color)
     ..visible = polyline.visible
     ..zIndex = polyline.zIndex
     ..geodesic = polyline.geodesic;
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 5a879c2..3fb80d4 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+2
+version: 0.1.0+3
 
 flutter:
   plugin:
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 fc21476..625ecb3 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
@@ -149,10 +149,26 @@
             {'markerId': 'marker-1'}
           ],
           'polygonsToAdd': [
-            {'polygonId': 'polygon-1'}
+            {
+              'polygonId': 'polygon-1',
+              'points': [
+                [43.355114, -5.851333],
+                [43.354797, -5.851860],
+                [43.354469, -5.851318],
+                [43.354762, -5.850824],
+              ],
+            },
           ],
           'polylinesToAdd': [
-            {'polylineId': 'polyline-1'}
+            {
+              'polylineId': 'polyline-1',
+              'points': [
+                [43.355114, -5.851333],
+                [43.354797, -5.851860],
+                [43.354469, -5.851318],
+                [43.354762, -5.850824],
+              ],
+            },
           ],
         });
         controller.debugSetOverrides(
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 b1bff7e..4345843 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
@@ -3,12 +3,18 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:ui';
 
 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';
 
+// This value is used when comparing the results of
+// converting from a byte value to a double between 0 and 1.
+// (For Color opacity values, for example)
+const _acceptableDelta = 0.01;
+
 /// Test Shapes (Circle, Polygon, Polyline)
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@@ -77,6 +83,25 @@
       expect(controller.circles, contains(CircleId('2')));
       expect(controller.circles, isNot(contains(CircleId('3'))));
     });
+
+    testWidgets('Converts colors to CSS', (WidgetTester tester) async {
+      final circles = {
+        Circle(
+          circleId: CircleId('1'),
+          fillColor: Color(0x7FFABADA),
+          strokeColor: Color(0xFFC0FFEE),
+        ),
+      };
+
+      controller.addCircles(circles);
+
+      final circle = controller.circles.values.first.circle;
+
+      expect(circle.get('fillColor'), '#fabada');
+      expect(circle.get('fillOpacity'), closeTo(0.5, _acceptableDelta));
+      expect(circle.get('strokeColor'), '#c0ffee');
+      expect(circle.get('strokeOpacity'), closeTo(1, _acceptableDelta));
+    });
   });
 
   group('PolygonsController', () {
@@ -144,6 +169,25 @@
       expect(controller.polygons, contains(PolygonId('2')));
       expect(controller.polygons, isNot(contains(PolygonId('3'))));
     });
+
+    testWidgets('Converts colors to CSS', (WidgetTester tester) async {
+      final polygons = {
+        Polygon(
+          polygonId: PolygonId('1'),
+          fillColor: Color(0x7FFABADA),
+          strokeColor: Color(0xFFC0FFEE),
+        ),
+      };
+
+      controller.addPolygons(polygons);
+
+      final polygon = controller.polygons.values.first.polygon;
+
+      expect(polygon.get('fillColor'), '#fabada');
+      expect(polygon.get('fillOpacity'), closeTo(0.5, _acceptableDelta));
+      expect(polygon.get('strokeColor'), '#c0ffee');
+      expect(polygon.get('strokeOpacity'), closeTo(1, _acceptableDelta));
+    });
   });
 
   group('PolylinesController', () {
@@ -210,5 +254,21 @@
       expect(controller.lines, contains(PolylineId('2')));
       expect(controller.lines, isNot(contains(PolylineId('3'))));
     });
+
+    testWidgets('Converts colors to CSS', (WidgetTester tester) async {
+      final lines = {
+        Polyline(
+          polylineId: PolylineId('1'),
+          color: Color(0x7FFABADA),
+        ),
+      };
+
+      controller.addPolylines(lines);
+
+      final line = controller.lines.values.first.line;
+
+      expect(line.get('strokeColor'), '#fabada');
+      expect(line.get('strokeOpacity'), closeTo(0.5, _acceptableDelta));
+    });
   });
 }