From 94f593b0abc9c3b12e1ba240f417459464840722 Mon Sep 17 00:00:00 2001 From: Alexander Troshkov <20989940+aednlaxer@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:47:48 +0300 Subject: [PATCH 01/23] Add Advanced markers support (web) --- .../google_maps_flutter_web/CHANGELOG.md | 4 + .../google_maps_flutter_web/README.md | 11 +- .../advanced_marker_test.dart | 223 +++++++ .../advanced_markers_test.dart | 619 ++++++++++++++++++ .../google_maps_controller_test.dart | 18 +- .../google_maps_controller_test.mocks.dart | 282 ++++---- .../google_maps_plugin_test.mocks.dart | 241 ++++--- .../marker_clustering_test.dart | 2 +- .../example/integration_test/marker_test.dart | 35 +- .../integration_test/markers_test.dart | 13 +- .../integration_test/overlays_test.mocks.dart | 22 +- .../example/web/index.html | 2 +- .../lib/google_maps_flutter_web.dart | 1 + .../lib/src/convert.dart | 291 +++++++- .../lib/src/google_maps_controller.dart | 74 ++- .../lib/src/google_maps_flutter_web.dart | 6 + .../lib/src/google_maps_inspector_web.dart | 23 +- .../lib/src/marker.dart | 255 ++++++-- .../lib/src/marker_clustering.dart | 88 ++- .../lib/src/marker_clustering_js_interop.dart | 74 +-- .../lib/src/markers.dart | 209 ++++-- .../google_maps_flutter_web/pubspec.yaml | 2 +- 22 files changed, 1954 insertions(+), 541 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart 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 028ca4a0d5a..eec6e1b8ecd 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,7 @@ +## 0.5.15 + +* Adds Advanced markers support. + ## 0.5.14+3 * Replaces uses of deprecated `Color` properties. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index 9a92c70bad1..2dd51dcaf8d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -32,7 +32,7 @@ Modify the `` tag of your `web/index.html` to load the Google Maps JavaScr The Google Maps Web SDK splits some of its functionality in [separate libraries](https://developers.google.com/maps/documentation/javascript/libraries#libraries-for-dynamic-library-import). If your app needs the `drawing` library (to draw polygons, rectangles, polylines, -circles or markers on a map), include it like this: +circles or legacy markers on a map), include it like this: ```html +``` + To request multiple libraries, separate them with commas: ```html ``` diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart new file mode 100644 index 00000000000..95ee0f0deed --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart @@ -0,0 +1,223 @@ +// Copyright 2013 The Flutter 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 'dart:async'; +import 'dart:js_interop'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:google_maps_flutter_web/src/utils.dart'; +import 'package:integration_test/integration_test.dart'; + +/// Test Markers +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // Since onTap/DragEnd events happen asynchronously, we need to store when the event + // is fired. We use a completer so the test can wait for the future to be completed. + late Completer methodCalledCompleter; + + /// This is the future value of the [methodCalledCompleter]. Reinitialized + /// in the [setUp] method, and completed (as `true`) by [onTap] and [onDragEnd] + /// when those methods are called from the MarkerController. + late Future methodCalled; + + void onTap() { + methodCalledCompleter.complete(true); + } + + void onDragStart(gmaps.LatLng _) { + methodCalledCompleter.complete(true); + } + + void onDrag(gmaps.LatLng _) { + methodCalledCompleter.complete(true); + } + + void onDragEnd(gmaps.LatLng _) { + methodCalledCompleter.complete(true); + } + + setUp(() { + methodCalledCompleter = Completer(); + methodCalled = methodCalledCompleter.future; + }); + + group('MarkerController', () { + late gmaps.AdvancedMarkerElement marker; + + setUp(() { + marker = gmaps.AdvancedMarkerElement(); + }); + + testWidgets('onTap gets called', (WidgetTester tester) async { + AdvancedMarkerController(marker: marker, onTap: onTap); + + // Trigger a click event... + gmaps.event.trigger( + marker, + 'click', + gmaps.MapMouseEvent(), + ); + + // The event handling is now truly async. Wait for it... + expect(await methodCalled, isTrue); + }); + + testWidgets('onDragStart gets called', (WidgetTester tester) async { + AdvancedMarkerController(marker: marker, onDragStart: onDragStart); + + // Trigger a drag end event... + gmaps.event.trigger( + marker, + 'dragstart', + gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0), + ); + + expect(await methodCalled, isTrue); + }); + + testWidgets('onDrag gets called', (WidgetTester tester) async { + AdvancedMarkerController(marker: marker, onDrag: onDrag); + + // Trigger a drag end event... + gmaps.event.trigger( + marker, + 'drag', + gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0), + ); + + expect(await methodCalled, isTrue); + }); + + testWidgets('onDragEnd gets called', (WidgetTester tester) async { + AdvancedMarkerController(marker: marker, onDragEnd: onDragEnd); + + // Trigger a drag end event... + gmaps.event.trigger( + marker, + 'dragend', + gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0), + ); + + expect(await methodCalled, isTrue); + }); + + testWidgets('update', (WidgetTester tester) async { + final AdvancedMarkerController controller = + AdvancedMarkerController(marker: marker); + final gmaps.AdvancedMarkerElementOptions options = + gmaps.AdvancedMarkerElementOptions() + ..collisionBehavior = + gmaps.CollisionBehavior.OPTIONAL_AND_HIDES_LOWER_PRIORITY + ..gmpDraggable = true + ..position = gmaps.LatLng(42, 54); + + expect(marker.collisionBehavior, gmaps.CollisionBehavior.REQUIRED); + expect(marker.gmpDraggable, isFalse); + + controller.update(options); + + expect(marker.gmpDraggable, isTrue); + expect( + marker.collisionBehavior, + gmaps.CollisionBehavior.OPTIONAL_AND_HIDES_LOWER_PRIORITY, + ); + final JSAny? position = marker.position; + expect(position, isNotNull); + expect(position is gmaps.LatLngLiteral, isTrue); + expect((position! as gmaps.LatLngLiteral).lat, equals(42)); + expect((position as gmaps.LatLngLiteral).lng, equals(54)); + }); + + testWidgets('infoWindow null, showInfoWindow.', + (WidgetTester tester) async { + final AdvancedMarkerController controller = + AdvancedMarkerController(marker: marker); + + controller.showInfoWindow(); + + expect(controller.infoWindowShown, isFalse); + }); + + testWidgets('showInfoWindow', (WidgetTester tester) async { + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.Map map = gmaps.Map(createDivElement()); + marker.map = map; + final AdvancedMarkerController controller = AdvancedMarkerController( + marker: marker, + infoWindow: infoWindow, + ); + + controller.showInfoWindow(); + + expect(infoWindow.get('map'), map); + expect(controller.infoWindowShown, isTrue); + }); + + testWidgets('hideInfoWindow', (WidgetTester tester) async { + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.Map map = gmaps.Map(createDivElement()); + marker.map = map; + final AdvancedMarkerController controller = AdvancedMarkerController( + marker: marker, + infoWindow: infoWindow, + ); + + controller.hideInfoWindow(); + + expect(infoWindow.get('map'), isNull); + expect(controller.infoWindowShown, isFalse); + }); + + group('remove', () { + late AdvancedMarkerController controller; + + setUp(() { + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.Map map = gmaps.Map(createDivElement()); + marker.map = map; + controller = + AdvancedMarkerController(marker: marker, infoWindow: infoWindow); + }); + + testWidgets('drops gmaps instance', (WidgetTester tester) async { + controller.remove(); + + expect(controller.marker, isNull); + }); + + testWidgets('cannot call update after remove', + (WidgetTester tester) async { + final gmaps.AdvancedMarkerElementOptions options = + gmaps.AdvancedMarkerElementOptions()..gmpDraggable = true; + + controller.remove(); + + expect(() { + controller.update(options); + }, throwsAssertionError); + }); + + testWidgets('cannot call showInfoWindow after remove', + (WidgetTester tester) async { + controller.remove(); + + expect(() { + controller.showInfoWindow(); + }, throwsAssertionError); + }); + + testWidgets('cannot call hideInfoWindow after remove', + (WidgetTester tester) async { + controller.remove(); + + expect(() { + controller.hideInfoWindow(); + }, throwsAssertionError); + }); + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart new file mode 100644 index 00000000000..61cb1deff60 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart @@ -0,0 +1,619 @@ +// Copyright 2013 The Flutter 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 'dart:async'; +import 'dart:convert'; +import 'dart:js_interop'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps/google_maps.dart' as gmaps; +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:google_maps_flutter_web/src/marker_clustering.dart'; +import 'package:google_maps_flutter_web/src/utils.dart'; +import 'package:http/http.dart' as http; +import 'package:integration_test/integration_test.dart'; +import 'package:web/src/dom.dart' as dom; +import 'package:web/web.dart'; + +import 'resources/icon_image_base64.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('MarkersController', () { + late StreamController> events; + late MarkersController controller; + late ClusterManagersController + clusterManagersController; + late gmaps.Map map; + + setUp(() { + events = StreamController>(); + + clusterManagersController = + ClusterManagersController( + stream: events); + controller = AdvancedMarkersController( + stream: events, + clusterManagersController: clusterManagersController, + ); + map = gmaps.Map(createDivElement()); + clusterManagersController.bindToMap(123, map); + controller.bindToMap(123, map); + }); + + testWidgets('addMarkers', (WidgetTester tester) async { + final Set markers = { + AdvancedMarker(markerId: const MarkerId('1')), + AdvancedMarker(markerId: const MarkerId('2')), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 2); + expect(controller.markers, contains(const MarkerId('1'))); + expect(controller.markers, contains(const MarkerId('2'))); + expect(controller.markers, isNot(contains(const MarkerId('66')))); + }); + + testWidgets('changeMarkers', (WidgetTester tester) async { + gmaps.AdvancedMarkerElement? marker; + gmaps.LatLngLiteral? position; + + final Set markers = { + AdvancedMarker(markerId: const MarkerId('1')), + }; + await controller.addMarkers(markers); + + marker = controller.markers[const MarkerId('1')]?.marker; + expect(marker, isNotNull); + expect(marker!.gmpDraggable, isFalse); + + // By default, markers fall in LatLng(0, 0). + position = marker.position! as gmaps.LatLngLiteral; + expect(position, isNotNull); + expect(position.lat, equals(0)); + expect(position.lng, equals(0)); + + // Update the marker with draggable and position. + final Set updatedMarkers = { + AdvancedMarker( + markerId: const MarkerId('1'), + draggable: true, + position: const LatLng(42, 54), + ), + }; + await controller.changeMarkers(updatedMarkers); + expect(controller.markers.length, 1); + + marker = controller.markers[const MarkerId('1')]?.marker; + expect(marker, isNotNull); + expect(marker!.gmpDraggable, isTrue); + + position = marker.position! as gmaps.LatLngLiteral; + expect(position, isNotNull); + expect(position.lat, equals(42)); + expect(position.lng, equals(54)); + }); + + testWidgets( + 'changeMarkers resets marker position if not passed when updating!', + (WidgetTester tester) async { + gmaps.AdvancedMarkerElement? marker; + gmaps.LatLngLiteral? position; + + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + position: const LatLng(42, 54), + ), + }; + await controller.addMarkers(markers); + + marker = controller.markers[const MarkerId('1')]?.marker; + expect(marker, isNotNull); + expect(marker!.gmpDraggable, isFalse); + + position = marker.position! as gmaps.LatLngLiteral; + expect(position, isNotNull); + expect(position.lat, equals(42)); + expect(position.lng, equals(54)); + + // Update the marker without position. + final Set updatedMarkers = { + AdvancedMarker( + markerId: const MarkerId('1'), + draggable: true, + ), + }; + await controller.changeMarkers(updatedMarkers); + expect(controller.markers.length, 1); + + marker = controller.markers[const MarkerId('1')]?.marker; + expect(marker, isNotNull); + expect(marker!.gmpDraggable, isTrue); + + position = marker.position! as gmaps.LatLngLiteral; + expect(position, isNotNull); + expect(position.lat, equals(0)); + expect(position.lng, equals(0)); + }); + + testWidgets('removeMarkers', (WidgetTester tester) async { + final Set markers = { + AdvancedMarker(markerId: const MarkerId('1')), + AdvancedMarker(markerId: const MarkerId('2')), + AdvancedMarker(markerId: const MarkerId('3')), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 3); + + // Remove some markers. + final Set markerIdsToRemove = { + const MarkerId('1'), + const MarkerId('3'), + }; + + controller.removeMarkers(markerIdsToRemove); + + expect(controller.markers.length, 1); + expect(controller.markers, isNot(contains(const MarkerId('1')))); + expect(controller.markers, contains(const MarkerId('2'))); + expect(controller.markers, isNot(contains(const MarkerId('3')))); + }); + + testWidgets('InfoWindow show/hide', (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + infoWindow: const InfoWindow(title: 'Title', snippet: 'Snippet'), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + + controller.showMarkerInfoWindow(const MarkerId('1')); + + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isTrue); + + controller.hideMarkerInfoWindow(const MarkerId('1')); + + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + }); + + testWidgets('only single InfoWindow is visible', + (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + infoWindow: const InfoWindow(title: 'Title', snippet: 'Snippet'), + ), + AdvancedMarker( + markerId: const MarkerId('2'), + infoWindow: const InfoWindow(title: 'Title', snippet: 'Snippet'), + ), + }; + await controller.addMarkers(markers); + + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isFalse); + + controller.showMarkerInfoWindow(const MarkerId('1')); + + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isTrue); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isFalse); + + controller.showMarkerInfoWindow(const MarkerId('2')); + + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isTrue); + }); + + testWidgets('markers with custom asset icon work', + (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: AssetMapBitmap( + 'assets/red_square.png', + imagePixelRatio: 1.0, + ), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLImageElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + expect(icon, isNotNull); + + final String assetUrl = icon!.src; + expect(assetUrl, endsWith('assets/red_square.png')); + + // Asset size is 48x48 physical pixels. + expect(icon.style.width, '48px'); + expect(icon.style.height, '48px'); + }); + + testWidgets('markers with custom asset icon and pixel ratio work', + (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: AssetMapBitmap( + 'assets/red_square.png', + imagePixelRatio: 2.0, + ), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLImageElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + expect(icon, isNotNull); + + final String assetUrl = icon!.src; + expect(assetUrl, endsWith('assets/red_square.png')); + + // Asset size is 48x48 physical pixels, and with pixel ratio 2.0 it + // should be drawn with size 24x24 logical pixels. + expect(icon.style.width, '24px'); + expect(icon.style.height, '24px'); + }); + + testWidgets('markers with custom asset icon with width and height work', + (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: AssetMapBitmap( + 'assets/red_square.png', + imagePixelRatio: 2.0, + width: 64, + height: 64, + )), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLImageElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + expect(icon, isNotNull); + + final String assetUrl = icon!.src; + expect(assetUrl, endsWith('assets/red_square.png')); + + // Asset size is 48x48 physical pixels, + // and scaled to requested 64x64 size. + expect(icon.style.width, '64px'); + expect(icon.style.height, '64px'); + }); + + testWidgets('markers with missing asset icon should not set size', + (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: AssetMapBitmap( + 'assets/broken_asset_name.png', + imagePixelRatio: 2.0, + )), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLImageElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + expect(icon, isNotNull); + + final String assetUrl = icon!.src; + expect(assetUrl, endsWith('assets/broken_asset_name.png')); + + // For invalid assets, the size and scaledSize should be null. + expect(icon.style.width, isEmpty); + expect(icon.style.height, isEmpty); + }); + + testWidgets('markers with custom bitmap icon work', + (WidgetTester tester) async { + final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: BytesMapBitmap( + bytes, + imagePixelRatio: tester.view.devicePixelRatio, + ), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLImageElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + expect(icon, isNotNull); + + final String blobUrl = icon!.src; + expect(blobUrl, startsWith('blob:')); + + final http.Response response = await http.get(Uri.parse(blobUrl)); + expect( + response.bodyBytes, + bytes, + reason: + 'Bytes from the Icon blob must match bytes used to create AdvancedMarker', + ); + + // Icon size is 16x16 pixels, this should be automatically read from the + // bitmap and set to the icon size scaled to 8x8 using the + // given imagePixelRatio. + final int expectedSize = 16 ~/ tester.view.devicePixelRatio; + expect(icon.style.width, '${expectedSize}px'); + expect(icon.style.height, '${expectedSize}px'); + }); + + testWidgets('markers with custom bitmap icon and pixel ratio work', + (WidgetTester tester) async { + final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: BytesMapBitmap( + bytes, + imagePixelRatio: 1, + ), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLImageElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + expect(icon, isNotNull); + + // Icon size is 16x16 pixels, this should be automatically read from the + // bitmap and set to the icon size and should not be changed as + // image pixel ratio is set to 1.0. + expect(icon!.style.width, '16px'); + expect(icon.style.height, '16px'); + }); + + testWidgets('markers with custom bitmap icon pass size to sdk', + (WidgetTester tester) async { + final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: BytesMapBitmap( + bytes, + width: 20, + height: 30, + ), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLImageElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + expect(icon, isNotNull); + expect(icon!.style.width, '20px'); + expect(icon.style.height, '30px'); + }); + + testWidgets('markers created with pin config and colored glyph work', + (WidgetTester widgetTester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: BitmapDescriptor.pinConfig( + backgroundColor: const Color(0xFF00FF00), + borderColor: const Color(0xFFFF0000), + glyph: const CircleGlyph(color: Color(0xFFFFFFFF)), + ), + ), + }; + await controller.addMarkers(markers); + expect(controller.markers.length, 1); + + final HTMLDivElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; + expect(icon, isNotNull); + + // Query nodes and check colors. This is a bit fragile as it depends on + // the implementation details of the icon which is not part of the public + // API. + final NodeList backgroundNodes = + icon!.querySelectorAll("[class*='maps-pin-view-background']"); + final NodeList borderNodes = + icon.querySelectorAll("[class*='maps-pin-view-border']"); + final NodeList glyphNodes = + icon.querySelectorAll("[class*='maps-pin-view-default-glyph']"); + + expect(backgroundNodes.length, 1); + expect(borderNodes.length, 1); + expect(glyphNodes.length, 1); + + expect( + (backgroundNodes.item(0)! as dom.Element) + .getAttribute('fill') + ?.toUpperCase(), + '#00FF00', + ); + expect( + (borderNodes.item(0)! as dom.Element) + .getAttribute('fill') + ?.toUpperCase(), + '#FF0000', + ); + expect( + (glyphNodes.item(0)! as dom.Element) + .getAttribute('fill') + ?.toUpperCase(), + '#FFFFFF', + ); + }); + + testWidgets('markers created with text glyph work', + (WidgetTester widgetTester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: BitmapDescriptor.pinConfig( + backgroundColor: Colors.black, + borderColor: Colors.black, + glyph: const TextGlyph( + text: 'Hey', + textColor: Color(0xFF0000FF), + ), + ), + ), + }; + await controller.addMarkers(markers); + expect(controller.markers.length, 1); + + final HTMLDivElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; + expect(icon, isNotNull); + + // Query pin nodes and find text element. This is a bit fragile as it + // depends on the implementation details of the icon which is not part of + // the public API. + dom.Element? paragraphElement; + final NodeList paragraphs = icon!.querySelectorAll('p'); + for (int i = 0; i < paragraphs.length; i++) { + final dom.Element? paragraph = paragraphs.item(i) as dom.Element?; + if (paragraph?.innerHTML.toString() == 'Hey') { + paragraphElement = paragraph; + break; + } + } + + expect(paragraphElement, isNotNull); + expect(paragraphElement!.innerHTML.toString(), 'Hey'); + + expect( + paragraphElement.getAttribute('style')?.toLowerCase(), + contains('color: #0000ff'), + ); + }); + + testWidgets('markers created with bitmap glyph work', + (WidgetTester widgetTester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + icon: BitmapDescriptor.pinConfig( + backgroundColor: Colors.black, + borderColor: Colors.black, + glyph: BitmapGlyph( + bitmap: await BitmapDescriptor.asset( + const ImageConfiguration( + size: Size.square(12), + ), + 'assets/red_square.png', + ), + ), + ), + ), + }; + await controller.addMarkers(markers); + expect(controller.markers.length, 1); + + final HTMLDivElement? icon = controller + .markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; + expect(icon, isNotNull); + + // Query pin nodes and find text element. This is a bit fragile as it + // depends on the implementation details of the icon which is not part of + // the public API. + HTMLImageElement? imgElement; + final NodeList imgElements = icon!.querySelectorAll('img'); + for (int i = 0; i < imgElements.length; i++) { + final dom.Element? img = imgElements.item(i) as dom.Element?; + final String src = (img! as HTMLImageElement).src; + if (src.endsWith('assets/red_square.png')) { + imgElement = img as HTMLImageElement; + break; + } + } + + expect(imgElement, isNotNull); + expect(imgElement!.src, endsWith('assets/red_square.png')); + expect( + imgElement.getAttribute('style')?.toLowerCase(), + contains('width: 12.0px; height: 12.0px;'), + ); + }); + + testWidgets('InfoWindow snippet can have links', + (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + infoWindow: const InfoWindow( + title: 'title for test', + snippet: 'Go to Google >>>', + ), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLElement? content = controller + .markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; + expect(content, isNotNull); + + final String innerHtml = (content!.innerHTML as JSString).toDart; + expect(innerHtml, contains('title for test')); + expect( + innerHtml, + contains( + 'Go to Google >>>', + )); + }); + + testWidgets('InfoWindow content is clickable', (WidgetTester tester) async { + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + infoWindow: const InfoWindow( + title: 'title for test', + snippet: 'some snippet', + ), + ), + }; + + await controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final HTMLElement? content = controller + .markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; + + content?.click(); + + final MapEvent event = await events.stream.first; + + expect(event, isA()); + expect((event as InfoWindowTapEvent).value, equals(const MarkerId('1'))); + }); + }); +} 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 2cf115e0ae7..f97119abc43 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 @@ -18,7 +18,7 @@ import 'google_maps_controller_test.mocks.dart'; // This value is used when comparing long~num, like // LatLng values. -const String _kCloudMapId = '000000000000000'; // Dummy map ID. +const String _kMapId = '000000000000000'; // Dummy map ID. gmaps.Map mapShim() => throw UnimplementedError(); @@ -35,7 +35,7 @@ gmaps.Map mapShim() => throw UnimplementedError(); MockSpec( fallbackGenerators: {#googleMap: mapShim}, ), - MockSpec( + MockSpec>( fallbackGenerators: {#googleMap: mapShim}, ), MockSpec( @@ -491,9 +491,9 @@ void main() { mapConfiguration: const MapConfiguration( mapType: MapType.satellite, zoomControlsEnabled: true, - mapId: _kCloudMapId, + mapId: _kMapId, fortyFiveDegreeImageryEnabled: false, - ), + ) ); controller.debugSetOverrides( createMap: (_, gmaps.MapOptions options) { @@ -507,12 +507,10 @@ void main() { expect(capturedOptions, isNotNull); expect(capturedOptions!.mapTypeId, gmaps.MapTypeId.SATELLITE); expect(capturedOptions!.zoomControl, true); - expect(capturedOptions!.mapId, _kCloudMapId); - expect( - capturedOptions!.gestureHandling, - 'auto', - reason: 'by default the map handles zoom/pan gestures internally', - ); + expect(capturedOptions!.mapId, _kMapId); + expect(capturedOptions!.gestureHandling, 'auto', + reason: + 'by default the map handles zoom/pan gestures internally'); expect(capturedOptions!.rotateControl, false); expect(capturedOptions!.tilt, 0); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index a47b2daad9e..2b4f9e10f3d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.5 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart. // Do not manually edit this file. @@ -21,11 +21,18 @@ import 'google_maps_controller_test.dart' as _i5; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +class _FakeMarkerController_0 extends _i1.SmartFake + implements _i2.MarkerController { + _FakeMarkerController_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + /// A class which mocks [CirclesController]. /// /// See the documentation for Mockito's code generation for more information. @@ -50,9 +57,9 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -65,21 +72,21 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addCircles(Set<_i3.Circle>? circlesToAdd) => super.noSuchMethod( - Invocation.method(#addCircles, [circlesToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addCircles, [circlesToAdd]), + returnValueForMissingStub: null, + ); @override void changeCircles(Set<_i3.Circle>? circlesToChange) => super.noSuchMethod( - Invocation.method(#changeCircles, [circlesToChange]), - returnValueForMissingStub: null, - ); + Invocation.method(#changeCircles, [circlesToChange]), + returnValueForMissingStub: null, + ); @override void removeCircles(Set<_i3.CircleId>? circleIdsToRemove) => @@ -90,9 +97,9 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [HeatmapsController]. @@ -120,9 +127,9 @@ class MockHeatmapsController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -135,21 +142,21 @@ class MockHeatmapsController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addHeatmaps(Set<_i3.Heatmap>? heatmapsToAdd) => super.noSuchMethod( - Invocation.method(#addHeatmaps, [heatmapsToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addHeatmaps, [heatmapsToAdd]), + returnValueForMissingStub: null, + ); @override void changeHeatmaps(Set<_i3.Heatmap>? heatmapsToChange) => super.noSuchMethod( - Invocation.method(#changeHeatmaps, [heatmapsToChange]), - returnValueForMissingStub: null, - ); + Invocation.method(#changeHeatmaps, [heatmapsToChange]), + returnValueForMissingStub: null, + ); @override void removeHeatmaps(Set<_i3.HeatmapId>? heatmapIdsToRemove) => @@ -160,9 +167,9 @@ class MockHeatmapsController extends _i1.Mock @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [PolygonsController]. @@ -190,9 +197,9 @@ class MockPolygonsController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -205,21 +212,21 @@ class MockPolygonsController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addPolygons(Set<_i3.Polygon>? polygonsToAdd) => super.noSuchMethod( - Invocation.method(#addPolygons, [polygonsToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addPolygons, [polygonsToAdd]), + returnValueForMissingStub: null, + ); @override void changePolygons(Set<_i3.Polygon>? polygonsToChange) => super.noSuchMethod( - Invocation.method(#changePolygons, [polygonsToChange]), - returnValueForMissingStub: null, - ); + Invocation.method(#changePolygons, [polygonsToChange]), + returnValueForMissingStub: null, + ); @override void removePolygons(Set<_i3.PolygonId>? polygonIdsToRemove) => @@ -230,9 +237,9 @@ class MockPolygonsController extends _i1.Mock @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [PolylinesController]. @@ -261,9 +268,9 @@ class MockPolylinesController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -276,15 +283,15 @@ class MockPolylinesController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addPolylines(Set<_i3.Polyline>? polylinesToAdd) => super.noSuchMethod( - Invocation.method(#addPolylines, [polylinesToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addPolylines, [polylinesToAdd]), + returnValueForMissingStub: null, + ); @override void changePolylines(Set<_i3.Polyline>? polylinesToChange) => @@ -302,23 +309,24 @@ class MockPolylinesController extends _i1.Mock @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [MarkersController]. /// /// See the documentation for Mockito's code generation for more information. -class MockMarkersController extends _i1.Mock implements _i2.MarkersController { +class MockMarkersController extends _i1.Mock + implements _i2.MarkersController { @override - Map<_i3.MarkerId, _i2.MarkerController> get markers => + Map<_i3.MarkerId, _i2.MarkerController> get markers => (super.noSuchMethod( - Invocation.getter(#markers), - returnValue: <_i3.MarkerId, _i2.MarkerController>{}, - returnValueForMissingStub: <_i3.MarkerId, _i2.MarkerController>{}, - ) - as Map<_i3.MarkerId, _i2.MarkerController>); + Invocation.getter(#markers), + returnValue: <_i3.MarkerId, _i2.MarkerController>{}, + returnValueForMissingStub: <_i3.MarkerId, + _i2.MarkerController>{}, + ) as Map<_i3.MarkerId, _i2.MarkerController>); @override _i4.Map get googleMap => @@ -331,9 +339,9 @@ class MockMarkersController extends _i1.Mock implements _i2.MarkersController { @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -346,27 +354,60 @@ class MockMarkersController extends _i1.Mock implements _i2.MarkersController { @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override _i6.Future addMarkers(Set<_i3.Marker>? markersToAdd) => (super.noSuchMethod( - Invocation.method(#addMarkers, [markersToAdd]), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) - as _i6.Future); + Invocation.method(#addMarkers, [markersToAdd]), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Future<_i2.MarkerController> createMarkerController( + _i3.Marker? marker, + Object? markerOptions, + _i4.InfoWindow? gmInfoWindow, + ) => + (super.noSuchMethod( + Invocation.method(#createMarkerController, [ + marker, + markerOptions, + gmInfoWindow, + ]), + returnValue: _i6.Future<_i2.MarkerController>.value( + _FakeMarkerController_0( + this, + Invocation.method(#createMarkerController, [ + marker, + markerOptions, + gmInfoWindow, + ]), + ), + ), + returnValueForMissingStub: + _i6.Future<_i2.MarkerController>.value( + _FakeMarkerController_0( + this, + Invocation.method(#createMarkerController, [ + marker, + markerOptions, + gmInfoWindow, + ]), + ), + ), + ) as _i6.Future<_i2.MarkerController>); @override _i6.Future changeMarkers(Set<_i3.Marker>? markersToChange) => (super.noSuchMethod( - Invocation.method(#changeMarkers, [markersToChange]), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) - as _i6.Future); + Invocation.method(#changeMarkers, [markersToChange]), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override void removeMarkers(Set<_i3.MarkerId>? markerIdsToRemove) => @@ -377,30 +418,28 @@ class MockMarkersController extends _i1.Mock implements _i2.MarkersController { @override void showMarkerInfoWindow(_i3.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#showMarkerInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#showMarkerInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override void hideMarkerInfoWindow(_i3.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#hideMarkerInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#hideMarkerInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override - bool isInfoWindowShown(_i3.MarkerId? markerId) => - (super.noSuchMethod( - Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false, - returnValueForMissingStub: false, - ) - as bool); + bool isInfoWindowShown(_i3.MarkerId? markerId) => (super.noSuchMethod( + Invocation.method(#isInfoWindowShown, [markerId]), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [TileOverlaysController]. @@ -419,9 +458,9 @@ class MockTileOverlaysController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -434,9 +473,9 @@ class MockTileOverlaysController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addTileOverlays(Set<_i3.TileOverlay>? tileOverlaysToAdd) => @@ -461,15 +500,15 @@ class MockTileOverlaysController extends _i1.Mock @override void clearTileCache(_i3.TileOverlayId? tileOverlayId) => super.noSuchMethod( - Invocation.method(#clearTileCache, [tileOverlayId]), - returnValueForMissingStub: null, - ); + Invocation.method(#clearTileCache, [tileOverlayId]), + returnValueForMissingStub: null, + ); @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [GroundOverlaysController]. @@ -488,9 +527,9 @@ class MockGroundOverlaysController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -503,9 +542,9 @@ class MockGroundOverlaysController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addGroundOverlays(Set<_i3.GroundOverlay>? groundOverlaysToAdd) => @@ -528,9 +567,16 @@ class MockGroundOverlaysController extends _i1.Mock returnValueForMissingStub: null, ); + @override + _i4.GroundOverlay? getGroundOverlay(_i3.GroundOverlayId? groundOverlayId) => + (super.noSuchMethod( + Invocation.method(#getGroundOverlay, [groundOverlayId]), + returnValueForMissingStub: null, + ) as _i4.GroundOverlay?); + @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } 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 0d38efbf4f8..4016913d9a9 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 @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.5 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_plugin_test.dart. // Do not manually edit this file. @@ -20,6 +20,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types @@ -28,29 +29,29 @@ import 'package:mockito/mockito.dart' as _i1; class _FakeMapConfiguration_0 extends _i1.SmartFake implements _i2.MapConfiguration { _FakeMapConfiguration_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeStreamController_1 extends _i1.SmartFake implements _i3.StreamController { _FakeStreamController_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeLatLngBounds_2 extends _i1.SmartFake implements _i2.LatLngBounds { _FakeLatLngBounds_2(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeScreenCoordinate_3 extends _i1.SmartFake implements _i2.ScreenCoordinate { _FakeScreenCoordinate_3(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeLatLng_4 extends _i1.SmartFake implements _i2.LatLng { _FakeLatLng_4(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [GoogleMapController]. @@ -121,35 +122,36 @@ class MockGoogleMapController extends _i1.Mock void debugSetOverrides({ _i4.DebugCreateMapFunction? createMap, _i4.DebugSetOptionsFunction? setOptions, - _i4.MarkersController? markers, + _i4.MarkersController? markers, _i4.CirclesController? circles, _i4.HeatmapsController? heatmaps, _i4.PolygonsController? polygons, _i4.PolylinesController? polylines, - _i6.ClusterManagersController? clusterManagers, + _i6.ClusterManagersController? clusterManagers, _i4.TileOverlaysController? tileOverlays, _i4.GroundOverlaysController? groundOverlays, - }) => super.noSuchMethod( - Invocation.method(#debugSetOverrides, [], { - #createMap: createMap, - #setOptions: setOptions, - #markers: markers, - #circles: circles, - #heatmaps: heatmaps, - #polygons: polygons, - #polylines: polylines, - #clusterManagers: clusterManagers, - #tileOverlays: tileOverlays, - #groundOverlays: groundOverlays, - }), - returnValueForMissingStub: null, - ); + }) => + super.noSuchMethod( + Invocation.method(#debugSetOverrides, [], { + #createMap: createMap, + #setOptions: setOptions, + #markers: markers, + #circles: circles, + #heatmaps: heatmaps, + #polygons: polygons, + #polylines: polylines, + #clusterManagers: clusterManagers, + #tileOverlays: tileOverlays, + #groundOverlays: groundOverlays, + }), + returnValueForMissingStub: null, + ); @override void init() => super.noSuchMethod( - Invocation.method(#init, []), - returnValueForMissingStub: null, - ); + Invocation.method(#init, []), + returnValueForMissingStub: null, + ); @override void updateMapConfiguration(_i2.MapConfiguration? update) => @@ -160,117 +162,109 @@ class MockGoogleMapController extends _i1.Mock @override void updateStyles(List<_i5.MapTypeStyle>? styles) => super.noSuchMethod( - Invocation.method(#updateStyles, [styles]), - returnValueForMissingStub: null, - ); + Invocation.method(#updateStyles, [styles]), + returnValueForMissingStub: null, + ); @override - _i3.Future<_i2.LatLngBounds> getVisibleRegion() => - (super.noSuchMethod( + _i3.Future<_i2.LatLngBounds> getVisibleRegion() => (super.noSuchMethod( + Invocation.method(#getVisibleRegion, []), + returnValue: _i3.Future<_i2.LatLngBounds>.value( + _FakeLatLngBounds_2( + this, Invocation.method(#getVisibleRegion, []), - returnValue: _i3.Future<_i2.LatLngBounds>.value( - _FakeLatLngBounds_2( - this, - Invocation.method(#getVisibleRegion, []), - ), - ), - returnValueForMissingStub: _i3.Future<_i2.LatLngBounds>.value( - _FakeLatLngBounds_2( - this, - Invocation.method(#getVisibleRegion, []), - ), - ), - ) - as _i3.Future<_i2.LatLngBounds>); + ), + ), + returnValueForMissingStub: _i3.Future<_i2.LatLngBounds>.value( + _FakeLatLngBounds_2( + this, + Invocation.method(#getVisibleRegion, []), + ), + ), + ) as _i3.Future<_i2.LatLngBounds>); @override _i3.Future<_i2.ScreenCoordinate> getScreenCoordinate(_i2.LatLng? latLng) => (super.noSuchMethod( + Invocation.method(#getScreenCoordinate, [latLng]), + returnValue: _i3.Future<_i2.ScreenCoordinate>.value( + _FakeScreenCoordinate_3( + this, Invocation.method(#getScreenCoordinate, [latLng]), - returnValue: _i3.Future<_i2.ScreenCoordinate>.value( - _FakeScreenCoordinate_3( - this, - Invocation.method(#getScreenCoordinate, [latLng]), - ), - ), - returnValueForMissingStub: _i3.Future<_i2.ScreenCoordinate>.value( - _FakeScreenCoordinate_3( - this, - Invocation.method(#getScreenCoordinate, [latLng]), - ), - ), - ) - as _i3.Future<_i2.ScreenCoordinate>); + ), + ), + returnValueForMissingStub: _i3.Future<_i2.ScreenCoordinate>.value( + _FakeScreenCoordinate_3( + this, + Invocation.method(#getScreenCoordinate, [latLng]), + ), + ), + ) as _i3.Future<_i2.ScreenCoordinate>); @override _i3.Future<_i2.LatLng> getLatLng(_i2.ScreenCoordinate? screenCoordinate) => (super.noSuchMethod( + Invocation.method(#getLatLng, [screenCoordinate]), + returnValue: _i3.Future<_i2.LatLng>.value( + _FakeLatLng_4( + this, Invocation.method(#getLatLng, [screenCoordinate]), - returnValue: _i3.Future<_i2.LatLng>.value( - _FakeLatLng_4( - this, - Invocation.method(#getLatLng, [screenCoordinate]), - ), - ), - returnValueForMissingStub: _i3.Future<_i2.LatLng>.value( - _FakeLatLng_4( - this, - Invocation.method(#getLatLng, [screenCoordinate]), - ), - ), - ) - as _i3.Future<_i2.LatLng>); + ), + ), + returnValueForMissingStub: _i3.Future<_i2.LatLng>.value( + _FakeLatLng_4( + this, + Invocation.method(#getLatLng, [screenCoordinate]), + ), + ), + ) as _i3.Future<_i2.LatLng>); @override _i3.Future moveCamera(_i2.CameraUpdate? cameraUpdate) => (super.noSuchMethod( - Invocation.method(#moveCamera, [cameraUpdate]), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) - as _i3.Future); + Invocation.method(#moveCamera, [cameraUpdate]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); @override - _i3.Future getZoomLevel() => - (super.noSuchMethod( - Invocation.method(#getZoomLevel, []), - returnValue: _i3.Future.value(0.0), - returnValueForMissingStub: _i3.Future.value(0.0), - ) - as _i3.Future); + _i3.Future getZoomLevel() => (super.noSuchMethod( + Invocation.method(#getZoomLevel, []), + returnValue: _i3.Future.value(0.0), + returnValueForMissingStub: _i3.Future.value(0.0), + ) as _i3.Future); @override void updateCircles(_i2.CircleUpdates? updates) => super.noSuchMethod( - Invocation.method(#updateCircles, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updateCircles, [updates]), + returnValueForMissingStub: null, + ); @override void updateHeatmaps(_i2.HeatmapUpdates? updates) => super.noSuchMethod( - Invocation.method(#updateHeatmaps, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updateHeatmaps, [updates]), + returnValueForMissingStub: null, + ); @override void updatePolygons(_i2.PolygonUpdates? updates) => super.noSuchMethod( - Invocation.method(#updatePolygons, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updatePolygons, [updates]), + returnValueForMissingStub: null, + ); @override void updatePolylines(_i2.PolylineUpdates? updates) => super.noSuchMethod( - Invocation.method(#updatePolylines, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updatePolylines, [updates]), + returnValueForMissingStub: null, + ); @override _i3.Future updateMarkers(_i2.MarkerUpdates? updates) => (super.noSuchMethod( - Invocation.method(#updateMarkers, [updates]), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) - as _i3.Future); + Invocation.method(#updateMarkers, [updates]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); @override void updateClusterManagers(_i2.ClusterManagerUpdates? updates) => @@ -295,34 +289,39 @@ class MockGoogleMapController extends _i1.Mock @override void clearTileCache(_i2.TileOverlayId? id) => super.noSuchMethod( - Invocation.method(#clearTileCache, [id]), - returnValueForMissingStub: null, - ); + Invocation.method(#clearTileCache, [id]), + returnValueForMissingStub: null, + ); @override void showInfoWindow(_i2.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#showInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#showInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override void hideInfoWindow(_i2.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#hideInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#hideInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override - bool isInfoWindowShown(_i2.MarkerId? markerId) => - (super.noSuchMethod( - Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false, - returnValueForMissingStub: false, - ) - as bool); + bool isInfoWindowShown(_i2.MarkerId? markerId) => (super.noSuchMethod( + Invocation.method(#isInfoWindowShown, [markerId]), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool isAdvancedMarkersAvailable() => (super.noSuchMethod( + Invocation.method(#isAdvancedMarkersAvailable, []), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); @override void dispose() => super.noSuchMethod( - Invocation.method(#dispose, []), - returnValueForMissingStub: null, - ); + Invocation.method(#dispose, []), + returnValueForMissingStub: null, + ); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_clustering_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_clustering_test.dart index 0fb3e61b81d..57cc5fe010d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_clustering_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_clustering_test.dart @@ -114,7 +114,7 @@ void main() { } // Repeatedly checks an asynchronous value against a test condition, waiting -// one frame between each check, returing the value if it passes the predicate +// one frame between each check, returning the value if it passes the predicate // before [maxTries] is reached. // // Returns null if the predicate is never satisfied. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart index ce86f70ac32..58b405a4e59 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart @@ -53,7 +53,7 @@ void main() { }); testWidgets('onTap gets called', (WidgetTester tester) async { - MarkerController(marker: marker, onTap: onTap); + LegacyMarkerController(marker: marker, onTap: onTap); // Trigger a click event... gmaps.event.trigger(marker, 'click', gmaps.MapMouseEvent()); @@ -63,7 +63,7 @@ void main() { }); testWidgets('onDragStart gets called', (WidgetTester tester) async { - MarkerController(marker: marker, onDragStart: onDragStart); + LegacyMarkerController(marker: marker, onDragStart: onDragStart); // Trigger a drag end event... gmaps.event.trigger( @@ -76,7 +76,7 @@ void main() { }); testWidgets('onDrag gets called', (WidgetTester tester) async { - MarkerController(marker: marker, onDrag: onDrag); + LegacyMarkerController(marker: marker, onDrag: onDrag); // Trigger a drag end event... gmaps.event.trigger( @@ -89,7 +89,7 @@ void main() { }); testWidgets('onDragEnd gets called', (WidgetTester tester) async { - MarkerController(marker: marker, onDragEnd: onDragEnd); + LegacyMarkerController(marker: marker, onDragEnd: onDragEnd); // Trigger a drag end event... gmaps.event.trigger( @@ -102,11 +102,11 @@ void main() { }); testWidgets('update', (WidgetTester tester) async { - final MarkerController controller = MarkerController(marker: marker); - final gmaps.MarkerOptions options = - gmaps.MarkerOptions() - ..draggable = true - ..position = gmaps.LatLng(42, 54); + final LegacyMarkerController controller = + LegacyMarkerController(marker: marker); + final gmaps.MarkerOptions options = gmaps.MarkerOptions() + ..draggable = true + ..position = gmaps.LatLng(42, 54); expect(marker.isDraggableDefined(), isFalse); @@ -117,10 +117,10 @@ void main() { expect(marker.position?.lng, equals(54)); }); - testWidgets('infoWindow null, showInfoWindow.', ( - WidgetTester tester, - ) async { - final MarkerController controller = MarkerController(marker: marker); + testWidgets('infoWindow null, showInfoWindow.', + (WidgetTester tester) async { + final LegacyMarkerController controller = + LegacyMarkerController(marker: marker); controller.showInfoWindow(); @@ -131,7 +131,7 @@ void main() { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); final gmaps.Map map = gmaps.Map(createDivElement()); marker.set('map', map); - final MarkerController controller = MarkerController( + final LegacyMarkerController controller = LegacyMarkerController( marker: marker, infoWindow: infoWindow, ); @@ -146,7 +146,7 @@ void main() { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); final gmaps.Map map = gmaps.Map(createDivElement()); marker.set('map', map); - final MarkerController controller = MarkerController( + final LegacyMarkerController controller = LegacyMarkerController( marker: marker, infoWindow: infoWindow, ); @@ -158,13 +158,14 @@ void main() { }); group('remove', () { - late MarkerController controller; + late LegacyMarkerController controller; setUp(() { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); final gmaps.Map map = gmaps.Map(createDivElement()); marker.set('map', map); - controller = MarkerController(marker: marker, infoWindow: infoWindow); + controller = + LegacyMarkerController(marker: marker, infoWindow: infoWindow); }); testWidgets('drops gmaps instance', (WidgetTester tester) async { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart index b8f60ee24cd..d97f93c2ff6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart @@ -25,18 +25,17 @@ void main() { group('MarkersController', () { late StreamController> events; - late MarkersController controller; - late ClusterManagersController clusterManagersController; + late LegacyMarkersController controller; + late ClusterManagersController clusterManagersController; late gmaps.Map map; setUp(() { events = StreamController>(); - clusterManagersController = ClusterManagersController(stream: events); - controller = MarkersController( - stream: events, - clusterManagersController: clusterManagersController, - ); + clusterManagersController = + ClusterManagersController(stream: events); + controller = LegacyMarkersController( + stream: events, clusterManagersController: clusterManagersController); map = gmaps.Map(createDivElement()); clusterManagersController.bindToMap(123, map); controller.bindToMap(123, map); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart index 8a13b88eb10..7d00b267d8b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.5 from annotations // in google_maps_flutter_web_integration_tests/integration_test/overlays_test.dart. // Do not manually edit this file. @@ -17,6 +17,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types @@ -24,7 +25,7 @@ import 'package:mockito/mockito.dart' as _i1; class _FakeTile_0 extends _i1.SmartFake implements _i2.Tile { _FakeTile_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [TileProvider]. @@ -34,13 +35,12 @@ class MockTileProvider extends _i1.Mock implements _i2.TileProvider { @override _i3.Future<_i2.Tile> getTile(int? x, int? y, int? zoom) => (super.noSuchMethod( - Invocation.method(#getTile, [x, y, zoom]), - returnValue: _i3.Future<_i2.Tile>.value( - _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), - ), - returnValueForMissingStub: _i3.Future<_i2.Tile>.value( - _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), - ), - ) - as _i3.Future<_i2.Tile>); + Invocation.method(#getTile, [x, y, zoom]), + returnValue: _i3.Future<_i2.Tile>.value( + _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), + ), + returnValueForMissingStub: _i3.Future<_i2.Tile>.value( + _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), + ), + ) as _i3.Future<_i2.Tile>); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/web/index.html b/packages/google_maps_flutter/google_maps_flutter_web/example/web/index.html index 9819866730a..4b4fe18c74b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/web/index.html +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/web/index.html @@ -7,7 +7,7 @@ Browser Tests - + 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 08588d9ccbf..217ee8797dd 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 @@ -20,6 +20,7 @@ import 'package:google_maps/google_maps_visualization.dart' as visualization; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:sanitize_html/sanitize_html.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'package:web/web.dart' as web; import 'package:web/web.dart'; import 'src/dom_window_extension.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 4c234b8646c..cd3d66f75ab 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 @@ -229,7 +229,7 @@ LatLng gmLatLngToLatLng(gmaps.LatLng latLng) { } /// Converts a [gmaps.LatLngBounds] into a [LatLngBounds]. -LatLngBounds gmLatLngBoundsTolatLngBounds(gmaps.LatLngBounds latLngBounds) { +LatLngBounds gmLatLngBoundsToLatLngBounds(gmaps.LatLngBounds latLngBounds) { return LatLngBounds( southwest: gmLatLngToLatLng(latLngBounds.southWest), northeast: gmLatLngToLatLng(latLngBounds.northEast), @@ -330,18 +330,32 @@ gmaps.Size? _gmSizeFromIconConfig(List iconConfig, int sizeIndex) { size = gmaps.Size(rawIconSize[0]! as double, rawIconSize[1]! as double); } } + return size; } -/// Sets the size of the Google Maps icon. -void _setIconSize({required Size size, required gmaps.Icon icon}) { - final gmaps.Size gmapsSize = gmaps.Size(size.width, size.height); - icon.size = gmapsSize; - icon.scaledSize = gmapsSize; +// Sets the size and style of the [icon] element. +void _setIconStyle({ + required web.Element icon, + required gmaps.Size? size, + required double? opacity, + required bool? isVisible, +}) { + icon.setAttribute( + 'style', + [ + if (size != null) ...[ + 'width: ${size.width.toStringAsFixed(1)}px;', + 'height: ${size.height.toStringAsFixed(1)}px;', + ], + if (opacity != null) 'opacity: $opacity;', + if (isVisible != null) 'visibility: ${isVisible ? 'visible' : 'hidden'};', + ].join(' '), + ); } void _setIconAnchor({ - required Size size, + required gmaps.Size size, required Offset anchor, required gmaps.Icon icon, }) { @@ -352,6 +366,13 @@ void _setIconAnchor({ icon.anchor = gmapsAnchor; } +// Sets the size of the Google Maps icon. +void _setIconSize({required gmaps.Size size, required gmaps.Icon icon}) { + final gmaps.Size gmapsSize = gmaps.Size(size.width, size.height); + icon.size = gmapsSize; + icon.scaledSize = gmapsSize; +} + /// Determines the appropriate size for a bitmap based on its descriptor. /// /// This method returns the icon's size based on the provided [width] and @@ -359,12 +380,12 @@ void _setIconAnchor({ /// [imagePixelRatio] based on the actual size of the image fetched from the /// [url]. If only one of the dimensions is provided, the other is calculated to /// maintain the image's original aspect ratio. -Future _getBitmapSize(MapBitmap mapBitmap, String url) async { +Future _getBitmapSize(MapBitmap mapBitmap, String url) async { final double? width = mapBitmap.width; final double? height = mapBitmap.height; if (width != null && height != null) { // If both, width and height are set, return the provided dimensions. - return Size(width, height); + return gmaps.Size(width, height); } else { assert( url.isNotEmpty, @@ -395,7 +416,7 @@ Future _getBitmapSize(MapBitmap mapBitmap, String url) async { } // Return the calculated size. - return Size(targetWidth, targetHeight); + return gmaps.Size(targetWidth, targetHeight); } } @@ -427,6 +448,148 @@ void _cleanUpBitmapConversionCaches() { _bitmapBlobUrlCache.clear(); } +/// Converts a [BitmapDescriptor] into a [Node] that can be used as +/// [AdvancedMarker]'s icon. +Future _advancedMarkerIconFromBitmapDescriptor( + BitmapDescriptor bitmapDescriptor, { + required double? opacity, + required bool isVisible, + required double? rotation, +}) async { + if (bitmapDescriptor is PinConfig) { + final gmaps.PinElementOptions options = + gmaps.PinElementOptions() + ..background = + bitmapDescriptor.backgroundColor != null + ? _getCssColor(bitmapDescriptor.backgroundColor!) + : null + ..borderColor = + bitmapDescriptor.borderColor != null + ? _getCssColor(bitmapDescriptor.borderColor!) + : null; + + final AdvancedMarkerGlyph? glyph = bitmapDescriptor.glyph; + switch (glyph) { + case final CircleGlyph circleGlyph: + options.glyphColor = _getCssColor(circleGlyph.color); + case final TextGlyph textGlyph: + final web.Element element = document.createElement('p'); + element.innerHTML = textGlyph.text.toJS; + if (textGlyph.textColor != null) { + element.setAttribute( + 'style', + 'color: ${_getCssColor(textGlyph.textColor!)}', + ); + } + options.glyph = element; + case final BitmapGlyph bitmapGlyph: + final Node? glyphBitmap = await _advancedMarkerIconFromBitmapDescriptor( + bitmapGlyph.bitmap, + // Always opaque, opacity is handled by the parent marker. + opacity: 1.0, + // Always visible, as the visibility is handled by the parent marker. + isVisible: true, + rotation: rotation, + ); + options.glyph = glyphBitmap; + case null: + break; + } + + final gmaps.PinElement pinElement = gmaps.PinElement(options); + final HTMLElement htmlElement = pinElement.element; + htmlElement.style + ..visibility = isVisible ? 'visible' : 'hidden' + ..opacity = opacity?.toString() ?? '1.0' + ..transform = rotation != null ? 'rotate(${rotation}deg)' : ''; + return htmlElement; + } + + if (bitmapDescriptor is MapBitmap) { + final String url = switch (bitmapDescriptor) { + (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( + bytesMapBitmap.byteData.hashCode, + () { + final Blob blob = Blob( + [bytesMapBitmap.byteData.toJS].toJS, + ); + return URL.createObjectURL(blob as JSObject); + }, + ), + (final AssetMapBitmap assetMapBitmap) => ui_web.assetManager.getAssetUrl( + assetMapBitmap.assetName, + ), + _ => throw UnimplementedError(), + }; + + final web.Element icon = document.createElement('img') + ..setAttribute('src', url); + + final gmaps.Size? size = switch (bitmapDescriptor.bitmapScaling) { + MapBitmapScaling.auto => await _getBitmapSize(bitmapDescriptor, url), + MapBitmapScaling.none => null, + }; + _setIconStyle( + icon: icon, + size: size, + opacity: opacity, + isVisible: isVisible, + ); + + return icon; + } + + // The following code is for the deprecated BitmapDescriptor.fromBytes + // and BitmapDescriptor.fromAssetImage. + final List iconConfig = bitmapDescriptor.toJson() as List; + if (iconConfig[0] == 'fromAssetImage') { + assert(iconConfig.length >= 2); + // iconConfig[2] contains the DPIs of the screen, but that information is + // already encoded in the iconConfig[1] + final web.Element icon = document.createElement('img')..setAttribute( + 'src', + ui_web.assetManager.getAssetUrl(iconConfig[1]! as String), + ); + + final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 3); + _setIconStyle( + icon: icon, + size: size, + opacity: opacity, + isVisible: isVisible, + ); + return icon; + } else if (iconConfig[0] == 'fromBytes') { + // Grab the bytes, and put them into a blob. + final List bytes = iconConfig[1]! as List; + // Create a Blob from bytes, but let the browser figure out the encoding. + final Blob blob; + + assert( + bytes is Uint8List, + 'The bytes for a BitmapDescriptor icon must be a Uint8List', + ); + + // TODO(ditman): Improve this conversion + // See https://github.com/dart-lang/web/issues/180 + blob = Blob([(bytes as Uint8List).toJS].toJS); + + final web.Element icon = document.createElement('img') + ..setAttribute('src', URL.createObjectURL(blob as JSObject)); + + final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); + _setIconStyle( + size: size, + icon: icon, + opacity: opacity, + isVisible: isVisible, + ); + return icon; + } + + return null; +} + // Converts a [BitmapDescriptor] into a [gmaps.Icon] that can be used in Markers. Future _gmIconFromBitmapDescriptor( BitmapDescriptor bitmapDescriptor, @@ -435,13 +598,27 @@ Future _gmIconFromBitmapDescriptor( gmaps.Icon? icon; if (bitmapDescriptor is MapBitmap) { - final String url = urlFromMapBitmap(bitmapDescriptor); + final String url = switch (bitmapDescriptor) { + (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( + bytesMapBitmap.byteData.hashCode, + () { + final Blob blob = Blob( + [bytesMapBitmap.byteData.toJS].toJS, + ); + return URL.createObjectURL(blob as JSObject); + }, + ), + (final AssetMapBitmap assetMapBitmap) => ui_web.assetManager.getAssetUrl( + assetMapBitmap.assetName, + ), + _ => throw UnimplementedError(), + }; icon = gmaps.Icon()..url = url; switch (bitmapDescriptor.bitmapScaling) { case MapBitmapScaling.auto: - final Size? size = await _getBitmapSize(bitmapDescriptor, url); + final gmaps.Size? size = await _getBitmapSize(bitmapDescriptor, url); if (size != null) { _setIconSize(size: size, icon: icon); _setIconAnchor(size: size, anchor: anchor, icon: icon); @@ -449,6 +626,7 @@ Future _gmIconFromBitmapDescriptor( case MapBitmapScaling.none: break; } + return icon; } @@ -496,27 +674,66 @@ Future _gmIconFromBitmapDescriptor( return icon; } -/// Computes the options for a new [gmaps.Marker] from an incoming set of options -/// [marker], and the existing marker registered with the map: [currentMarker]. -Future _markerOptionsFromMarker( +// Computes the options for a new [gmaps.Marker] from an incoming set of options +// [marker], and the existing marker registered with the map: [currentMarker]. +Future _markerOptionsFromMarker( Marker marker, - gmaps.Marker? currentMarker, + T? currentMarker, ) async { - return gmaps.MarkerOptions() - ..position = gmaps.LatLng( - marker.position.latitude, - marker.position.longitude, - ) - ..title = sanitizeHtml(marker.infoWindow.title ?? '') - // The deprecated parameter is used here to avoid losing precision. - // ignore: deprecated_member_use - ..zIndex = marker.zIndex - ..visible = marker.visible - ..opacity = marker.alpha - ..draggable = marker.draggable - ..icon = await _gmIconFromBitmapDescriptor(marker.icon, marker.anchor); - // TODO(ditman): Compute anchor properly, otherwise infowindows attach to the wrong spot. - // Flat and Rotation are not supported directly on the web. + if (marker is AdvancedMarker) { + final gmaps.AdvancedMarkerElementOptions options = + gmaps.AdvancedMarkerElementOptions() + ..collisionBehavior = _markerCollisionBehaviorToGmCollisionBehavior( + marker.collisionBehavior, + ) + ..content = await _advancedMarkerIconFromBitmapDescriptor( + marker.icon, + opacity: marker.alpha, + isVisible: marker.visible, + rotation: marker.rotation, + ) + ..position = gmaps.LatLng( + marker.position.latitude, + marker.position.longitude, + ) + ..title = sanitizeHtml(marker.infoWindow.title ?? '') + ..zIndex = marker.zIndex + ..gmpDraggable = marker.draggable; + return options as O; + } else { + final gmaps.MarkerOptions options = + gmaps.MarkerOptions() + ..position = gmaps.LatLng( + marker.position.latitude, + marker.position.longitude, + ) + ..icon = await _gmIconFromBitmapDescriptor(marker.icon, marker.anchor) + ..title = sanitizeHtml(marker.infoWindow.title ?? '') + ..zIndex = marker.zIndex + ..visible = marker.visible + ..opacity = marker.alpha + ..draggable = marker.draggable; + + // TODO(ditman): Compute anchor properly, otherwise infowindows attach to the wrong spot. + // Flat and Rotation are not supported directly on the web. + + return options as O; + } +} + +/// Gets marker Id from a [marker] object. +MarkerId getMarkerId(Object? marker) { + final JSObject object = marker! as JSObject; + final gmaps.MVCObject mapObject = marker as gmaps.MVCObject; + if (object.isA()) { + return MarkerId((mapObject.get('markerId')! as JSString).toDart); + } else if (object.isA()) { + return MarkerId((mapObject.get('id')! as JSString).toDart); + } else { + throw ArgumentError( + 'Must be either a gmaps.Marker or a gmaps.AdvancedMarkerElement', + ); + } } gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { @@ -864,3 +1081,15 @@ gmaps.ControlPosition? _toControlPosition( return gmaps.ControlPosition.TOP_RIGHT; } } + +gmaps.CollisionBehavior _markerCollisionBehaviorToGmCollisionBehavior( + MarkerCollisionBehavior markerCollisionBehavior, +) { + return switch (markerCollisionBehavior) { + MarkerCollisionBehavior.requiredDisplay => gmaps.CollisionBehavior.REQUIRED, + MarkerCollisionBehavior.optionalAndHidesLowerPriority => + gmaps.CollisionBehavior.OPTIONAL_AND_HIDES_LOWER_PRIORITY, + MarkerCollisionBehavior.requiredAndHidesOptional => + gmaps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL, + }; +} 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 9a1aedcf639..a2571af94d3 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 @@ -38,13 +38,54 @@ class GoogleMapController { _heatmapsController = HeatmapsController(); _polygonsController = PolygonsController(stream: _streamController); _polylinesController = PolylinesController(stream: _streamController); - _clusterManagersController = ClusterManagersController( - stream: _streamController, - ); - _markersController = MarkersController( - stream: _streamController, - clusterManagersController: _clusterManagersController!, - ); + + // Check if all markers are of the same type. Mixing marker types is not + // allowed. + final Set markerTypes = + _markers.map((Marker e) => e.runtimeType).toSet(); + if (markerTypes.isNotEmpty) { + assert(markerTypes.length == 1, 'All markers must be of the same type.'); + + switch (mapConfiguration.markerType) { + case null: + case MarkerType.marker: + assert( + markerTypes.first == Marker, + 'All markers must be of type Marker because ' + 'mapConfiguration.markerType is MarkerType.marker', + ); + case MarkerType.advancedMarker: + assert( + markerTypes.first == AdvancedMarker, + 'All markers must be of type AdvancedMarker because ' + 'mapConfiguration.markerType is MarkerType.advanced', + ); + } + } + + // Advanced and legacy markers are handled differently so markers controller + // and cluster manager need be initialized with the correct marker type. + _clusterManagersController = switch (mapConfiguration.markerType) { + null || + MarkerType.marker => + ClusterManagersController(stream: _streamController), + MarkerType.advancedMarker => + ClusterManagersController( + stream: _streamController), + }; + _markersController = switch (mapConfiguration.markerType) { + null || MarkerType.marker => LegacyMarkersController( + stream: stream, + clusterManagersController: _clusterManagersController! + as ClusterManagersController, + ), + MarkerType.advancedMarker => AdvancedMarkersController( + stream: stream, + clusterManagersController: clusterManagersController! + as ClusterManagersController, + ), + }; + _tileOverlaysController = TileOverlaysController(); _groundOverlaysController = GroundOverlaysController( stream: _streamController, @@ -134,8 +175,8 @@ class GoogleMapController { HeatmapsController? _heatmapsController; PolygonsController? _polygonsController; PolylinesController? _polylinesController; - MarkersController? _markersController; - ClusterManagersController? _clusterManagersController; + MarkersController? _markersController; + ClusterManagersController? _clusterManagersController; TileOverlaysController? _tileOverlaysController; GroundOverlaysController? _groundOverlaysController; @@ -152,7 +193,7 @@ class GoogleMapController { /// The ClusterManagersController of this Map. Only for integration testing. @visibleForTesting - ClusterManagersController? get clusterManagersController => + ClusterManagersController? get clusterManagersController => _clusterManagersController; /// The GroundOverlaysController of this Map. Only for integration testing. @@ -165,12 +206,12 @@ class GoogleMapController { void debugSetOverrides({ DebugCreateMapFunction? createMap, DebugSetOptionsFunction? setOptions, - MarkersController? markers, + MarkersController? markers, CirclesController? circles, HeatmapsController? heatmaps, PolygonsController? polygons, PolylinesController? polylines, - ClusterManagersController? clusterManagers, + ClusterManagersController? clusterManagers, TileOverlaysController? tileOverlays, GroundOverlaysController? groundOverlays, }) { @@ -468,7 +509,7 @@ class GoogleMapController { await Future.value(_googleMap!.bounds) ?? _nullGmapsLatLngBounds; - return gmLatLngBoundsTolatLngBounds(bounds); + return gmLatLngBoundsToLatLngBounds(bounds); } /// Returns the [ScreenCoordinate] for a given viewport [LatLng]. @@ -652,6 +693,13 @@ class GoogleMapController { return _markersController?.isInfoWindowShown(markerId) ?? false; } + /// Returns true if this map supports [AdvancedMarker]s. + bool isAdvancedMarkersAvailable() { + assert(_googleMap != null, 'Cannot get map capabilities of a null map.'); + + return _googleMap!.mapCapabilities.isAdvancedMarkersAvailable ?? false; + } + // Cleanup /// Disposes of this controller and its resources. 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 4f0058b4aec..7fe39488584 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 @@ -314,6 +314,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _map(mapId).lastStyleError; } + @override + Future isAdvancedMarkersAvailable({required int mapId}) async { + final GoogleMapController map = _map(mapId); + return map.isAdvancedMarkersAvailable(); + } + /// Disposes of the current map. It can't be used afterwards! @override void dispose({required int mapId}) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart index b14a8e2812b..c23fd2a85db 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart @@ -15,8 +15,8 @@ import 'marker_clustering.dart'; typedef ConfigurationProvider = MapConfiguration Function(int mapId); /// Function that gets the [ClusterManagersController] for a given `mapId`. -typedef ClusterManagersControllerProvider = - ClusterManagersController? Function(int mapId); +typedef ClusterManagersControllerProvider = ClusterManagersController? + Function(int mapId); /// Function that gets the [GroundOverlaysController] for a given `mapId`. typedef GroundOverlaysControllerProvider = @@ -103,16 +103,15 @@ class GoogleMapsInspectorWeb extends GoogleMapsInspectorPlatform { final JSAny? clickable = groundOverlay.get('clickable'); return GroundOverlay.fromBounds( - groundOverlayId: groundOverlayId, - image: BytesMapBitmap( - Uint8List.fromList([0]), - bitmapScaling: MapBitmapScaling.none, - ), - bounds: gmLatLngBoundsTolatLngBounds(groundOverlay.bounds), - transparency: 1.0 - groundOverlay.opacity, - visible: groundOverlay.map != null, - clickable: clickable != null && (clickable as JSBoolean).toDart, - ); + groundOverlayId: groundOverlayId, + image: BytesMapBitmap( + Uint8List.fromList([0]), + bitmapScaling: MapBitmapScaling.none, + ), + bounds: gmLatLngBoundsToLatLngBounds(groundOverlay.bounds), + transparency: 1.0 - groundOverlay.opacity, + visible: groundOverlay.map != null, + clickable: clickable != null && (clickable as JSBoolean).toDart); } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index fadf59fd767..468e0feb27c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -4,11 +4,15 @@ part of '../google_maps_flutter_web.dart'; -/// The `MarkerController` class wraps a [gmaps.Marker], how it handles events, and its associated (optional) [gmaps.InfoWindow] widget. -class MarkerController { - /// Creates a `MarkerController`, which wraps a [gmaps.Marker] object, its `onTap`/`onDrag` behavior, and its associated [gmaps.InfoWindow]. +/// The `MarkerController` class wraps a [gmaps.AdvancedMarkerElement] +/// or [gmaps.Marker], how it handles events, and its associated (optional) +/// [gmaps.InfoWindow] widget. +abstract class MarkerController { + /// Creates a `MarkerController`, which wraps a [gmaps.AdvancedMarkerElement] + /// or [gmaps.Marker] object, its `onTap`/`onDrag` behavior, and its + /// associated [gmaps.InfoWindow]. MarkerController({ - required gmaps.Marker marker, + required T marker, gmaps.InfoWindow? infoWindow, bool consumeTapEvents = false, LatLngCallback? onDragStart, @@ -16,36 +20,20 @@ class MarkerController { LatLngCallback? onDragEnd, VoidCallback? onTap, ClusterManagerId? clusterManagerId, - }) : _marker = marker, - _infoWindow = infoWindow, - _consumeTapEvents = consumeTapEvents, - _clusterManagerId = clusterManagerId { - if (onTap != null) { - marker.onClick.listen((gmaps.MapMouseEvent event) { - onTap.call(); - }); - } - if (onDragStart != null) { - marker.onDragstart.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDragStart.call(event.latLng ?? _nullGmapsLatLng); - }); - } - if (onDrag != null) { - marker.onDrag.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDrag.call(event.latLng ?? _nullGmapsLatLng); - }); - } - if (onDragEnd != null) { - marker.onDragend.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDragEnd.call(event.latLng ?? _nullGmapsLatLng); - }); - } + }) : _marker = marker, + _infoWindow = infoWindow, + _consumeTapEvents = consumeTapEvents, + _clusterManagerId = clusterManagerId { + initializeMarkerListener( + marker: marker, + onDragStart: onDragStart, + onDrag: onDrag, + onDragEnd: onDragEnd, + onTap: onTap, + ); } - gmaps.Marker? _marker; + T? _marker; final bool _consumeTapEvents; @@ -64,56 +52,221 @@ class MarkerController { /// Returns [ClusterManagerId] if marker belongs to cluster. ClusterManagerId? get clusterManagerId => _clusterManagerId; - /// Returns the [gmaps.Marker] associated to this controller. - gmaps.Marker? get marker => _marker; + /// Returns the marker associated to this controller. + T? get marker => _marker; /// Returns the [gmaps.InfoWindow] associated to the marker. @visibleForTesting gmaps.InfoWindow? get infoWindow => _infoWindow; - /// Updates the options of the wrapped [gmaps.Marker] object. + /// Updates the options of the wrapped marker object. /// /// This cannot be called after [remove]. void update( - gmaps.MarkerOptions options, { + O options, { HTMLElement? newInfoWindowContent, + }); + + /// Initializes the listener for the wrapped marker object. + void initializeMarkerListener({ + required T marker, + required LatLngCallback? onDragStart, + required LatLngCallback? onDrag, + required LatLngCallback? onDragEnd, + required VoidCallback? onTap, + }); + + /// Disposes of the currently wrapped marker object. + void remove(); + + /// Hide the associated [gmaps.InfoWindow]. + /// + /// This cannot be called after [remove]. + void hideInfoWindow() { + assert(_marker != null, 'Cannot `hideInfoWindow` on a `remove`d Marker.'); + if (_infoWindow != null) { + _infoWindow.close(); + _infoWindowShown = false; + } + } + + /// Show the associated [gmaps.InfoWindow]. + /// + /// This cannot be called after [remove]. + void showInfoWindow(); +} + +/// A `MarkerController` that wraps a [gmaps.Marker] object. +/// +/// [gmaps.Marker] is a legacy class that is being replaced +/// by [gmaps.AdvancedMarkerElement]. +class LegacyMarkerController + extends MarkerController { + /// Creates a `LegacyMarkerController`, which wraps a [gmaps.Marker] object. + LegacyMarkerController({ + required super.marker, + super.infoWindow, + super.consumeTapEvents, + super.onDragStart, + super.onDrag, + super.onDragEnd, + super.onTap, + super.clusterManagerId, + }); + + @override + void initializeMarkerListener({ + required gmaps.Marker marker, + required LatLngCallback? onDragStart, + required LatLngCallback? onDrag, + required LatLngCallback? onDragEnd, + required VoidCallback? onTap, }) { - assert(_marker != null, 'Cannot `update` Marker after calling `remove`.'); - _marker!.options = options; - if (_infoWindow != null && newInfoWindowContent != null) { - _infoWindow.content = newInfoWindowContent; + if (onTap != null) { + marker.onClick.listen((gmaps.MapMouseEvent event) { + onTap.call(); + }); + } + if (onDragStart != null) { + marker.onDragstart.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragStart.call(event.latLng ?? _nullGmapsLatLng); + }); + } + if (onDrag != null) { + marker.onDrag.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDrag.call(event.latLng ?? _nullGmapsLatLng); + }); + } + if (onDragEnd != null) { + marker.onDragend.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragEnd.call(event.latLng ?? _nullGmapsLatLng); + }); } } - /// Disposes of the currently wrapped [gmaps.Marker]. + @override void remove() { if (_marker != null) { _infoWindowShown = false; - _marker!.visible = false; _marker!.map = null; _marker = null; } } - /// Hide the associated [gmaps.InfoWindow]. - /// - /// This cannot be called after [remove]. - void hideInfoWindow() { - assert(_marker != null, 'Cannot `hideInfoWindow` on a `remove`d Marker.'); + @override + void showInfoWindow() { + assert(_marker != null, 'Cannot `showInfoWindow` on a `remove`d Marker.'); if (_infoWindow != null) { - _infoWindow.close(); + _infoWindow.open(_marker!.map, _marker); + _infoWindowShown = true; + } + } + + @override + void update(gmaps.MarkerOptions options, + {web.HTMLElement? newInfoWindowContent}) { + assert(_marker != null, 'Cannot `update` Marker after calling `remove`.'); + _marker!.options = options; + + if (_infoWindow != null && newInfoWindowContent != null) { + _infoWindow.content = newInfoWindowContent; + } + } +} + +/// A `MarkerController` that wraps a [gmaps.AdvancedMarkerElement] object. +/// +/// [gmaps.AdvancedMarkerElement] is a new class that is +/// replacing [gmaps.Marker]. +class AdvancedMarkerController extends MarkerController< + gmaps.AdvancedMarkerElement, gmaps.AdvancedMarkerElementOptions> { + /// Creates a `AdvancedMarkerController`, which wraps + /// a [gmaps.AdvancedMarkerElement] object. + AdvancedMarkerController({ + required super.marker, + super.infoWindow, + super.consumeTapEvents, + super.onDragStart, + super.onDrag, + super.onDragEnd, + super.onTap, + super.clusterManagerId, + }); + + @override + void initializeMarkerListener({ + required gmaps.AdvancedMarkerElement marker, + required LatLngCallback? onDragStart, + required LatLngCallback? onDrag, + required LatLngCallback? onDragEnd, + required VoidCallback? onTap, + }) { + if (onTap != null) { + marker.onClick.listen((gmaps.MapMouseEvent event) { + onTap.call(); + }); + } + if (onDragStart != null) { + marker.onDragstart.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragStart.call(event.latLng ?? _nullGmapsLatLng); + }); + } + if (onDrag != null) { + marker.onDrag.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDrag.call(event.latLng ?? _nullGmapsLatLng); + }); + } + if (onDragEnd != null) { + marker.onDragend.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragEnd.call(event.latLng ?? _nullGmapsLatLng); + }); + } + } + + @override + void remove() { + if (_marker != null) { _infoWindowShown = false; + + _marker!.remove(); + _marker!.map = null; + _marker = null; } } - /// Show the associated [gmaps.InfoWindow]. - /// - /// This cannot be called after [remove]. + @override void showInfoWindow() { assert(_marker != null, 'Cannot `showInfoWindow` on a `remove`d Marker.'); + if (_infoWindow != null) { _infoWindow.open(_marker!.map, _marker); _infoWindowShown = true; } } + + @override + void update( + gmaps.AdvancedMarkerElementOptions options, { + web.HTMLElement? newInfoWindowContent, + }) { + assert(_marker != null, 'Cannot `update` Marker after calling `remove`.'); + + final gmaps.AdvancedMarkerElement marker = _marker!; + marker.collisionBehavior = options.collisionBehavior; + marker.content = options.content; + marker.gmpDraggable = options.gmpDraggable; + marker.position = options.position; + marker.title = options.title ?? ''; + marker.zIndex = options.zIndex; + + if (_infoWindow != null && newInfoWindowContent != null) { + _infoWindow.content = newInfoWindowContent; + } + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart index 388648a307b..f2c871ae271 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart @@ -17,23 +17,26 @@ import 'types.dart'; /// This class maps [ClusterManager] objects to javascript [MarkerClusterer] /// objects and provides an interface for adding and removing markers from /// clusters. -class ClusterManagersController extends GeometryController { +/// +/// [T] must extend [JSObject]. It's not specified in code because our mocking +/// framework does not support mocking JSObjects. +class ClusterManagersController extends GeometryController { /// Creates a new [ClusterManagersController] instance. /// /// The [stream] parameter is a required [StreamController] used for /// emitting map events. - ClusterManagersController({ - required StreamController> stream, - }) : _streamController = stream, - _clusterManagerIdToMarkerClusterer = - {}; + ClusterManagersController( + {required StreamController> stream}) + : _streamController = stream, + _clusterManagerIdToMarkerClusterer = + >{}; // The stream over which cluster managers broadcast their events final StreamController> _streamController; // A cache of [MarkerClusterer]s indexed by their [ClusterManagerId]. - final Map - _clusterManagerIdToMarkerClusterer; + final Map> + _clusterManagerIdToMarkerClusterer; /// Adds a set of [ClusterManager] objects to the cache. void addClusterManagers(Set clusterManagersToAdd) { @@ -41,13 +44,10 @@ class ClusterManagersController extends GeometryController { } void _addClusterManager(ClusterManager clusterManager) { - final MarkerClusterer markerClusterer = createMarkerClusterer( + final MarkerClusterer markerClusterer = createMarkerClusterer( googleMap, - ( - gmaps.MapMouseEvent event, - MarkerClustererCluster cluster, - gmaps.Map map, - ) => + (gmaps.MapMouseEvent event, MarkerClustererCluster cluster, + gmaps.Map map) => _clusterClicked(clusterManager.clusterManagerId, event, cluster, map), ); @@ -62,7 +62,7 @@ class ClusterManagersController extends GeometryController { } void _removeClusterManager(ClusterManagerId clusterManagerId) { - final MarkerClusterer? markerClusterer = + final MarkerClusterer? markerClusterer = _clusterManagerIdToMarkerClusterer[clusterManagerId]; if (markerClusterer != null) { markerClusterer.clearMarkers(true); @@ -71,10 +71,12 @@ class ClusterManagersController extends GeometryController { _clusterManagerIdToMarkerClusterer.remove(clusterManagerId); } - /// Adds given [gmaps.Marker] to the [MarkerClusterer] with given - /// [ClusterManagerId]. - void addItem(ClusterManagerId clusterManagerId, gmaps.Marker marker) { - final MarkerClusterer? markerClusterer = + /// Adds given markers to the [MarkerClusterer] with given [ClusterManagerId]. + void addItem( + ClusterManagerId clusterManagerId, + T marker, + ) { + final MarkerClusterer? markerClusterer = _clusterManagerIdToMarkerClusterer[clusterManagerId]; if (markerClusterer != null) { markerClusterer.addMarker(marker, true); @@ -82,11 +84,14 @@ class ClusterManagersController extends GeometryController { } } - /// Removes given [gmaps.Marker] from the [MarkerClusterer] with given - /// [ClusterManagerId]. - void removeItem(ClusterManagerId clusterManagerId, gmaps.Marker? marker) { + /// Removes given marker from the [MarkerClusterer] with + /// given [ClusterManagerId]. + void removeItem( + ClusterManagerId clusterManagerId, + T? marker, + ) { if (marker != null) { - final MarkerClusterer? markerClusterer = + final MarkerClusterer? markerClusterer = _clusterManagerIdToMarkerClusterer[clusterManagerId]; if (markerClusterer != null) { markerClusterer.removeMarker(marker, true); @@ -98,25 +103,22 @@ class ClusterManagersController extends GeometryController { /// Returns list of clusters in [MarkerClusterer] with given /// [ClusterManagerId]. List getClusters(ClusterManagerId clusterManagerId) { - final MarkerClusterer? markerClusterer = + final MarkerClusterer? markerClusterer = _clusterManagerIdToMarkerClusterer[clusterManagerId]; if (markerClusterer != null) { return markerClusterer.clusters - .map( - (MarkerClustererCluster cluster) => - _convertCluster(clusterManagerId, cluster), - ) + .map((MarkerClustererCluster cluster) => + _convertCluster(clusterManagerId, cluster)) .toList(); } return []; } void _clusterClicked( - ClusterManagerId clusterManagerId, - gmaps.MapMouseEvent event, - MarkerClustererCluster markerClustererCluster, - gmaps.Map map, - ) { + ClusterManagerId clusterManagerId, + gmaps.MapMouseEvent event, + MarkerClustererCluster markerClustererCluster, + gmaps.Map map) { if (markerClustererCluster.count > 0 && markerClustererCluster.bounds != null) { final Cluster cluster = _convertCluster( @@ -128,22 +130,14 @@ class ClusterManagersController extends GeometryController { } /// Converts [MarkerClustererCluster] to [Cluster]. - Cluster _convertCluster( - ClusterManagerId clusterManagerId, - MarkerClustererCluster markerClustererCluster, - ) { + Cluster _convertCluster(ClusterManagerId clusterManagerId, + MarkerClustererCluster markerClustererCluster) { final LatLng position = gmLatLngToLatLng(markerClustererCluster.position); - final LatLngBounds bounds = gmLatLngBoundsTolatLngBounds( - markerClustererCluster.bounds!, - ); - + final LatLngBounds bounds = + gmLatLngBoundsToLatLngBounds(markerClustererCluster.bounds!); final List markerIds = - markerClustererCluster.markers - .map( - (gmaps.Marker marker) => - MarkerId((marker.get('markerId')! as JSString).toDart), - ) - .toList(); + markerClustererCluster.markers.map(getMarkerId).toList(); + return Cluster( clusterManagerId, markerIds, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart index bc74bca2492..582237c302c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart @@ -13,32 +13,31 @@ import 'dart:js_interop'; import 'package:google_maps/google_maps.dart' as gmaps; /// A typedef representing a callback function for handling cluster tap events. -typedef ClusterClickHandler = - void Function(gmaps.MapMouseEvent, MarkerClustererCluster, gmaps.Map); +typedef ClusterClickHandler = void Function( + gmaps.MapMouseEvent, + MarkerClustererCluster, + gmaps.Map, +); /// The [MarkerClustererOptions] object used to initialize [MarkerClusterer]. /// /// See: https://googlemaps.github.io/js-markerclusterer/interfaces/MarkerClustererOptions.html @JS() @anonymous -extension type MarkerClustererOptions._(JSObject _) implements JSObject { +extension type MarkerClustererOptions._(JSObject _) implements JSObject { /// Constructs a new [MarkerClustererOptions] object. factory MarkerClustererOptions({ gmaps.Map? map, - List? markers, - ClusterClickHandler? onClusterClick, - }) => MarkerClustererOptions._js( - map: map as JSAny?, - markers: markers?.cast().toJS ?? JSArray(), - onClusterClick: - onClusterClick != null - ? ((JSAny event, MarkerClustererCluster cluster, JSAny map) => - onClusterClick( - event as gmaps.MapMouseEvent, - cluster, - map as gmaps.Map, - )) - .toJS + List? markers, + ClusterClickHandler? onClusterClick, + }) => + MarkerClustererOptions._js( + map: map as JSAny?, + markers: markers?.cast().toJS ?? JSArray(), + onClusterClick: onClusterClick != null + ? ((JSAny event, MarkerClustererCluster cluster, JSAny map) => + onClusterClick(event as gmaps.MapMouseEvent, cluster, + map as gmaps.Map)).toJS : null, ); @@ -53,13 +52,13 @@ extension type MarkerClustererOptions._(JSObject _) implements JSObject { @JS('map') external JSAny? get _map; - /// Returns the list of [gmaps.Marker] objects. - List? get markers => _markers?.toDart.cast(); + /// Returns the list of marker objects. + List? get markers => _markers?.toDart.cast(); @JS('markers') external JSArray? get _markers; /// Returns the onClusterClick handler. - ClusterClickHandler? get onClusterClick => + ClusterClickHandler? get onClusterClick => _onClusterClick?.toDart as ClusterClickHandler?; @JS('onClusterClick') external JSExportedDartFunction? get _onClusterClick; @@ -69,14 +68,14 @@ extension type MarkerClustererOptions._(JSObject _) implements JSObject { /// /// https://googlemaps.github.io/js-markerclusterer/classes/Cluster.html @JS('markerClusterer.Cluster') -extension type MarkerClustererCluster._(JSObject _) implements JSObject { +extension type MarkerClustererCluster._(JSObject _) implements JSObject { /// Getter for the cluster marker. - gmaps.Marker get marker => _marker as gmaps.Marker; + T get marker => _marker as T; @JS('marker') external JSAny get _marker; /// List of markers in the cluster. - List get markers => _markers.toDart.cast(); + List get markers => _markers.toDart.cast(); @JS('markers') external JSArray get _markers; @@ -97,7 +96,7 @@ extension type MarkerClustererCluster._(JSObject _) implements JSObject { external void delete(); /// Adds a marker to the cluster. - void push(gmaps.Marker marker) => _push(marker as JSAny); + void push(T marker) => _push(marker as JSAny); @JS('push') external void _push(JSAny marker); } @@ -106,30 +105,29 @@ extension type MarkerClustererCluster._(JSObject _) implements JSObject { /// /// https://googlemaps.github.io/js-markerclusterer/classes/MarkerClusterer.html @JS('markerClusterer.MarkerClusterer') -extension type MarkerClusterer._(JSObject _) implements JSObject { +extension type MarkerClusterer._(JSObject _) implements JSObject { /// Constructs a new [MarkerClusterer] object. - external MarkerClusterer(MarkerClustererOptions options); + external MarkerClusterer(MarkerClustererOptions options); /// Adds a marker to be clustered by the [MarkerClusterer]. - void addMarker(gmaps.Marker marker, bool? noDraw) => - _addMarker(marker as JSAny, noDraw); + void addMarker(T marker, bool? noDraw) => _addMarker(marker as JSAny, noDraw); @JS('addMarker') external void _addMarker(JSAny marker, bool? noDraw); /// Adds a list of markers to be clustered by the [MarkerClusterer]. - void addMarkers(List? markers, bool? noDraw) => + void addMarkers(List? markers, bool? noDraw) => _addMarkers(markers?.cast().toJS, noDraw); @JS('addMarkers') external void _addMarkers(JSArray? markers, bool? noDraw); /// Removes a marker from the [MarkerClusterer]. - bool removeMarker(gmaps.Marker marker, bool? noDraw) => + bool removeMarker(T marker, bool? noDraw) => _removeMarker(marker as JSAny, noDraw); @JS('removeMarker') external bool _removeMarker(JSAny marker, bool? noDraw); /// Removes a list of markers from the [MarkerClusterer]. - bool removeMarkers(List? markers, bool? noDraw) => + bool removeMarkers(List? markers, bool? noDraw) => _removeMarkers(markers?.cast().toJS, noDraw); @JS('removeMarkers') external bool _removeMarkers(JSArray? markers, bool? noDraw); @@ -144,8 +142,8 @@ extension type MarkerClusterer._(JSObject _) implements JSObject { external void onRemove(); /// Returns the list of clusters. - List get clusters => - _clusters.toDart.cast(); + List> get clusters => + _clusters.toDart.cast>(); @JS('clusters') external JSArray get _clusters; @@ -155,13 +153,11 @@ extension type MarkerClusterer._(JSObject _) implements JSObject { /// Creates [MarkerClusterer] object with given [gmaps.Map] and /// [ClusterClickHandler]. -MarkerClusterer createMarkerClusterer( - gmaps.Map map, - ClusterClickHandler onClusterClickHandler, -) { - final MarkerClustererOptions options = MarkerClustererOptions( +MarkerClusterer createMarkerClusterer( + gmaps.Map map, ClusterClickHandler onClusterClickHandler) { + final MarkerClustererOptions options = MarkerClustererOptions( map: map, onClusterClick: onClusterClickHandler, ); - return MarkerClusterer(options); + return MarkerClusterer(options); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index a066aabba51..3889e10fb5d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -5,26 +5,34 @@ part of '../google_maps_flutter_web.dart'; /// This class manages a set of [MarkerController]s associated to a [GoogleMapController]. -class MarkersController extends GeometryController { +/// +/// * [LegacyMarkersController] implements the [MarkersController] for the +/// legacy [gmaps.Marker] class. +/// * [AdvancedMarkersController] implements the [MarkersController] for the +/// advanced [gmaps.AdvancedMarkerElement] class. +/// +/// [T] must extend [JSObject]. It's not specified in code because our mocking +/// framework does not support mocking JSObjects. +abstract class MarkersController extends GeometryController { /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. MarkersController({ required StreamController> stream, - required ClusterManagersController clusterManagersController, - }) : _streamController = stream, - _clusterManagersController = clusterManagersController, - _markerIdToController = {}; + required ClusterManagersController clusterManagersController, + }) : _streamController = stream, + _clusterManagersController = clusterManagersController, + _markerIdToController = >{}; // A cache of [MarkerController]s indexed by their [MarkerId]. - final Map _markerIdToController; + final Map> _markerIdToController; // The stream over which markers broadcast their events final StreamController> _streamController; - final ClusterManagersController _clusterManagersController; + final ClusterManagersController _clusterManagersController; /// Returns the cache of [MarkerController]s. Test only. @visibleForTesting - Map get markers => _markerIdToController; + Map> get markers => _markerIdToController; /// Adds a set of [Marker] objects to the cache. /// @@ -53,53 +61,36 @@ class MarkersController extends GeometryController { } } - final gmaps.Marker? currentMarker = - _markerIdToController[marker.markerId]?.marker; - - final gmaps.MarkerOptions markerOptions = await _markerOptionsFromMarker( - marker, - currentMarker, - ); - - final gmaps.Marker gmMarker = gmaps.Marker(markerOptions); - - gmMarker.set('markerId', marker.markerId.value.toJS); - - if (marker.clusterManagerId != null) { - _clusterManagersController.addItem(marker.clusterManagerId!, gmMarker); - } else { - gmMarker.map = googleMap; - } - - final MarkerController controller = MarkerController( - marker: gmMarker, - clusterManagerId: marker.clusterManagerId, - infoWindow: gmInfoWindow, - consumeTapEvents: marker.consumeTapEvents, - onTap: () { - showMarkerInfoWindow(marker.markerId); - _onMarkerTap(marker.markerId); - }, - onDragStart: (gmaps.LatLng latLng) { - _onMarkerDragStart(marker.markerId, latLng); - }, - onDrag: (gmaps.LatLng latLng) { - _onMarkerDrag(marker.markerId, latLng); - }, - onDragEnd: (gmaps.LatLng latLng) { - _onMarkerDragEnd(marker.markerId, latLng); - }, - ); + final MarkerController? markerController = + _markerIdToController[marker.markerId]; + final T? currentMarker = markerController?.marker; + final O markerOptions = + await _markerOptionsFromMarker(marker, currentMarker); + final MarkerController controller = + await createMarkerController(marker, markerOptions, gmInfoWindow); _markerIdToController[marker.markerId] = controller; } + /// Creates a [MarkerController] for the given [marker]. + /// + /// [markerOptions] contains configuration that should be used to create + /// a [gmaps.Marker] or [gmaps.AdvancedMarkerElement] object. [markersOptions] + /// is either [gmaps.MarkerOptions] or [gmaps.AdvancedMarkerElementOptions]. + /// + /// [gmInfoWindow] is marker's info window to show on tap. + Future> createMarkerController( + Marker marker, + O markerOptions, + gmaps.InfoWindow? gmInfoWindow, + ); + /// Updates a set of [Marker] objects with new options. Future changeMarkers(Set markersToChange) async { await Future.wait(markersToChange.map(_changeMarker)); } Future _changeMarker(Marker marker) async { - final MarkerController? markerController = + final MarkerController? markerController = _markerIdToController[marker.markerId]; if (markerController != null) { final ClusterManagerId? oldClusterManagerId = @@ -111,8 +102,10 @@ class MarkersController extends GeometryController { _removeMarker(marker.markerId); await _addMarker(marker); } else { - final gmaps.MarkerOptions markerOptions = - await _markerOptionsFromMarker(marker, markerController.marker); + final O markerOptions = await _markerOptionsFromMarker( + marker, + markerController.marker, + ); final gmaps.InfoWindowOptions? infoWindow = _infoWindowOptionsFromMarker(marker); markerController.update( @@ -129,7 +122,8 @@ class MarkersController extends GeometryController { } void _removeMarker(MarkerId markerId) { - final MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = + _markerIdToController[markerId]; if (markerController?.clusterManagerId != null) { _clusterManagersController.removeItem( markerController!.clusterManagerId!, @@ -147,7 +141,8 @@ class MarkersController extends GeometryController { /// See also [hideMarkerInfoWindow] and [isInfoWindowShown]. void showMarkerInfoWindow(MarkerId markerId) { _hideAllMarkerInfoWindow(); - final MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = + _markerIdToController[markerId]; markerController?.showInfoWindow(); } @@ -155,7 +150,8 @@ class MarkersController extends GeometryController { /// /// See also [showMarkerInfoWindow] and [isInfoWindowShown]. void hideMarkerInfoWindow(MarkerId markerId) { - final MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = + _markerIdToController[markerId]; markerController?.hideInfoWindow(); } @@ -163,7 +159,8 @@ class MarkersController extends GeometryController { /// /// See also [showMarkerInfoWindow] and [hideMarkerInfoWindow]. bool isInfoWindowShown(MarkerId markerId) { - final MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = + _markerIdToController[markerId]; return markerController?.infoWindowShown ?? false; } @@ -200,12 +197,106 @@ class MarkersController extends GeometryController { void _hideAllMarkerInfoWindow() { _markerIdToController.values - .where( - (MarkerController? controller) => - controller?.infoWindowShown ?? false, - ) - .forEach((MarkerController controller) { - controller.hideInfoWindow(); - }); + .where((MarkerController? controller) => + controller?.infoWindowShown ?? false) + .forEach((MarkerController controller) { + controller.hideInfoWindow(); + }); + } +} + +/// A [MarkersController] for the legacy [gmaps.Marker] class. +class LegacyMarkersController + extends MarkersController { + /// Initialize the markers controller for the legacy [gmaps.Marker] class. + LegacyMarkersController({ + required super.stream, + required super.clusterManagersController, + }); + + @override + Future createMarkerController( + Marker marker, + gmaps.MarkerOptions markerOptions, + gmaps.InfoWindow? gmInfoWindow, + ) async { + final gmaps.Marker gmMarker = gmaps.Marker(markerOptions); + gmMarker.set('markerId', marker.markerId.value.toJS); + + if (marker.clusterManagerId != null) { + _clusterManagersController.addItem(marker.clusterManagerId!, gmMarker); + } else { + gmMarker.map = googleMap; + } + + return LegacyMarkerController( + marker: gmMarker, + clusterManagerId: marker.clusterManagerId, + infoWindow: gmInfoWindow, + consumeTapEvents: marker.consumeTapEvents, + onTap: () { + showMarkerInfoWindow(marker.markerId); + _onMarkerTap(marker.markerId); + }, + onDragStart: (gmaps.LatLng latLng) { + _onMarkerDragStart(marker.markerId, latLng); + }, + onDrag: (gmaps.LatLng latLng) { + _onMarkerDrag(marker.markerId, latLng); + }, + onDragEnd: (gmaps.LatLng latLng) { + _onMarkerDragEnd(marker.markerId, latLng); + }, + ); + } +} + +/// A [MarkersController] for the advanced [gmaps.AdvancedMarkerElement] class. +class AdvancedMarkersController extends MarkersController< + gmaps.AdvancedMarkerElement, gmaps.AdvancedMarkerElementOptions> { + /// Initialize the markers controller for advanced markers + /// ([gmaps.AdvancedMarkerElement]). + AdvancedMarkersController({ + required super.stream, + required super.clusterManagersController, + }); + + @override + Future createMarkerController( + Marker marker, + gmaps.AdvancedMarkerElementOptions markerOptions, + gmaps.InfoWindow? gmInfoWindow, + ) async { + assert(marker is AdvancedMarker, 'Marker must be an AdvancedMarker.'); + + final gmaps.AdvancedMarkerElement gmMarker = + gmaps.AdvancedMarkerElement(markerOptions); + gmMarker.setAttribute('id', marker.markerId.value); + + if (marker.clusterManagerId != null) { + _clusterManagersController.addItem(marker.clusterManagerId!, gmMarker); + } else { + gmMarker.map = googleMap; + } + + return AdvancedMarkerController( + marker: gmMarker, + clusterManagerId: marker.clusterManagerId, + infoWindow: gmInfoWindow, + consumeTapEvents: marker.consumeTapEvents, + onTap: () { + showMarkerInfoWindow(marker.markerId); + _onMarkerTap(marker.markerId); + }, + onDragStart: (gmaps.LatLng latLng) { + _onMarkerDragStart(marker.markerId, latLng); + }, + onDrag: (gmaps.LatLng latLng) { + _onMarkerDrag(marker.markerId, latLng); + }, + onDragEnd: (gmaps.LatLng latLng) { + _onMarkerDragEnd(marker.markerId, latLng); + }, + ); } } 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 fe1f6554088..a8be625ffbb 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 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/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.5.14+3 +version: 0.5.15 environment: sdk: ^3.7.0 From bf9ab89806262ba922ebc79d01d4d7e80c1c96e3 Mon Sep 17 00:00:00 2001 From: Alexander Troshkov <20989940+aednlaxer@users.noreply.github.com> Date: Tue, 12 Aug 2025 15:12:06 +0300 Subject: [PATCH 02/23] Set minimum version of `web` package to 1.0.0 --- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a8be625ffbb..be137070f7e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: google_maps_flutter_platform_interface: ^2.14.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 - web: ">=0.5.1 <2.0.0" + web: ">=1.0.0 <2.0.0" dev_dependencies: flutter_test: From c2f5cb07180e14ffa88bc4abd46c6ca6358af025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 26 Sep 2025 09:59:31 +0800 Subject: [PATCH 03/23] Advanced markers package pr fixes --- .../google_maps_flutter_web/lib/src/google_maps_controller.dart | 2 +- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 a2571af94d3..56cd6f343aa 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 @@ -81,7 +81,7 @@ class GoogleMapController { ), MarkerType.advancedMarker => AdvancedMarkersController( stream: stream, - clusterManagersController: clusterManagersController! + clusterManagersController: _clusterManagersController! as ClusterManagersController, ), }; 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 be137070f7e..a170df9a36c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: google_maps_flutter_platform_interface: ^2.14.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 - web: ">=1.0.0 <2.0.0" + web: ^1.0.0 dev_dependencies: flutter_test: From ee763598396f4ca1d76c1e2dc10c96e05d8c716e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 26 Sep 2025 11:22:55 +0800 Subject: [PATCH 04/23] Unify web package double import --- .../lib/google_maps_flutter_web.dart | 1 - .../lib/src/convert.dart | 58 +++++++++---------- .../lib/src/google_maps_controller.dart | 6 +- .../lib/src/marker.dart | 2 +- .../lib/src/markers.dart | 6 +- .../lib/src/overlay.dart | 14 ++--- 6 files changed, 43 insertions(+), 44 deletions(-) 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 217ee8797dd..0413faaf104 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 @@ -21,7 +21,6 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:sanitize_html/sanitize_html.dart'; import 'package:stream_transform/stream_transform.dart'; import 'package:web/web.dart' as web; -import 'package:web/web.dart'; import 'src/dom_window_extension.dart'; import 'src/google_maps_inspector_web.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 cd3d66f75ab..8d9c6ad129c 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 @@ -12,7 +12,7 @@ final gmaps.LatLngBounds _nullGmapsLatLngBounds = gmaps.LatLngBounds( ); // The TrustedType Policy used by this plugin. Used to sanitize InfoWindow contents. -TrustedTypePolicy? _gmapsTrustedTypePolicy; +web.TrustedTypePolicy? _gmapsTrustedTypePolicy; // A cache for image size Futures to reduce redundant image fetch requests. // This cache should be always cleaned up after marker updates are processed. @@ -270,27 +270,27 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { // Add an outer wrapper to the contents of the infowindow, we need it to listen // to click events... - final HTMLElement container = + final web.HTMLElement container = createDivElement() ..id = 'gmaps-marker-${marker.markerId.value}-infowindow'; if (markerTitle.isNotEmpty) { - final HTMLHeadingElement title = - (document.createElement('h3') as HTMLHeadingElement) + final web.HTMLHeadingElement title = + (web.document.createElement('h3') as web.HTMLHeadingElement) ..className = 'infowindow-title' ..innerText = markerTitle; container.appendChild(title); } if (markerSnippet.isNotEmpty) { - final HTMLElement snippet = + final web.HTMLElement snippet = createDivElement()..className = 'infowindow-snippet'; // Firefox and Safari don't support Trusted Types yet. // See https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory#browser_compatibility - if (window.nullableTrustedTypes != null) { - _gmapsTrustedTypePolicy ??= window.trustedTypes.createPolicy( + if (web.window.nullableTrustedTypes != null) { + _gmapsTrustedTypePolicy ??= web.window.trustedTypes.createPolicy( 'google_maps_flutter_sanitize', - TrustedTypePolicyOptions( + web.TrustedTypePolicyOptions( createHTML: (String html) { return sanitizeHtml(html).toJS; @@ -424,10 +424,10 @@ Future _getBitmapSize(MapBitmap mapBitmap, String url) async { /// /// This method attempts to fetch the image size for a given [url]. Future _fetchBitmapSize(String url) async { - final HTMLImageElement image = HTMLImageElement()..src = url; + final web.HTMLImageElement image = web.HTMLImageElement()..src = url; // Wait for the onLoad or onError event. - await Future.any(>[image.onLoad.first, image.onError.first]); + await Future.any(>[image.onLoad.first, image.onError.first]); if (image.width == 0 || image.height == 0) { // Complete with null for invalid images. @@ -450,7 +450,7 @@ void _cleanUpBitmapConversionCaches() { /// Converts a [BitmapDescriptor] into a [Node] that can be used as /// [AdvancedMarker]'s icon. -Future _advancedMarkerIconFromBitmapDescriptor( +Future _advancedMarkerIconFromBitmapDescriptor( BitmapDescriptor bitmapDescriptor, { required double? opacity, required bool isVisible, @@ -473,7 +473,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( case final CircleGlyph circleGlyph: options.glyphColor = _getCssColor(circleGlyph.color); case final TextGlyph textGlyph: - final web.Element element = document.createElement('p'); + final web.Element element = web.document.createElement('p'); element.innerHTML = textGlyph.text.toJS; if (textGlyph.textColor != null) { element.setAttribute( @@ -483,7 +483,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( } options.glyph = element; case final BitmapGlyph bitmapGlyph: - final Node? glyphBitmap = await _advancedMarkerIconFromBitmapDescriptor( + final web.Node? glyphBitmap = await _advancedMarkerIconFromBitmapDescriptor( bitmapGlyph.bitmap, // Always opaque, opacity is handled by the parent marker. opacity: 1.0, @@ -497,7 +497,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( } final gmaps.PinElement pinElement = gmaps.PinElement(options); - final HTMLElement htmlElement = pinElement.element; + final web.HTMLElement htmlElement = pinElement.element; htmlElement.style ..visibility = isVisible ? 'visible' : 'hidden' ..opacity = opacity?.toString() ?? '1.0' @@ -510,10 +510,10 @@ Future _advancedMarkerIconFromBitmapDescriptor( (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( bytesMapBitmap.byteData.hashCode, () { - final Blob blob = Blob( + final web.Blob blob = web.Blob( [bytesMapBitmap.byteData.toJS].toJS, ); - return URL.createObjectURL(blob as JSObject); + return web.URL.createObjectURL(blob as JSObject); }, ), (final AssetMapBitmap assetMapBitmap) => ui_web.assetManager.getAssetUrl( @@ -522,7 +522,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( _ => throw UnimplementedError(), }; - final web.Element icon = document.createElement('img') + final web.Element icon = web.document.createElement('img') ..setAttribute('src', url); final gmaps.Size? size = switch (bitmapDescriptor.bitmapScaling) { @@ -546,7 +546,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( assert(iconConfig.length >= 2); // iconConfig[2] contains the DPIs of the screen, but that information is // already encoded in the iconConfig[1] - final web.Element icon = document.createElement('img')..setAttribute( + final web.Element icon = web.document.createElement('img')..setAttribute( 'src', ui_web.assetManager.getAssetUrl(iconConfig[1]! as String), ); @@ -563,7 +563,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( // Grab the bytes, and put them into a blob. final List bytes = iconConfig[1]! as List; // Create a Blob from bytes, but let the browser figure out the encoding. - final Blob blob; + final web.Blob blob; assert( bytes is Uint8List, @@ -572,10 +572,10 @@ Future _advancedMarkerIconFromBitmapDescriptor( // TODO(ditman): Improve this conversion // See https://github.com/dart-lang/web/issues/180 - blob = Blob([(bytes as Uint8List).toJS].toJS); + blob = web.Blob([(bytes as Uint8List).toJS].toJS); - final web.Element icon = document.createElement('img') - ..setAttribute('src', URL.createObjectURL(blob as JSObject)); + final web.Element icon = web.document.createElement('img') + ..setAttribute('src', web.URL.createObjectURL(blob as JSObject)); final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); _setIconStyle( @@ -602,10 +602,10 @@ Future _gmIconFromBitmapDescriptor( (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( bytesMapBitmap.byteData.hashCode, () { - final Blob blob = Blob( + final web.Blob blob = web.Blob( [bytesMapBitmap.byteData.toJS].toJS, ); - return URL.createObjectURL(blob as JSObject); + return web.URL.createObjectURL(blob as JSObject); }, ), (final AssetMapBitmap assetMapBitmap) => ui_web.assetManager.getAssetUrl( @@ -651,7 +651,7 @@ Future _gmIconFromBitmapDescriptor( // Grab the bytes, and put them into a blob final List bytes = iconConfig[1]! as List; // Create a Blob from bytes, but let the browser figure out the encoding - final Blob blob; + final web.Blob blob; assert( bytes is Uint8List, @@ -660,9 +660,9 @@ Future _gmIconFromBitmapDescriptor( // TODO(ditman): Improve this conversion // See https://github.com/dart-lang/web/issues/180 - blob = Blob([(bytes as Uint8List).toJS].toJS); + blob = web.Blob([(bytes as Uint8List).toJS].toJS); - icon = gmaps.Icon()..url = URL.createObjectURL(blob as JSObject); + icon = gmaps.Icon()..url = web.URL.createObjectURL(blob as JSObject); final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); if (size != null) { @@ -974,10 +974,10 @@ String urlFromMapBitmap(MapBitmap mapBitmap) { (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( bytesMapBitmap.byteData.hashCode, () { - final Blob blob = Blob( + final web.Blob blob = web.Blob( [bytesMapBitmap.byteData.toJS].toJS, ); - return URL.createObjectURL(blob as JSObject); + return web.URL.createObjectURL(blob as JSObject); }, ), (final AssetMapBitmap assetMapBitmap) => ui_web.assetManager.getAssetUrl( 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 56cd6f343aa..e2dc151d024 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 @@ -7,7 +7,7 @@ part of '../google_maps_flutter_web.dart'; /// Type used when passing an override to the _createMap function. @visibleForTesting typedef DebugCreateMapFunction = - gmaps.Map Function(HTMLElement div, gmaps.MapOptions options); + gmaps.Map Function(web.HTMLElement div, gmaps.MapOptions options); /// Type used when passing an override to the _setOptions function. @visibleForTesting @@ -140,7 +140,7 @@ class GoogleMapController { // The Flutter widget that contains the rendered Map. HtmlElementView? _widget; - late HTMLElement _div; + late web.HTMLElement _div; /// The Flutter widget that will contain the rendered Map. Used for caching. Widget? get widget { @@ -230,7 +230,7 @@ class GoogleMapController { DebugCreateMapFunction? _overrideCreateMap; DebugSetOptionsFunction? _overrideSetOptions; - gmaps.Map _createMap(HTMLElement div, gmaps.MapOptions options) { + gmaps.Map _createMap(web.HTMLElement div, gmaps.MapOptions options) { if (_overrideCreateMap != null) { return _overrideCreateMap!(div, options); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 468e0feb27c..8a880c2c1cf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -64,7 +64,7 @@ abstract class MarkerController { /// This cannot be called after [remove]. void update( O options, { - HTMLElement? newInfoWindowContent, + web.HTMLElement? newInfoWindowContent, }); /// Initializes the listener for the wrapped marker object. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 3889e10fb5d..4a47a1464fb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -51,8 +51,8 @@ abstract class MarkersController extends GeometryController { // Google Maps' JS SDK does not have a click event on the InfoWindow, so // we make one... if (infoWindowOptions.content != null && - infoWindowOptions.content is HTMLElement) { - final HTMLElement content = infoWindowOptions.content! as HTMLElement; + infoWindowOptions.content is web.HTMLElement) { + final web.HTMLElement content = infoWindowOptions.content! as web.HTMLElement; content.onclick = (JSAny? _) { @@ -110,7 +110,7 @@ abstract class MarkersController extends GeometryController { _infoWindowOptionsFromMarker(marker); markerController.update( markerOptions, - newInfoWindowContent: infoWindow?.content as HTMLElement?, + newInfoWindowContent: infoWindow?.content as web.HTMLElement?, ); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart index 9a5983e0acf..7c293f1110b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart @@ -32,17 +32,17 @@ class TileOverlayController { } /// Renders a Tile for gmaps; delegating to the configured [TileProvider]. - HTMLElement? _getTile( + web.HTMLElement? _getTile( gmaps.Point? tileCoord, num? zoom, - Document? ownerDocument, + web.Document? ownerDocument, ) { if (_tileOverlay.tileProvider == null) { return null; } - final HTMLImageElement img = - ownerDocument!.createElement('img') as HTMLImageElement; + final web.HTMLImageElement img = + ownerDocument!.createElement('img') as web.HTMLImageElement; img.width = img.height = logicalTileSize; img.hidden = true.toJS; img.setAttribute('decoding', 'async'); @@ -54,14 +54,14 @@ class TileOverlayController { return; } // Using img lets us take advantage of native decoding. - final String src = URL.createObjectURL( - Blob([tile.data!.toJS].toJS) as JSObject, + final String src = web.URL.createObjectURL( + web.Blob([tile.data!.toJS].toJS) as JSObject, ); img.src = src; img.onload = (JSAny? _) { img.hidden = false.toJS; - URL.revokeObjectURL(src); + web.URL.revokeObjectURL(src); }.toJS; }); From 6c8a1581e57d9cc67403f47a49a99524ed84d0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 26 Sep 2025 15:32:53 +0800 Subject: [PATCH 05/23] Raise google maps flutter web package to version 0.6.0 --- .../google_maps_flutter/google_maps_flutter_web/CHANGELOG.md | 2 +- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 eec6e1b8ecd..771e6ae7273 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.5.15 +## 0.6.0 * Adds Advanced markers support. 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 a170df9a36c..de1f2fcd5b6 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 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/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.5.15 +version: 0.6.0 environment: sdk: ^3.7.0 From cf9c3a58c179df100555f57ff9faf78cba9e9631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 26 Sep 2025 16:20:47 +0800 Subject: [PATCH 06/23] Fix get marker id function in google maps flutter web package --- .../google_maps_flutter_web/lib/src/convert.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 8d9c6ad129c..b1142d869ff 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 @@ -724,11 +724,13 @@ Future _markerOptionsFromMarker( /// Gets marker Id from a [marker] object. MarkerId getMarkerId(Object? marker) { final JSObject object = marker! as JSObject; - final gmaps.MVCObject mapObject = marker as gmaps.MVCObject; if (object.isA()) { + final gmaps.MVCObject mapObject = marker as gmaps.MVCObject; return MarkerId((mapObject.get('markerId')! as JSString).toDart); } else if (object.isA()) { - return MarkerId((mapObject.get('id')! as JSString).toDart); + final gmaps.AdvancedMarkerElement element = + marker as gmaps.AdvancedMarkerElement; + return MarkerId(element.id); } else { throw ArgumentError( 'Must be either a gmaps.Marker or a gmaps.AdvancedMarkerElement', From 45b32d3e9bf32e42460a338fe80bf2e7e6d20209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 26 Sep 2025 16:40:40 +0800 Subject: [PATCH 07/23] Updated license headers in the google maps flutter web package --- .../example/integration_test/advanced_marker_test.dart | 2 +- .../example/integration_test/advanced_markers_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart index 95ee0f0deed..f0697dfe13e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart index 61cb1deff60..321de172a13 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. From 97b24fdba106324d4c02b7b36612f6d2d009cc23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 17 Oct 2025 15:44:12 +0800 Subject: [PATCH 08/23] Minor refactoring to advanced markers for web --- .../google_maps_flutter_web/lib/src/convert.dart | 2 +- .../google_maps_flutter_web/lib/src/marker.dart | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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 b1142d869ff..bf4a63467a7 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 @@ -474,7 +474,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( options.glyphColor = _getCssColor(circleGlyph.color); case final TextGlyph textGlyph: final web.Element element = web.document.createElement('p'); - element.innerHTML = textGlyph.text.toJS; + element.text = textGlyph.text; if (textGlyph.textColor != null) { element.setAttribute( 'style', diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 8a880c2c1cf..576bc54f1ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -24,7 +24,7 @@ abstract class MarkerController { _infoWindow = infoWindow, _consumeTapEvents = consumeTapEvents, _clusterManagerId = clusterManagerId { - initializeMarkerListener( + addMarkerListener( marker: marker, onDragStart: onDragStart, onDrag: onDrag, @@ -68,7 +68,7 @@ abstract class MarkerController { }); /// Initializes the listener for the wrapped marker object. - void initializeMarkerListener({ + void addMarkerListener({ required T marker, required LatLngCallback? onDragStart, required LatLngCallback? onDrag, @@ -115,7 +115,7 @@ class LegacyMarkerController }); @override - void initializeMarkerListener({ + void addMarkerListener({ required gmaps.Marker marker, required LatLngCallback? onDragStart, required LatLngCallback? onDrag, @@ -197,7 +197,7 @@ class AdvancedMarkerController extends MarkerController< }); @override - void initializeMarkerListener({ + void addMarkerListener({ required gmaps.AdvancedMarkerElement marker, required LatLngCallback? onDragStart, required LatLngCallback? onDrag, From 4bae6f996634261c513178b4dc83c0393dce200e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 17 Oct 2025 17:46:58 +0800 Subject: [PATCH 09/23] Handle listeners for web markers --- .../lib/src/marker.dart | 89 +++++++++++++------ 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 576bc54f1ab..d5dfd5c7dab 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -20,10 +20,10 @@ abstract class MarkerController { LatLngCallback? onDragEnd, VoidCallback? onTap, ClusterManagerId? clusterManagerId, - }) : _marker = marker, - _infoWindow = infoWindow, - _consumeTapEvents = consumeTapEvents, - _clusterManagerId = clusterManagerId { + }) : _marker = marker, + _infoWindow = infoWindow, + _consumeTapEvents = consumeTapEvents, + _clusterManagerId = clusterManagerId { addMarkerListener( marker: marker, onDragStart: onDragStart, @@ -62,10 +62,7 @@ abstract class MarkerController { /// Updates the options of the wrapped marker object. /// /// This cannot be called after [remove]. - void update( - O options, { - web.HTMLElement? newInfoWindowContent, - }); + void update(O options, {web.HTMLElement? newInfoWindowContent}); /// Initializes the listener for the wrapped marker object. void addMarkerListener({ @@ -114,6 +111,14 @@ class LegacyMarkerController super.clusterManagerId, }); + /// List of active stream subscriptions for marker events. + /// + /// This list keeps track of all event subscriptions created for the marker, + /// including taps and different drag events. + /// These subscriptions should be disposed when the controller is disposed. + final List> _subscriptions = + >[]; + @override void addMarkerListener({ required gmaps.Marker marker, @@ -123,27 +128,29 @@ class LegacyMarkerController required VoidCallback? onTap, }) { if (onTap != null) { - marker.onClick.listen((gmaps.MapMouseEvent event) { - onTap.call(); - }); + _subscriptions.add( + marker.onClick.listen((gmaps.MapMouseEvent event) { + onTap.call(); + }), + ); } if (onDragStart != null) { - marker.onDragstart.listen((gmaps.MapMouseEvent event) { + _subscriptions.add(marker.onDragstart.listen((gmaps.MapMouseEvent event) { marker.position = event.latLng; onDragStart.call(event.latLng ?? _nullGmapsLatLng); - }); + })); } if (onDrag != null) { - marker.onDrag.listen((gmaps.MapMouseEvent event) { + _subscriptions.add(marker.onDrag.listen((gmaps.MapMouseEvent event) { marker.position = event.latLng; onDrag.call(event.latLng ?? _nullGmapsLatLng); - }); + })); } if (onDragEnd != null) { - marker.onDragend.listen((gmaps.MapMouseEvent event) { + _subscriptions.add(marker.onDragend.listen((gmaps.MapMouseEvent event) { marker.position = event.latLng; onDragEnd.call(event.latLng ?? _nullGmapsLatLng); - }); + })); } } @@ -153,6 +160,11 @@ class LegacyMarkerController _infoWindowShown = false; _marker!.map = null; _marker = null; + + for (final StreamSubscription sub in _subscriptions) { + sub.cancel(); + } + _subscriptions.clear(); } } @@ -166,8 +178,10 @@ class LegacyMarkerController } @override - void update(gmaps.MarkerOptions options, - {web.HTMLElement? newInfoWindowContent}) { + void update( + gmaps.MarkerOptions options, { + web.HTMLElement? newInfoWindowContent, + }) { assert(_marker != null, 'Cannot `update` Marker after calling `remove`.'); _marker!.options = options; @@ -181,8 +195,12 @@ class LegacyMarkerController /// /// [gmaps.AdvancedMarkerElement] is a new class that is /// replacing [gmaps.Marker]. -class AdvancedMarkerController extends MarkerController< - gmaps.AdvancedMarkerElement, gmaps.AdvancedMarkerElementOptions> { +class AdvancedMarkerController + extends + MarkerController< + gmaps.AdvancedMarkerElement, + gmaps.AdvancedMarkerElementOptions + > { /// Creates a `AdvancedMarkerController`, which wraps /// a [gmaps.AdvancedMarkerElement] object. AdvancedMarkerController({ @@ -196,6 +214,14 @@ class AdvancedMarkerController extends MarkerController< super.clusterManagerId, }); + /// List of active stream subscriptions for marker events. + /// + /// This list keeps track of all event subscriptions created for the marker, + /// including taps and different drag events. + /// These subscriptions should be disposed when the controller is disposed. + final List> _subscriptions = + >[]; + @override void addMarkerListener({ required gmaps.AdvancedMarkerElement marker, @@ -205,27 +231,27 @@ class AdvancedMarkerController extends MarkerController< required VoidCallback? onTap, }) { if (onTap != null) { - marker.onClick.listen((gmaps.MapMouseEvent event) { + _subscriptions.add(marker.onClick.listen((gmaps.MapMouseEvent event) { onTap.call(); - }); + })); } if (onDragStart != null) { - marker.onDragstart.listen((gmaps.MapMouseEvent event) { + _subscriptions.add(marker.onDragstart.listen((gmaps.MapMouseEvent event) { marker.position = event.latLng; onDragStart.call(event.latLng ?? _nullGmapsLatLng); - }); + })); } if (onDrag != null) { - marker.onDrag.listen((gmaps.MapMouseEvent event) { + _subscriptions.add(marker.onDrag.listen((gmaps.MapMouseEvent event) { marker.position = event.latLng; onDrag.call(event.latLng ?? _nullGmapsLatLng); - }); + })); } if (onDragEnd != null) { - marker.onDragend.listen((gmaps.MapMouseEvent event) { + _subscriptions.add(marker.onDragend.listen((gmaps.MapMouseEvent event) { marker.position = event.latLng; onDragEnd.call(event.latLng ?? _nullGmapsLatLng); - }); + })); } } @@ -237,6 +263,11 @@ class AdvancedMarkerController extends MarkerController< _marker!.remove(); _marker!.map = null; _marker = null; + + for (final StreamSubscription sub in _subscriptions) { + sub.cancel(); + } + _subscriptions.clear(); } } From 770f2813cf9f2607ac7e1ffb434019fa1974fc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 17 Oct 2025 17:49:01 +0800 Subject: [PATCH 10/23] Code formatting --- .../advanced_marker_test.dart | 42 +-- .../advanced_markers_test.dart | 290 +++++++++--------- .../google_maps_controller_test.dart | 10 +- .../google_maps_controller_test.mocks.dart | 283 ++++++++--------- .../google_maps_plugin_test.mocks.dart | 239 ++++++++------- .../example/integration_test/marker_test.dart | 28 +- .../integration_test/markers_test.dart | 9 +- .../integration_test/overlays_test.mocks.dart | 19 +- .../lib/src/convert.dart | 8 +- .../lib/src/google_maps_controller.dart | 27 +- .../lib/src/google_maps_inspector_web.dart | 23 +- .../lib/src/marker.dart | 70 +++-- .../lib/src/marker_clustering.dart | 55 ++-- .../lib/src/marker_clustering_js_interop.dart | 29 +- .../lib/src/markers.dart | 45 ++- 15 files changed, 634 insertions(+), 543 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart index f0697dfe13e..46f1a461b6f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart @@ -56,11 +56,7 @@ void main() { AdvancedMarkerController(marker: marker, onTap: onTap); // Trigger a click event... - gmaps.event.trigger( - marker, - 'click', - gmaps.MapMouseEvent(), - ); + gmaps.event.trigger(marker, 'click', gmaps.MapMouseEvent()); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); @@ -106,8 +102,9 @@ void main() { }); testWidgets('update', (WidgetTester tester) async { - final AdvancedMarkerController controller = - AdvancedMarkerController(marker: marker); + final AdvancedMarkerController controller = AdvancedMarkerController( + marker: marker, + ); final gmaps.AdvancedMarkerElementOptions options = gmaps.AdvancedMarkerElementOptions() ..collisionBehavior = @@ -132,10 +129,12 @@ void main() { expect((position as gmaps.LatLngLiteral).lng, equals(54)); }); - testWidgets('infoWindow null, showInfoWindow.', - (WidgetTester tester) async { - final AdvancedMarkerController controller = - AdvancedMarkerController(marker: marker); + testWidgets('infoWindow null, showInfoWindow.', ( + WidgetTester tester, + ) async { + final AdvancedMarkerController controller = AdvancedMarkerController( + marker: marker, + ); controller.showInfoWindow(); @@ -179,8 +178,10 @@ void main() { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); final gmaps.Map map = gmaps.Map(createDivElement()); marker.map = map; - controller = - AdvancedMarkerController(marker: marker, infoWindow: infoWindow); + controller = AdvancedMarkerController( + marker: marker, + infoWindow: infoWindow, + ); }); testWidgets('drops gmaps instance', (WidgetTester tester) async { @@ -189,8 +190,9 @@ void main() { expect(controller.marker, isNull); }); - testWidgets('cannot call update after remove', - (WidgetTester tester) async { + testWidgets('cannot call update after remove', ( + WidgetTester tester, + ) async { final gmaps.AdvancedMarkerElementOptions options = gmaps.AdvancedMarkerElementOptions()..gmpDraggable = true; @@ -201,8 +203,9 @@ void main() { }, throwsAssertionError); }); - testWidgets('cannot call showInfoWindow after remove', - (WidgetTester tester) async { + testWidgets('cannot call showInfoWindow after remove', ( + WidgetTester tester, + ) async { controller.remove(); expect(() { @@ -210,8 +213,9 @@ void main() { }, throwsAssertionError); }); - testWidgets('cannot call hideInfoWindow after remove', - (WidgetTester tester) async { + testWidgets('cannot call hideInfoWindow after remove', ( + WidgetTester tester, + ) async { controller.remove(); expect(() { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart index 321de172a13..31e578290e8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart @@ -26,10 +26,13 @@ void main() { group('MarkersController', () { late StreamController> events; - late MarkersController controller; + late MarkersController< + gmaps.AdvancedMarkerElement, + gmaps.AdvancedMarkerElementOptions + > + controller; late ClusterManagersController - clusterManagersController; + clusterManagersController; late gmaps.Map map; setUp(() { @@ -37,7 +40,8 @@ void main() { clusterManagersController = ClusterManagersController( - stream: events); + stream: events, + ); controller = AdvancedMarkersController( stream: events, clusterManagersController: clusterManagersController, @@ -102,47 +106,45 @@ void main() { }); testWidgets( - 'changeMarkers resets marker position if not passed when updating!', - (WidgetTester tester) async { - gmaps.AdvancedMarkerElement? marker; - gmaps.LatLngLiteral? position; - - final Set markers = { - AdvancedMarker( - markerId: const MarkerId('1'), - position: const LatLng(42, 54), - ), - }; - await controller.addMarkers(markers); - - marker = controller.markers[const MarkerId('1')]?.marker; - expect(marker, isNotNull); - expect(marker!.gmpDraggable, isFalse); - - position = marker.position! as gmaps.LatLngLiteral; - expect(position, isNotNull); - expect(position.lat, equals(42)); - expect(position.lng, equals(54)); - - // Update the marker without position. - final Set updatedMarkers = { - AdvancedMarker( - markerId: const MarkerId('1'), - draggable: true, - ), - }; - await controller.changeMarkers(updatedMarkers); - expect(controller.markers.length, 1); - - marker = controller.markers[const MarkerId('1')]?.marker; - expect(marker, isNotNull); - expect(marker!.gmpDraggable, isTrue); + 'changeMarkers resets marker position if not passed when updating!', + (WidgetTester tester) async { + gmaps.AdvancedMarkerElement? marker; + gmaps.LatLngLiteral? position; - position = marker.position! as gmaps.LatLngLiteral; - expect(position, isNotNull); - expect(position.lat, equals(0)); - expect(position.lng, equals(0)); - }); + final Set markers = { + AdvancedMarker( + markerId: const MarkerId('1'), + position: const LatLng(42, 54), + ), + }; + await controller.addMarkers(markers); + + marker = controller.markers[const MarkerId('1')]?.marker; + expect(marker, isNotNull); + expect(marker!.gmpDraggable, isFalse); + + position = marker.position! as gmaps.LatLngLiteral; + expect(position, isNotNull); + expect(position.lat, equals(42)); + expect(position.lng, equals(54)); + + // Update the marker without position. + final Set updatedMarkers = { + AdvancedMarker(markerId: const MarkerId('1'), draggable: true), + }; + await controller.changeMarkers(updatedMarkers); + expect(controller.markers.length, 1); + + marker = controller.markers[const MarkerId('1')]?.marker; + expect(marker, isNotNull); + expect(marker!.gmpDraggable, isTrue); + + position = marker.position! as gmaps.LatLngLiteral; + expect(position, isNotNull); + expect(position.lat, equals(0)); + expect(position.lng, equals(0)); + }, + ); testWidgets('removeMarkers', (WidgetTester tester) async { final Set markers = { @@ -190,8 +192,9 @@ void main() { expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); }); - testWidgets('only single InfoWindow is visible', - (WidgetTester tester) async { + testWidgets('only single InfoWindow is visible', ( + WidgetTester tester, + ) async { final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), @@ -218,23 +221,22 @@ void main() { expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isTrue); }); - testWidgets('markers with custom asset icon work', - (WidgetTester tester) async { + testWidgets('markers with custom asset icon work', ( + WidgetTester tester, + ) async { final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), - icon: AssetMapBitmap( - 'assets/red_square.png', - imagePixelRatio: 1.0, - ), + icon: AssetMapBitmap('assets/red_square.png', imagePixelRatio: 1.0), ), }; await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + final HTMLImageElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLImageElement?; expect(icon, isNotNull); final String assetUrl = icon!.src; @@ -245,23 +247,22 @@ void main() { expect(icon.style.height, '48px'); }); - testWidgets('markers with custom asset icon and pixel ratio work', - (WidgetTester tester) async { + testWidgets('markers with custom asset icon and pixel ratio work', ( + WidgetTester tester, + ) async { final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), - icon: AssetMapBitmap( - 'assets/red_square.png', - imagePixelRatio: 2.0, - ), + icon: AssetMapBitmap('assets/red_square.png', imagePixelRatio: 2.0), ), }; await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + final HTMLImageElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLImageElement?; expect(icon, isNotNull); final String assetUrl = icon!.src; @@ -273,24 +274,27 @@ void main() { expect(icon.style.height, '24px'); }); - testWidgets('markers with custom asset icon with width and height work', - (WidgetTester tester) async { + testWidgets('markers with custom asset icon with width and height work', ( + WidgetTester tester, + ) async { final Set markers = { AdvancedMarker( - markerId: const MarkerId('1'), - icon: AssetMapBitmap( - 'assets/red_square.png', - imagePixelRatio: 2.0, - width: 64, - height: 64, - )), + markerId: const MarkerId('1'), + icon: AssetMapBitmap( + 'assets/red_square.png', + imagePixelRatio: 2.0, + width: 64, + height: 64, + ), + ), }; await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + final HTMLImageElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLImageElement?; expect(icon, isNotNull); final String assetUrl = icon!.src; @@ -302,22 +306,25 @@ void main() { expect(icon.style.height, '64px'); }); - testWidgets('markers with missing asset icon should not set size', - (WidgetTester tester) async { + testWidgets('markers with missing asset icon should not set size', ( + WidgetTester tester, + ) async { final Set markers = { AdvancedMarker( - markerId: const MarkerId('1'), - icon: AssetMapBitmap( - 'assets/broken_asset_name.png', - imagePixelRatio: 2.0, - )), + markerId: const MarkerId('1'), + icon: AssetMapBitmap( + 'assets/broken_asset_name.png', + imagePixelRatio: 2.0, + ), + ), }; await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + final HTMLImageElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLImageElement?; expect(icon, isNotNull); final String assetUrl = icon!.src; @@ -328,8 +335,9 @@ void main() { expect(icon.style.height, isEmpty); }); - testWidgets('markers with custom bitmap icon work', - (WidgetTester tester) async { + testWidgets('markers with custom bitmap icon work', ( + WidgetTester tester, + ) async { final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); final Set markers = { AdvancedMarker( @@ -344,8 +352,9 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + final HTMLImageElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLImageElement?; expect(icon, isNotNull); final String blobUrl = icon!.src; @@ -367,24 +376,23 @@ void main() { expect(icon.style.height, '${expectedSize}px'); }); - testWidgets('markers with custom bitmap icon and pixel ratio work', - (WidgetTester tester) async { + testWidgets('markers with custom bitmap icon and pixel ratio work', ( + WidgetTester tester, + ) async { final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), - icon: BytesMapBitmap( - bytes, - imagePixelRatio: 1, - ), + icon: BytesMapBitmap(bytes, imagePixelRatio: 1), ), }; await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + final HTMLImageElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLImageElement?; expect(icon, isNotNull); // Icon size is 16x16 pixels, this should be automatically read from the @@ -394,32 +402,31 @@ void main() { expect(icon.style.height, '16px'); }); - testWidgets('markers with custom bitmap icon pass size to sdk', - (WidgetTester tester) async { + testWidgets('markers with custom bitmap icon pass size to sdk', ( + WidgetTester tester, + ) async { final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), - icon: BytesMapBitmap( - bytes, - width: 20, - height: 30, - ), + icon: BytesMapBitmap(bytes, width: 20, height: 30), ), }; await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; + final HTMLImageElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLImageElement?; expect(icon, isNotNull); expect(icon!.style.width, '20px'); expect(icon.style.height, '30px'); }); - testWidgets('markers created with pin config and colored glyph work', - (WidgetTester widgetTester) async { + testWidgets('markers created with pin config and colored glyph work', ( + WidgetTester widgetTester, + ) async { final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), @@ -433,19 +440,23 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLDivElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; + final HTMLDivElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLDivElement?; expect(icon, isNotNull); // Query nodes and check colors. This is a bit fragile as it depends on // the implementation details of the icon which is not part of the public // API. - final NodeList backgroundNodes = - icon!.querySelectorAll("[class*='maps-pin-view-background']"); - final NodeList borderNodes = - icon.querySelectorAll("[class*='maps-pin-view-border']"); - final NodeList glyphNodes = - icon.querySelectorAll("[class*='maps-pin-view-default-glyph']"); + final NodeList backgroundNodes = icon!.querySelectorAll( + "[class*='maps-pin-view-background']", + ); + final NodeList borderNodes = icon.querySelectorAll( + "[class*='maps-pin-view-border']", + ); + final NodeList glyphNodes = icon.querySelectorAll( + "[class*='maps-pin-view-default-glyph']", + ); expect(backgroundNodes.length, 1); expect(borderNodes.length, 1); @@ -471,26 +482,25 @@ void main() { ); }); - testWidgets('markers created with text glyph work', - (WidgetTester widgetTester) async { + testWidgets('markers created with text glyph work', ( + WidgetTester widgetTester, + ) async { final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: BitmapDescriptor.pinConfig( backgroundColor: Colors.black, borderColor: Colors.black, - glyph: const TextGlyph( - text: 'Hey', - textColor: Color(0xFF0000FF), - ), + glyph: const TextGlyph(text: 'Hey', textColor: Color(0xFF0000FF)), ), ), }; await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLDivElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; + final HTMLDivElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLDivElement?; expect(icon, isNotNull); // Query pin nodes and find text element. This is a bit fragile as it @@ -515,8 +525,9 @@ void main() { ); }); - testWidgets('markers created with bitmap glyph work', - (WidgetTester widgetTester) async { + testWidgets('markers created with bitmap glyph work', ( + WidgetTester widgetTester, + ) async { final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), @@ -525,9 +536,7 @@ void main() { borderColor: Colors.black, glyph: BitmapGlyph( bitmap: await BitmapDescriptor.asset( - const ImageConfiguration( - size: Size.square(12), - ), + const ImageConfiguration(size: Size.square(12)), 'assets/red_square.png', ), ), @@ -537,8 +546,9 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLDivElement? icon = controller - .markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; + final HTMLDivElement? icon = + controller.markers[const MarkerId('1')]?.marker?.content + as HTMLDivElement?; expect(icon, isNotNull); // Query pin nodes and find text element. This is a bit fragile as it @@ -563,8 +573,9 @@ void main() { ); }); - testWidgets('InfoWindow snippet can have links', - (WidgetTester tester) async { + testWidgets('InfoWindow snippet can have links', ( + WidgetTester tester, + ) async { final Set markers = { AdvancedMarker( markerId: const MarkerId('1'), @@ -578,17 +589,19 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLElement? content = controller - .markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; + final HTMLElement? content = + controller.markers[const MarkerId('1')]?.infoWindow?.content + as HTMLElement?; expect(content, isNotNull); final String innerHtml = (content!.innerHTML as JSString).toDart; expect(innerHtml, contains('title for test')); expect( - innerHtml, - contains( - 'Go to Google >>>', - )); + innerHtml, + contains( + 'Go to Google >>>', + ), + ); }); testWidgets('InfoWindow content is clickable', (WidgetTester tester) async { @@ -605,8 +618,9 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLElement? content = controller - .markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; + final HTMLElement? content = + controller.markers[const MarkerId('1')]?.infoWindow?.content + as HTMLElement?; content?.click(); 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 f97119abc43..a6192e76f2f 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 @@ -493,7 +493,7 @@ void main() { zoomControlsEnabled: true, mapId: _kMapId, fortyFiveDegreeImageryEnabled: false, - ) + ), ); controller.debugSetOverrides( createMap: (_, gmaps.MapOptions options) { @@ -508,9 +508,11 @@ void main() { expect(capturedOptions!.mapTypeId, gmaps.MapTypeId.SATELLITE); expect(capturedOptions!.zoomControl, true); expect(capturedOptions!.mapId, _kMapId); - expect(capturedOptions!.gestureHandling, 'auto', - reason: - 'by default the map handles zoom/pan gestures internally'); + expect( + capturedOptions!.gestureHandling, + 'auto', + reason: 'by default the map handles zoom/pan gestures internally', + ); expect(capturedOptions!.rotateControl, false); expect(capturedOptions!.tilt, 0); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index 2b4f9e10f3d..4e9c3782fec 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -30,7 +30,7 @@ import 'google_maps_controller_test.dart' as _i5; class _FakeMarkerController_0 extends _i1.SmartFake implements _i2.MarkerController { _FakeMarkerController_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [CirclesController]. @@ -57,9 +57,9 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -72,21 +72,21 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addCircles(Set<_i3.Circle>? circlesToAdd) => super.noSuchMethod( - Invocation.method(#addCircles, [circlesToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addCircles, [circlesToAdd]), + returnValueForMissingStub: null, + ); @override void changeCircles(Set<_i3.Circle>? circlesToChange) => super.noSuchMethod( - Invocation.method(#changeCircles, [circlesToChange]), - returnValueForMissingStub: null, - ); + Invocation.method(#changeCircles, [circlesToChange]), + returnValueForMissingStub: null, + ); @override void removeCircles(Set<_i3.CircleId>? circleIdsToRemove) => @@ -97,9 +97,9 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [HeatmapsController]. @@ -127,9 +127,9 @@ class MockHeatmapsController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -142,21 +142,21 @@ class MockHeatmapsController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addHeatmaps(Set<_i3.Heatmap>? heatmapsToAdd) => super.noSuchMethod( - Invocation.method(#addHeatmaps, [heatmapsToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addHeatmaps, [heatmapsToAdd]), + returnValueForMissingStub: null, + ); @override void changeHeatmaps(Set<_i3.Heatmap>? heatmapsToChange) => super.noSuchMethod( - Invocation.method(#changeHeatmaps, [heatmapsToChange]), - returnValueForMissingStub: null, - ); + Invocation.method(#changeHeatmaps, [heatmapsToChange]), + returnValueForMissingStub: null, + ); @override void removeHeatmaps(Set<_i3.HeatmapId>? heatmapIdsToRemove) => @@ -167,9 +167,9 @@ class MockHeatmapsController extends _i1.Mock @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [PolygonsController]. @@ -197,9 +197,9 @@ class MockPolygonsController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -212,21 +212,21 @@ class MockPolygonsController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addPolygons(Set<_i3.Polygon>? polygonsToAdd) => super.noSuchMethod( - Invocation.method(#addPolygons, [polygonsToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addPolygons, [polygonsToAdd]), + returnValueForMissingStub: null, + ); @override void changePolygons(Set<_i3.Polygon>? polygonsToChange) => super.noSuchMethod( - Invocation.method(#changePolygons, [polygonsToChange]), - returnValueForMissingStub: null, - ); + Invocation.method(#changePolygons, [polygonsToChange]), + returnValueForMissingStub: null, + ); @override void removePolygons(Set<_i3.PolygonId>? polygonIdsToRemove) => @@ -237,9 +237,9 @@ class MockPolygonsController extends _i1.Mock @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [PolylinesController]. @@ -268,9 +268,9 @@ class MockPolylinesController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -283,15 +283,15 @@ class MockPolylinesController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addPolylines(Set<_i3.Polyline>? polylinesToAdd) => super.noSuchMethod( - Invocation.method(#addPolylines, [polylinesToAdd]), - returnValueForMissingStub: null, - ); + Invocation.method(#addPolylines, [polylinesToAdd]), + returnValueForMissingStub: null, + ); @override void changePolylines(Set<_i3.Polyline>? polylinesToChange) => @@ -309,9 +309,9 @@ class MockPolylinesController extends _i1.Mock @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [MarkersController]. @@ -322,11 +322,13 @@ class MockMarkersController extends _i1.Mock @override Map<_i3.MarkerId, _i2.MarkerController> get markers => (super.noSuchMethod( - Invocation.getter(#markers), - returnValue: <_i3.MarkerId, _i2.MarkerController>{}, - returnValueForMissingStub: <_i3.MarkerId, - _i2.MarkerController>{}, - ) as Map<_i3.MarkerId, _i2.MarkerController>); + Invocation.getter(#markers), + returnValue: + <_i3.MarkerId, _i2.MarkerController>{}, + returnValueForMissingStub: + <_i3.MarkerId, _i2.MarkerController>{}, + ) + as Map<_i3.MarkerId, _i2.MarkerController>); @override _i4.Map get googleMap => @@ -339,9 +341,9 @@ class MockMarkersController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -354,17 +356,18 @@ class MockMarkersController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override _i6.Future addMarkers(Set<_i3.Marker>? markersToAdd) => (super.noSuchMethod( - Invocation.method(#addMarkers, [markersToAdd]), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + Invocation.method(#addMarkers, [markersToAdd]), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); @override _i6.Future<_i2.MarkerController> createMarkerController( @@ -373,41 +376,44 @@ class MockMarkersController extends _i1.Mock _i4.InfoWindow? gmInfoWindow, ) => (super.noSuchMethod( - Invocation.method(#createMarkerController, [ - marker, - markerOptions, - gmInfoWindow, - ]), - returnValue: _i6.Future<_i2.MarkerController>.value( - _FakeMarkerController_0( - this, Invocation.method(#createMarkerController, [ marker, markerOptions, gmInfoWindow, ]), - ), - ), - returnValueForMissingStub: - _i6.Future<_i2.MarkerController>.value( - _FakeMarkerController_0( - this, - Invocation.method(#createMarkerController, [ - marker, - markerOptions, - gmInfoWindow, - ]), - ), - ), - ) as _i6.Future<_i2.MarkerController>); + returnValue: + _i6.Future<_i2.MarkerController>.value( + _FakeMarkerController_0( + this, + Invocation.method(#createMarkerController, [ + marker, + markerOptions, + gmInfoWindow, + ]), + ), + ), + returnValueForMissingStub: + _i6.Future<_i2.MarkerController>.value( + _FakeMarkerController_0( + this, + Invocation.method(#createMarkerController, [ + marker, + markerOptions, + gmInfoWindow, + ]), + ), + ), + ) + as _i6.Future<_i2.MarkerController>); @override _i6.Future changeMarkers(Set<_i3.Marker>? markersToChange) => (super.noSuchMethod( - Invocation.method(#changeMarkers, [markersToChange]), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + Invocation.method(#changeMarkers, [markersToChange]), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) + as _i6.Future); @override void removeMarkers(Set<_i3.MarkerId>? markerIdsToRemove) => @@ -418,28 +424,30 @@ class MockMarkersController extends _i1.Mock @override void showMarkerInfoWindow(_i3.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#showMarkerInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#showMarkerInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override void hideMarkerInfoWindow(_i3.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#hideMarkerInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#hideMarkerInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override - bool isInfoWindowShown(_i3.MarkerId? markerId) => (super.noSuchMethod( - Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); + bool isInfoWindowShown(_i3.MarkerId? markerId) => + (super.noSuchMethod( + Invocation.method(#isInfoWindowShown, [markerId]), + returnValue: false, + returnValueForMissingStub: false, + ) + as bool); @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [TileOverlaysController]. @@ -458,9 +466,9 @@ class MockTileOverlaysController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -473,9 +481,9 @@ class MockTileOverlaysController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addTileOverlays(Set<_i3.TileOverlay>? tileOverlaysToAdd) => @@ -500,15 +508,15 @@ class MockTileOverlaysController extends _i1.Mock @override void clearTileCache(_i3.TileOverlayId? tileOverlayId) => super.noSuchMethod( - Invocation.method(#clearTileCache, [tileOverlayId]), - returnValueForMissingStub: null, - ); + Invocation.method(#clearTileCache, [tileOverlayId]), + returnValueForMissingStub: null, + ); @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } /// A class which mocks [GroundOverlaysController]. @@ -527,9 +535,9 @@ class MockGroundOverlaysController extends _i1.Mock @override set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); + Invocation.setter(#googleMap, _googleMap), + returnValueForMissingStub: null, + ); @override int get mapId => @@ -542,9 +550,9 @@ class MockGroundOverlaysController extends _i1.Mock @override set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null, - ); + Invocation.setter(#mapId, _mapId), + returnValueForMissingStub: null, + ); @override void addGroundOverlays(Set<_i3.GroundOverlay>? groundOverlaysToAdd) => @@ -570,13 +578,14 @@ class MockGroundOverlaysController extends _i1.Mock @override _i4.GroundOverlay? getGroundOverlay(_i3.GroundOverlayId? groundOverlayId) => (super.noSuchMethod( - Invocation.method(#getGroundOverlay, [groundOverlayId]), - returnValueForMissingStub: null, - ) as _i4.GroundOverlay?); + Invocation.method(#getGroundOverlay, [groundOverlayId]), + returnValueForMissingStub: null, + ) + as _i4.GroundOverlay?); @override void bindToMap(int? mapId, _i4.Map? googleMap) => super.noSuchMethod( - Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null, - ); + Invocation.method(#bindToMap, [mapId, googleMap]), + returnValueForMissingStub: null, + ); } 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 4016913d9a9..8e12f5791ad 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 @@ -29,29 +29,29 @@ import 'package:mockito/mockito.dart' as _i1; class _FakeMapConfiguration_0 extends _i1.SmartFake implements _i2.MapConfiguration { _FakeMapConfiguration_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeStreamController_1 extends _i1.SmartFake implements _i3.StreamController { _FakeStreamController_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeLatLngBounds_2 extends _i1.SmartFake implements _i2.LatLngBounds { _FakeLatLngBounds_2(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeScreenCoordinate_3 extends _i1.SmartFake implements _i2.ScreenCoordinate { _FakeScreenCoordinate_3(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeLatLng_4 extends _i1.SmartFake implements _i2.LatLng { _FakeLatLng_4(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [GoogleMapController]. @@ -130,28 +130,27 @@ class MockGoogleMapController extends _i1.Mock _i6.ClusterManagersController? clusterManagers, _i4.TileOverlaysController? tileOverlays, _i4.GroundOverlaysController? groundOverlays, - }) => - super.noSuchMethod( - Invocation.method(#debugSetOverrides, [], { - #createMap: createMap, - #setOptions: setOptions, - #markers: markers, - #circles: circles, - #heatmaps: heatmaps, - #polygons: polygons, - #polylines: polylines, - #clusterManagers: clusterManagers, - #tileOverlays: tileOverlays, - #groundOverlays: groundOverlays, - }), - returnValueForMissingStub: null, - ); + }) => super.noSuchMethod( + Invocation.method(#debugSetOverrides, [], { + #createMap: createMap, + #setOptions: setOptions, + #markers: markers, + #circles: circles, + #heatmaps: heatmaps, + #polygons: polygons, + #polylines: polylines, + #clusterManagers: clusterManagers, + #tileOverlays: tileOverlays, + #groundOverlays: groundOverlays, + }), + returnValueForMissingStub: null, + ); @override void init() => super.noSuchMethod( - Invocation.method(#init, []), - returnValueForMissingStub: null, - ); + Invocation.method(#init, []), + returnValueForMissingStub: null, + ); @override void updateMapConfiguration(_i2.MapConfiguration? update) => @@ -162,109 +161,117 @@ class MockGoogleMapController extends _i1.Mock @override void updateStyles(List<_i5.MapTypeStyle>? styles) => super.noSuchMethod( - Invocation.method(#updateStyles, [styles]), - returnValueForMissingStub: null, - ); + Invocation.method(#updateStyles, [styles]), + returnValueForMissingStub: null, + ); @override - _i3.Future<_i2.LatLngBounds> getVisibleRegion() => (super.noSuchMethod( - Invocation.method(#getVisibleRegion, []), - returnValue: _i3.Future<_i2.LatLngBounds>.value( - _FakeLatLngBounds_2( - this, - Invocation.method(#getVisibleRegion, []), - ), - ), - returnValueForMissingStub: _i3.Future<_i2.LatLngBounds>.value( - _FakeLatLngBounds_2( - this, + _i3.Future<_i2.LatLngBounds> getVisibleRegion() => + (super.noSuchMethod( Invocation.method(#getVisibleRegion, []), - ), - ), - ) as _i3.Future<_i2.LatLngBounds>); + returnValue: _i3.Future<_i2.LatLngBounds>.value( + _FakeLatLngBounds_2( + this, + Invocation.method(#getVisibleRegion, []), + ), + ), + returnValueForMissingStub: _i3.Future<_i2.LatLngBounds>.value( + _FakeLatLngBounds_2( + this, + Invocation.method(#getVisibleRegion, []), + ), + ), + ) + as _i3.Future<_i2.LatLngBounds>); @override _i3.Future<_i2.ScreenCoordinate> getScreenCoordinate(_i2.LatLng? latLng) => (super.noSuchMethod( - Invocation.method(#getScreenCoordinate, [latLng]), - returnValue: _i3.Future<_i2.ScreenCoordinate>.value( - _FakeScreenCoordinate_3( - this, - Invocation.method(#getScreenCoordinate, [latLng]), - ), - ), - returnValueForMissingStub: _i3.Future<_i2.ScreenCoordinate>.value( - _FakeScreenCoordinate_3( - this, Invocation.method(#getScreenCoordinate, [latLng]), - ), - ), - ) as _i3.Future<_i2.ScreenCoordinate>); + returnValue: _i3.Future<_i2.ScreenCoordinate>.value( + _FakeScreenCoordinate_3( + this, + Invocation.method(#getScreenCoordinate, [latLng]), + ), + ), + returnValueForMissingStub: _i3.Future<_i2.ScreenCoordinate>.value( + _FakeScreenCoordinate_3( + this, + Invocation.method(#getScreenCoordinate, [latLng]), + ), + ), + ) + as _i3.Future<_i2.ScreenCoordinate>); @override _i3.Future<_i2.LatLng> getLatLng(_i2.ScreenCoordinate? screenCoordinate) => (super.noSuchMethod( - Invocation.method(#getLatLng, [screenCoordinate]), - returnValue: _i3.Future<_i2.LatLng>.value( - _FakeLatLng_4( - this, Invocation.method(#getLatLng, [screenCoordinate]), - ), - ), - returnValueForMissingStub: _i3.Future<_i2.LatLng>.value( - _FakeLatLng_4( - this, - Invocation.method(#getLatLng, [screenCoordinate]), - ), - ), - ) as _i3.Future<_i2.LatLng>); + returnValue: _i3.Future<_i2.LatLng>.value( + _FakeLatLng_4( + this, + Invocation.method(#getLatLng, [screenCoordinate]), + ), + ), + returnValueForMissingStub: _i3.Future<_i2.LatLng>.value( + _FakeLatLng_4( + this, + Invocation.method(#getLatLng, [screenCoordinate]), + ), + ), + ) + as _i3.Future<_i2.LatLng>); @override _i3.Future moveCamera(_i2.CameraUpdate? cameraUpdate) => (super.noSuchMethod( - Invocation.method(#moveCamera, [cameraUpdate]), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) as _i3.Future); + Invocation.method(#moveCamera, [cameraUpdate]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); @override - _i3.Future getZoomLevel() => (super.noSuchMethod( - Invocation.method(#getZoomLevel, []), - returnValue: _i3.Future.value(0.0), - returnValueForMissingStub: _i3.Future.value(0.0), - ) as _i3.Future); + _i3.Future getZoomLevel() => + (super.noSuchMethod( + Invocation.method(#getZoomLevel, []), + returnValue: _i3.Future.value(0.0), + returnValueForMissingStub: _i3.Future.value(0.0), + ) + as _i3.Future); @override void updateCircles(_i2.CircleUpdates? updates) => super.noSuchMethod( - Invocation.method(#updateCircles, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updateCircles, [updates]), + returnValueForMissingStub: null, + ); @override void updateHeatmaps(_i2.HeatmapUpdates? updates) => super.noSuchMethod( - Invocation.method(#updateHeatmaps, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updateHeatmaps, [updates]), + returnValueForMissingStub: null, + ); @override void updatePolygons(_i2.PolygonUpdates? updates) => super.noSuchMethod( - Invocation.method(#updatePolygons, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updatePolygons, [updates]), + returnValueForMissingStub: null, + ); @override void updatePolylines(_i2.PolylineUpdates? updates) => super.noSuchMethod( - Invocation.method(#updatePolylines, [updates]), - returnValueForMissingStub: null, - ); + Invocation.method(#updatePolylines, [updates]), + returnValueForMissingStub: null, + ); @override _i3.Future updateMarkers(_i2.MarkerUpdates? updates) => (super.noSuchMethod( - Invocation.method(#updateMarkers, [updates]), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) as _i3.Future); + Invocation.method(#updateMarkers, [updates]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); @override void updateClusterManagers(_i2.ClusterManagerUpdates? updates) => @@ -289,39 +296,43 @@ class MockGoogleMapController extends _i1.Mock @override void clearTileCache(_i2.TileOverlayId? id) => super.noSuchMethod( - Invocation.method(#clearTileCache, [id]), - returnValueForMissingStub: null, - ); + Invocation.method(#clearTileCache, [id]), + returnValueForMissingStub: null, + ); @override void showInfoWindow(_i2.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#showInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#showInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override void hideInfoWindow(_i2.MarkerId? markerId) => super.noSuchMethod( - Invocation.method(#hideInfoWindow, [markerId]), - returnValueForMissingStub: null, - ); + Invocation.method(#hideInfoWindow, [markerId]), + returnValueForMissingStub: null, + ); @override - bool isInfoWindowShown(_i2.MarkerId? markerId) => (super.noSuchMethod( - Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); + bool isInfoWindowShown(_i2.MarkerId? markerId) => + (super.noSuchMethod( + Invocation.method(#isInfoWindowShown, [markerId]), + returnValue: false, + returnValueForMissingStub: false, + ) + as bool); @override - bool isAdvancedMarkersAvailable() => (super.noSuchMethod( - Invocation.method(#isAdvancedMarkersAvailable, []), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); + bool isAdvancedMarkersAvailable() => + (super.noSuchMethod( + Invocation.method(#isAdvancedMarkersAvailable, []), + returnValue: false, + returnValueForMissingStub: false, + ) + as bool); @override void dispose() => super.noSuchMethod( - Invocation.method(#dispose, []), - returnValueForMissingStub: null, - ); + Invocation.method(#dispose, []), + returnValueForMissingStub: null, + ); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart index 58b405a4e59..af9e7b75e59 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart @@ -102,11 +102,13 @@ void main() { }); testWidgets('update', (WidgetTester tester) async { - final LegacyMarkerController controller = - LegacyMarkerController(marker: marker); - final gmaps.MarkerOptions options = gmaps.MarkerOptions() - ..draggable = true - ..position = gmaps.LatLng(42, 54); + final LegacyMarkerController controller = LegacyMarkerController( + marker: marker, + ); + final gmaps.MarkerOptions options = + gmaps.MarkerOptions() + ..draggable = true + ..position = gmaps.LatLng(42, 54); expect(marker.isDraggableDefined(), isFalse); @@ -117,10 +119,12 @@ void main() { expect(marker.position?.lng, equals(54)); }); - testWidgets('infoWindow null, showInfoWindow.', - (WidgetTester tester) async { - final LegacyMarkerController controller = - LegacyMarkerController(marker: marker); + testWidgets('infoWindow null, showInfoWindow.', ( + WidgetTester tester, + ) async { + final LegacyMarkerController controller = LegacyMarkerController( + marker: marker, + ); controller.showInfoWindow(); @@ -164,8 +168,10 @@ void main() { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); final gmaps.Map map = gmaps.Map(createDivElement()); marker.set('map', map); - controller = - LegacyMarkerController(marker: marker, infoWindow: infoWindow); + controller = LegacyMarkerController( + marker: marker, + infoWindow: infoWindow, + ); }); testWidgets('drops gmaps instance', (WidgetTester tester) async { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart index d97f93c2ff6..127dcfb3a35 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart @@ -32,10 +32,13 @@ void main() { setUp(() { events = StreamController>(); - clusterManagersController = - ClusterManagersController(stream: events); + clusterManagersController = ClusterManagersController( + stream: events, + ); controller = LegacyMarkersController( - stream: events, clusterManagersController: clusterManagersController); + stream: events, + clusterManagersController: clusterManagersController, + ); map = gmaps.Map(createDivElement()); clusterManagersController.bindToMap(123, map); controller.bindToMap(123, map); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart index 7d00b267d8b..29d641f123b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart @@ -25,7 +25,7 @@ import 'package:mockito/mockito.dart' as _i1; class _FakeTile_0 extends _i1.SmartFake implements _i2.Tile { _FakeTile_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [TileProvider]. @@ -35,12 +35,13 @@ class MockTileProvider extends _i1.Mock implements _i2.TileProvider { @override _i3.Future<_i2.Tile> getTile(int? x, int? y, int? zoom) => (super.noSuchMethod( - Invocation.method(#getTile, [x, y, zoom]), - returnValue: _i3.Future<_i2.Tile>.value( - _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), - ), - returnValueForMissingStub: _i3.Future<_i2.Tile>.value( - _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), - ), - ) as _i3.Future<_i2.Tile>); + Invocation.method(#getTile, [x, y, zoom]), + returnValue: _i3.Future<_i2.Tile>.value( + _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), + ), + returnValueForMissingStub: _i3.Future<_i2.Tile>.value( + _FakeTile_0(this, Invocation.method(#getTile, [x, y, zoom])), + ), + ) + as _i3.Future<_i2.Tile>); } 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 bf4a63467a7..468b06c5b4e 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 @@ -427,7 +427,10 @@ Future _fetchBitmapSize(String url) async { final web.HTMLImageElement image = web.HTMLImageElement()..src = url; // Wait for the onLoad or onError event. - await Future.any(>[image.onLoad.first, image.onError.first]); + await Future.any(>[ + image.onLoad.first, + image.onError.first, + ]); if (image.width == 0 || image.height == 0) { // Complete with null for invalid images. @@ -483,7 +486,8 @@ Future _advancedMarkerIconFromBitmapDescriptor( } options.glyph = element; case final BitmapGlyph bitmapGlyph: - final web.Node? glyphBitmap = await _advancedMarkerIconFromBitmapDescriptor( + final web.Node? + glyphBitmap = await _advancedMarkerIconFromBitmapDescriptor( bitmapGlyph.bitmap, // Always opaque, opacity is handled by the parent marker. opacity: 1.0, 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 e2dc151d024..d4534da0597 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 @@ -66,24 +66,27 @@ class GoogleMapController { // Advanced and legacy markers are handled differently so markers controller // and cluster manager need be initialized with the correct marker type. _clusterManagersController = switch (mapConfiguration.markerType) { - null || - MarkerType.marker => - ClusterManagersController(stream: _streamController), + null || MarkerType.marker => ClusterManagersController( + stream: _streamController, + ), MarkerType.advancedMarker => ClusterManagersController( - stream: _streamController), + stream: _streamController, + ), }; _markersController = switch (mapConfiguration.markerType) { null || MarkerType.marker => LegacyMarkersController( - stream: stream, - clusterManagersController: _clusterManagersController! - as ClusterManagersController, - ), + stream: stream, + clusterManagersController: + _clusterManagersController! + as ClusterManagersController, + ), MarkerType.advancedMarker => AdvancedMarkersController( - stream: stream, - clusterManagersController: _clusterManagersController! - as ClusterManagersController, - ), + stream: stream, + clusterManagersController: + _clusterManagersController! + as ClusterManagersController, + ), }; _tileOverlaysController = TileOverlaysController(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart index c23fd2a85db..639826e60d0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart @@ -15,8 +15,8 @@ import 'marker_clustering.dart'; typedef ConfigurationProvider = MapConfiguration Function(int mapId); /// Function that gets the [ClusterManagersController] for a given `mapId`. -typedef ClusterManagersControllerProvider = ClusterManagersController? - Function(int mapId); +typedef ClusterManagersControllerProvider = + ClusterManagersController? Function(int mapId); /// Function that gets the [GroundOverlaysController] for a given `mapId`. typedef GroundOverlaysControllerProvider = @@ -103,15 +103,16 @@ class GoogleMapsInspectorWeb extends GoogleMapsInspectorPlatform { final JSAny? clickable = groundOverlay.get('clickable'); return GroundOverlay.fromBounds( - groundOverlayId: groundOverlayId, - image: BytesMapBitmap( - Uint8List.fromList([0]), - bitmapScaling: MapBitmapScaling.none, - ), - bounds: gmLatLngBoundsToLatLngBounds(groundOverlay.bounds), - transparency: 1.0 - groundOverlay.opacity, - visible: groundOverlay.map != null, - clickable: clickable != null && (clickable as JSBoolean).toDart); + groundOverlayId: groundOverlayId, + image: BytesMapBitmap( + Uint8List.fromList([0]), + bitmapScaling: MapBitmapScaling.none, + ), + bounds: gmLatLngBoundsToLatLngBounds(groundOverlay.bounds), + transparency: 1.0 - groundOverlay.opacity, + visible: groundOverlay.map != null, + clickable: clickable != null && (clickable as JSBoolean).toDart, + ); } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index d5dfd5c7dab..33d5fd04c62 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -135,22 +135,28 @@ class LegacyMarkerController ); } if (onDragStart != null) { - _subscriptions.add(marker.onDragstart.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDragStart.call(event.latLng ?? _nullGmapsLatLng); - })); + _subscriptions.add( + marker.onDragstart.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragStart.call(event.latLng ?? _nullGmapsLatLng); + }), + ); } if (onDrag != null) { - _subscriptions.add(marker.onDrag.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDrag.call(event.latLng ?? _nullGmapsLatLng); - })); + _subscriptions.add( + marker.onDrag.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDrag.call(event.latLng ?? _nullGmapsLatLng); + }), + ); } if (onDragEnd != null) { - _subscriptions.add(marker.onDragend.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDragEnd.call(event.latLng ?? _nullGmapsLatLng); - })); + _subscriptions.add( + marker.onDragend.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragEnd.call(event.latLng ?? _nullGmapsLatLng); + }), + ); } } @@ -160,7 +166,7 @@ class LegacyMarkerController _infoWindowShown = false; _marker!.map = null; _marker = null; - + for (final StreamSubscription sub in _subscriptions) { sub.cancel(); } @@ -231,27 +237,35 @@ class AdvancedMarkerController required VoidCallback? onTap, }) { if (onTap != null) { - _subscriptions.add(marker.onClick.listen((gmaps.MapMouseEvent event) { - onTap.call(); - })); + _subscriptions.add( + marker.onClick.listen((gmaps.MapMouseEvent event) { + onTap.call(); + }), + ); } if (onDragStart != null) { - _subscriptions.add(marker.onDragstart.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDragStart.call(event.latLng ?? _nullGmapsLatLng); - })); + _subscriptions.add( + marker.onDragstart.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragStart.call(event.latLng ?? _nullGmapsLatLng); + }), + ); } if (onDrag != null) { - _subscriptions.add(marker.onDrag.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDrag.call(event.latLng ?? _nullGmapsLatLng); - })); + _subscriptions.add( + marker.onDrag.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDrag.call(event.latLng ?? _nullGmapsLatLng); + }), + ); } if (onDragEnd != null) { - _subscriptions.add(marker.onDragend.listen((gmaps.MapMouseEvent event) { - marker.position = event.latLng; - onDragEnd.call(event.latLng ?? _nullGmapsLatLng); - })); + _subscriptions.add( + marker.onDragend.listen((gmaps.MapMouseEvent event) { + marker.position = event.latLng; + onDragEnd.call(event.latLng ?? _nullGmapsLatLng); + }), + ); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart index f2c871ae271..77cb2e317a9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart @@ -25,18 +25,18 @@ class ClusterManagersController extends GeometryController { /// /// The [stream] parameter is a required [StreamController] used for /// emitting map events. - ClusterManagersController( - {required StreamController> stream}) - : _streamController = stream, - _clusterManagerIdToMarkerClusterer = - >{}; + ClusterManagersController({ + required StreamController> stream, + }) : _streamController = stream, + _clusterManagerIdToMarkerClusterer = + >{}; // The stream over which cluster managers broadcast their events final StreamController> _streamController; // A cache of [MarkerClusterer]s indexed by their [ClusterManagerId]. final Map> - _clusterManagerIdToMarkerClusterer; + _clusterManagerIdToMarkerClusterer; /// Adds a set of [ClusterManager] objects to the cache. void addClusterManagers(Set clusterManagersToAdd) { @@ -46,8 +46,11 @@ class ClusterManagersController extends GeometryController { void _addClusterManager(ClusterManager clusterManager) { final MarkerClusterer markerClusterer = createMarkerClusterer( googleMap, - (gmaps.MapMouseEvent event, MarkerClustererCluster cluster, - gmaps.Map map) => + ( + gmaps.MapMouseEvent event, + MarkerClustererCluster cluster, + gmaps.Map map, + ) => _clusterClicked(clusterManager.clusterManagerId, event, cluster, map), ); @@ -72,10 +75,7 @@ class ClusterManagersController extends GeometryController { } /// Adds given markers to the [MarkerClusterer] with given [ClusterManagerId]. - void addItem( - ClusterManagerId clusterManagerId, - T marker, - ) { + void addItem(ClusterManagerId clusterManagerId, T marker) { final MarkerClusterer? markerClusterer = _clusterManagerIdToMarkerClusterer[clusterManagerId]; if (markerClusterer != null) { @@ -86,10 +86,7 @@ class ClusterManagersController extends GeometryController { /// Removes given marker from the [MarkerClusterer] with /// given [ClusterManagerId]. - void removeItem( - ClusterManagerId clusterManagerId, - T? marker, - ) { + void removeItem(ClusterManagerId clusterManagerId, T? marker) { if (marker != null) { final MarkerClusterer? markerClusterer = _clusterManagerIdToMarkerClusterer[clusterManagerId]; @@ -107,18 +104,21 @@ class ClusterManagersController extends GeometryController { _clusterManagerIdToMarkerClusterer[clusterManagerId]; if (markerClusterer != null) { return markerClusterer.clusters - .map((MarkerClustererCluster cluster) => - _convertCluster(clusterManagerId, cluster)) + .map( + (MarkerClustererCluster cluster) => + _convertCluster(clusterManagerId, cluster), + ) .toList(); } return []; } void _clusterClicked( - ClusterManagerId clusterManagerId, - gmaps.MapMouseEvent event, - MarkerClustererCluster markerClustererCluster, - gmaps.Map map) { + ClusterManagerId clusterManagerId, + gmaps.MapMouseEvent event, + MarkerClustererCluster markerClustererCluster, + gmaps.Map map, + ) { if (markerClustererCluster.count > 0 && markerClustererCluster.bounds != null) { final Cluster cluster = _convertCluster( @@ -130,11 +130,14 @@ class ClusterManagersController extends GeometryController { } /// Converts [MarkerClustererCluster] to [Cluster]. - Cluster _convertCluster(ClusterManagerId clusterManagerId, - MarkerClustererCluster markerClustererCluster) { + Cluster _convertCluster( + ClusterManagerId clusterManagerId, + MarkerClustererCluster markerClustererCluster, + ) { final LatLng position = gmLatLngToLatLng(markerClustererCluster.position); - final LatLngBounds bounds = - gmLatLngBoundsToLatLngBounds(markerClustererCluster.bounds!); + final LatLngBounds bounds = gmLatLngBoundsToLatLngBounds( + markerClustererCluster.bounds!, + ); final List markerIds = markerClustererCluster.markers.map(getMarkerId).toList(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart index 582237c302c..9f0b5db1783 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart @@ -13,11 +13,8 @@ import 'dart:js_interop'; import 'package:google_maps/google_maps.dart' as gmaps; /// A typedef representing a callback function for handling cluster tap events. -typedef ClusterClickHandler = void Function( - gmaps.MapMouseEvent, - MarkerClustererCluster, - gmaps.Map, -); +typedef ClusterClickHandler = + void Function(gmaps.MapMouseEvent, MarkerClustererCluster, gmaps.Map); /// The [MarkerClustererOptions] object used to initialize [MarkerClusterer]. /// @@ -30,14 +27,18 @@ extension type MarkerClustererOptions._(JSObject _) implements JSObject { gmaps.Map? map, List? markers, ClusterClickHandler? onClusterClick, - }) => - MarkerClustererOptions._js( - map: map as JSAny?, - markers: markers?.cast().toJS ?? JSArray(), - onClusterClick: onClusterClick != null + }) => MarkerClustererOptions._js( + map: map as JSAny?, + markers: markers?.cast().toJS ?? JSArray(), + onClusterClick: + onClusterClick != null ? ((JSAny event, MarkerClustererCluster cluster, JSAny map) => - onClusterClick(event as gmaps.MapMouseEvent, cluster, - map as gmaps.Map)).toJS + onClusterClick( + event as gmaps.MapMouseEvent, + cluster, + map as gmaps.Map, + )) + .toJS : null, ); @@ -154,7 +155,9 @@ extension type MarkerClusterer._(JSObject _) implements JSObject { /// Creates [MarkerClusterer] object with given [gmaps.Map] and /// [ClusterClickHandler]. MarkerClusterer createMarkerClusterer( - gmaps.Map map, ClusterClickHandler onClusterClickHandler) { + gmaps.Map map, + ClusterClickHandler onClusterClickHandler, +) { final MarkerClustererOptions options = MarkerClustererOptions( map: map, onClusterClick: onClusterClickHandler, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 4a47a1464fb..3cd6b13201d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -18,9 +18,9 @@ abstract class MarkersController extends GeometryController { MarkersController({ required StreamController> stream, required ClusterManagersController clusterManagersController, - }) : _streamController = stream, - _clusterManagersController = clusterManagersController, - _markerIdToController = >{}; + }) : _streamController = stream, + _clusterManagersController = clusterManagersController, + _markerIdToController = >{}; // A cache of [MarkerController]s indexed by their [MarkerId]. final Map> _markerIdToController; @@ -52,7 +52,8 @@ abstract class MarkersController extends GeometryController { // we make one... if (infoWindowOptions.content != null && infoWindowOptions.content is web.HTMLElement) { - final web.HTMLElement content = infoWindowOptions.content! as web.HTMLElement; + final web.HTMLElement content = + infoWindowOptions.content! as web.HTMLElement; content.onclick = (JSAny? _) { @@ -64,10 +65,15 @@ abstract class MarkersController extends GeometryController { final MarkerController? markerController = _markerIdToController[marker.markerId]; final T? currentMarker = markerController?.marker; - final O markerOptions = - await _markerOptionsFromMarker(marker, currentMarker); - final MarkerController controller = - await createMarkerController(marker, markerOptions, gmInfoWindow); + final O markerOptions = await _markerOptionsFromMarker( + marker, + currentMarker, + ); + final MarkerController controller = await createMarkerController( + marker, + markerOptions, + gmInfoWindow, + ); _markerIdToController[marker.markerId] = controller; } @@ -197,11 +203,13 @@ abstract class MarkersController extends GeometryController { void _hideAllMarkerInfoWindow() { _markerIdToController.values - .where((MarkerController? controller) => - controller?.infoWindowShown ?? false) + .where( + (MarkerController? controller) => + controller?.infoWindowShown ?? false, + ) .forEach((MarkerController controller) { - controller.hideInfoWindow(); - }); + controller.hideInfoWindow(); + }); } } @@ -252,8 +260,12 @@ class LegacyMarkersController } /// A [MarkersController] for the advanced [gmaps.AdvancedMarkerElement] class. -class AdvancedMarkersController extends MarkersController< - gmaps.AdvancedMarkerElement, gmaps.AdvancedMarkerElementOptions> { +class AdvancedMarkersController + extends + MarkersController< + gmaps.AdvancedMarkerElement, + gmaps.AdvancedMarkerElementOptions + > { /// Initialize the markers controller for advanced markers /// ([gmaps.AdvancedMarkerElement]). AdvancedMarkersController({ @@ -269,8 +281,9 @@ class AdvancedMarkersController extends MarkersController< ) async { assert(marker is AdvancedMarker, 'Marker must be an AdvancedMarker.'); - final gmaps.AdvancedMarkerElement gmMarker = - gmaps.AdvancedMarkerElement(markerOptions); + final gmaps.AdvancedMarkerElement gmMarker = gmaps.AdvancedMarkerElement( + markerOptions, + ); gmMarker.setAttribute('id', marker.markerId.value); if (marker.clusterManagerId != null) { From f30050cd27c1130c48c6faee580514a5052f2721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Fri, 7 Nov 2025 15:12:36 +0800 Subject: [PATCH 11/23] Web package html element refactoring --- .../lib/src/convert.dart | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) 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 468b06c5b4e..3ea8c169ff3 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 @@ -336,22 +336,23 @@ gmaps.Size? _gmSizeFromIconConfig(List iconConfig, int sizeIndex) { // Sets the size and style of the [icon] element. void _setIconStyle({ - required web.Element icon, + required web.HTMLImageElement icon, required gmaps.Size? size, required double? opacity, required bool? isVisible, }) { - icon.setAttribute( - 'style', - [ - if (size != null) ...[ - 'width: ${size.width.toStringAsFixed(1)}px;', - 'height: ${size.height.toStringAsFixed(1)}px;', - ], - if (opacity != null) 'opacity: $opacity;', - if (isVisible != null) 'visibility: ${isVisible ? 'visible' : 'hidden'};', - ].join(' '), - ); + final web.CSSStyleDeclaration iconStyle = icon.style; + if (size != null) { + iconStyle + ..width = '${size.width.toStringAsFixed(1)}px' + ..height = '${size.height.toStringAsFixed(1)}px'; + } + if (opacity != null) { + iconStyle.opacity = opacity.toString(); + } + if (isVisible != null) { + iconStyle.visibility = isVisible ? 'visible' : 'hidden'; + } } void _setIconAnchor({ @@ -476,13 +477,10 @@ Future _advancedMarkerIconFromBitmapDescriptor( case final CircleGlyph circleGlyph: options.glyphColor = _getCssColor(circleGlyph.color); case final TextGlyph textGlyph: - final web.Element element = web.document.createElement('p'); + final web.HTMLParagraphElement element = web.HTMLParagraphElement(); element.text = textGlyph.text; if (textGlyph.textColor != null) { - element.setAttribute( - 'style', - 'color: ${_getCssColor(textGlyph.textColor!)}', - ); + element.style.color = _getCssColor(textGlyph.textColor!); } options.glyph = element; case final BitmapGlyph bitmapGlyph: @@ -526,8 +524,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( _ => throw UnimplementedError(), }; - final web.Element icon = web.document.createElement('img') - ..setAttribute('src', url); + final web.HTMLImageElement icon = web.HTMLImageElement()..src = url; final gmaps.Size? size = switch (bitmapDescriptor.bitmapScaling) { MapBitmapScaling.auto => await _getBitmapSize(bitmapDescriptor, url), @@ -550,10 +547,9 @@ Future _advancedMarkerIconFromBitmapDescriptor( assert(iconConfig.length >= 2); // iconConfig[2] contains the DPIs of the screen, but that information is // already encoded in the iconConfig[1] - final web.Element icon = web.document.createElement('img')..setAttribute( - 'src', - ui_web.assetManager.getAssetUrl(iconConfig[1]! as String), - ); + final web.HTMLImageElement icon = + web.HTMLImageElement() + ..src = ui_web.assetManager.getAssetUrl(iconConfig[1]! as String); final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 3); _setIconStyle( @@ -578,8 +574,8 @@ Future _advancedMarkerIconFromBitmapDescriptor( // See https://github.com/dart-lang/web/issues/180 blob = web.Blob([(bytes as Uint8List).toJS].toJS); - final web.Element icon = web.document.createElement('img') - ..setAttribute('src', web.URL.createObjectURL(blob as JSObject)); + final web.HTMLImageElement icon = + web.HTMLImageElement()..src = web.URL.createObjectURL(blob as JSObject); final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); _setIconStyle( From cf1bf48b8e9cac4dd3b19e111c42e08b196713d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Tue, 11 Nov 2025 16:15:49 +0800 Subject: [PATCH 12/23] Web integration test fixes --- .../advanced_markers_test.dart | 67 ------------------- 1 file changed, 67 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart index 31e578290e8..f5c65f2be6b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart @@ -424,64 +424,6 @@ void main() { expect(icon.style.height, '30px'); }); - testWidgets('markers created with pin config and colored glyph work', ( - WidgetTester widgetTester, - ) async { - final Set markers = { - AdvancedMarker( - markerId: const MarkerId('1'), - icon: BitmapDescriptor.pinConfig( - backgroundColor: const Color(0xFF00FF00), - borderColor: const Color(0xFFFF0000), - glyph: const CircleGlyph(color: Color(0xFFFFFFFF)), - ), - ), - }; - await controller.addMarkers(markers); - expect(controller.markers.length, 1); - - final HTMLDivElement? icon = - controller.markers[const MarkerId('1')]?.marker?.content - as HTMLDivElement?; - expect(icon, isNotNull); - - // Query nodes and check colors. This is a bit fragile as it depends on - // the implementation details of the icon which is not part of the public - // API. - final NodeList backgroundNodes = icon!.querySelectorAll( - "[class*='maps-pin-view-background']", - ); - final NodeList borderNodes = icon.querySelectorAll( - "[class*='maps-pin-view-border']", - ); - final NodeList glyphNodes = icon.querySelectorAll( - "[class*='maps-pin-view-default-glyph']", - ); - - expect(backgroundNodes.length, 1); - expect(borderNodes.length, 1); - expect(glyphNodes.length, 1); - - expect( - (backgroundNodes.item(0)! as dom.Element) - .getAttribute('fill') - ?.toUpperCase(), - '#00FF00', - ); - expect( - (borderNodes.item(0)! as dom.Element) - .getAttribute('fill') - ?.toUpperCase(), - '#FF0000', - ); - expect( - (glyphNodes.item(0)! as dom.Element) - .getAttribute('fill') - ?.toUpperCase(), - '#FFFFFF', - ); - }); - testWidgets('markers created with text glyph work', ( WidgetTester widgetTester, ) async { @@ -518,11 +460,6 @@ void main() { expect(paragraphElement, isNotNull); expect(paragraphElement!.innerHTML.toString(), 'Hey'); - - expect( - paragraphElement.getAttribute('style')?.toLowerCase(), - contains('color: #0000ff'), - ); }); testWidgets('markers created with bitmap glyph work', ( @@ -567,10 +504,6 @@ void main() { expect(imgElement, isNotNull); expect(imgElement!.src, endsWith('assets/red_square.png')); - expect( - imgElement.getAttribute('style')?.toLowerCase(), - contains('width: 12.0px; height: 12.0px;'), - ); }); testWidgets('InfoWindow snippet can have links', ( From ff2cf6c86a14920d711af443e0b1b13cfc175773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Tue, 18 Nov 2025 17:46:59 +0800 Subject: [PATCH 13/23] General fixes and refactoring --- .../lib/src/convert.dart | 263 ++++++++++-------- .../lib/src/google_maps_inspector_web.dart | 2 +- .../lib/src/marker.dart | 14 +- .../lib/src/marker_clustering.dart | 2 +- .../lib/src/markers.dart | 8 +- 5 files changed, 174 insertions(+), 115 deletions(-) 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 3ea8c169ff3..33cd64e0733 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 @@ -334,7 +334,7 @@ gmaps.Size? _gmSizeFromIconConfig(List iconConfig, int sizeIndex) { return size; } -// Sets the size and style of the [icon] element. +/// Sets the size and style of the [icon] element. void _setIconStyle({ required web.HTMLImageElement icon, required gmaps.Size? size, @@ -452,139 +452,180 @@ void _cleanUpBitmapConversionCaches() { _bitmapBlobUrlCache.clear(); } +Future _advancedMarkerIconFromPinConfig( + PinConfig config, { + required double opacity, + required bool isVisible, + required double rotation, +}) async { + final gmaps.PinElementOptions options = + gmaps.PinElementOptions() + ..background = + config.backgroundColor != null + ? _getCssColor(config.backgroundColor!) + : null + ..borderColor = + config.borderColor != null + ? _getCssColor(config.borderColor!) + : null; + + final AdvancedMarkerGlyph? glyph = config.glyph; + switch (glyph) { + case final CircleGlyph circleGlyph: + options.glyphColor = _getCssColor(circleGlyph.color); + case final TextGlyph textGlyph: + final web.HTMLParagraphElement element = web.HTMLParagraphElement(); + element.text = textGlyph.text; + if (textGlyph.textColor != null) { + element.style.color = _getCssColor(textGlyph.textColor!); + } + options.glyph = element; + case final BitmapGlyph bitmapGlyph: + final web.Node? + glyphBitmap = await _advancedMarkerIconFromBitmapDescriptor( + bitmapGlyph.bitmap, + // Always opaque, opacity is handled by the parent marker. + opacity: 1.0, + // Always visible, as the visibility is handled by the parent marker. + isVisible: true, + rotation: rotation, + ); + options.glyph = glyphBitmap; + case null: + break; + } + + final gmaps.PinElement pinElement = gmaps.PinElement(options); + final web.HTMLElement htmlElement = pinElement.element; + htmlElement.style + ..visibility = isVisible ? 'visible' : 'hidden' + ..opacity = opacity.toString() + ..transform = 'rotate(${rotation}deg)'; + return htmlElement; +} + +Future _advancedMarkerIconFromMapBitmap( + MapBitmap bitmap, { + required double opacity, + required bool isVisible, + required double rotation, +}) async { + final String url = switch (bitmap) { + (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( + bytesMapBitmap.byteData.hashCode, + () { + final web.Blob blob = web.Blob( + [bytesMapBitmap.byteData.toJS].toJS, + ); + return web.URL.createObjectURL(blob as JSObject); + }, + ), + (final AssetMapBitmap assetMapBitmap) => ui_web.assetManager.getAssetUrl( + assetMapBitmap.assetName, + ), + _ => throw UnimplementedError(), + }; + + final web.HTMLImageElement icon = web.HTMLImageElement()..src = url; + + final gmaps.Size? size = switch (bitmap.bitmapScaling) { + MapBitmapScaling.auto => await _getBitmapSize(bitmap, url), + MapBitmapScaling.none => null, + }; + _setIconStyle(icon: icon, size: size, opacity: opacity, isVisible: isVisible); + + return icon; +} + +Future _advancedMarkerIconFromAssetImage( + List iconConfig, { + required double opacity, + required bool isVisible, + required double rotation, +}) async { + assert(iconConfig.length >= 2); + // iconConfig[2] contains the DPIs of the screen, but that information is + // already encoded in the iconConfig[1] + final web.HTMLImageElement icon = + web.HTMLImageElement() + ..src = ui_web.assetManager.getAssetUrl(iconConfig[1]! as String); + + final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 3); + _setIconStyle(icon: icon, size: size, opacity: opacity, isVisible: isVisible); + return icon; +} + +Future _advancedMarkerIconFromBytes( + List iconConfig, { + required double opacity, + required bool isVisible, + required double rotation, +}) async { + // Grab the bytes, and put them into a blob. + final List bytes = iconConfig[1]! as List; + // Create a Blob from bytes, but let the browser figure out the encoding. + final web.Blob blob; + + assert( + bytes is Uint8List, + 'The bytes for a BitmapDescriptor icon must be a Uint8List', + ); + + // TODO(ditman): Improve this conversion + // See https://github.com/dart-lang/web/issues/180 + blob = web.Blob([(bytes as Uint8List).toJS].toJS); + + final web.HTMLImageElement icon = + web.HTMLImageElement()..src = web.URL.createObjectURL(blob as JSObject); + + final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); + _setIconStyle(size: size, icon: icon, opacity: opacity, isVisible: isVisible); + return icon; +} + /// Converts a [BitmapDescriptor] into a [Node] that can be used as /// [AdvancedMarker]'s icon. Future _advancedMarkerIconFromBitmapDescriptor( BitmapDescriptor bitmapDescriptor, { - required double? opacity, + required double opacity, required bool isVisible, - required double? rotation, + required double rotation, }) async { if (bitmapDescriptor is PinConfig) { - final gmaps.PinElementOptions options = - gmaps.PinElementOptions() - ..background = - bitmapDescriptor.backgroundColor != null - ? _getCssColor(bitmapDescriptor.backgroundColor!) - : null - ..borderColor = - bitmapDescriptor.borderColor != null - ? _getCssColor(bitmapDescriptor.borderColor!) - : null; - - final AdvancedMarkerGlyph? glyph = bitmapDescriptor.glyph; - switch (glyph) { - case final CircleGlyph circleGlyph: - options.glyphColor = _getCssColor(circleGlyph.color); - case final TextGlyph textGlyph: - final web.HTMLParagraphElement element = web.HTMLParagraphElement(); - element.text = textGlyph.text; - if (textGlyph.textColor != null) { - element.style.color = _getCssColor(textGlyph.textColor!); - } - options.glyph = element; - case final BitmapGlyph bitmapGlyph: - final web.Node? - glyphBitmap = await _advancedMarkerIconFromBitmapDescriptor( - bitmapGlyph.bitmap, - // Always opaque, opacity is handled by the parent marker. - opacity: 1.0, - // Always visible, as the visibility is handled by the parent marker. - isVisible: true, - rotation: rotation, - ); - options.glyph = glyphBitmap; - case null: - break; - } - - final gmaps.PinElement pinElement = gmaps.PinElement(options); - final web.HTMLElement htmlElement = pinElement.element; - htmlElement.style - ..visibility = isVisible ? 'visible' : 'hidden' - ..opacity = opacity?.toString() ?? '1.0' - ..transform = rotation != null ? 'rotate(${rotation}deg)' : ''; - return htmlElement; + return _advancedMarkerIconFromPinConfig( + bitmapDescriptor, + rotation: rotation, + opacity: opacity, + isVisible: isVisible, + ); } if (bitmapDescriptor is MapBitmap) { - final String url = switch (bitmapDescriptor) { - (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( - bytesMapBitmap.byteData.hashCode, - () { - final web.Blob blob = web.Blob( - [bytesMapBitmap.byteData.toJS].toJS, - ); - return web.URL.createObjectURL(blob as JSObject); - }, - ), - (final AssetMapBitmap assetMapBitmap) => ui_web.assetManager.getAssetUrl( - assetMapBitmap.assetName, - ), - _ => throw UnimplementedError(), - }; - - final web.HTMLImageElement icon = web.HTMLImageElement()..src = url; - - final gmaps.Size? size = switch (bitmapDescriptor.bitmapScaling) { - MapBitmapScaling.auto => await _getBitmapSize(bitmapDescriptor, url), - MapBitmapScaling.none => null, - }; - _setIconStyle( - icon: icon, - size: size, + return _advancedMarkerIconFromMapBitmap( + bitmapDescriptor, + rotation: rotation, opacity: opacity, isVisible: isVisible, ); - - return icon; } // The following code is for the deprecated BitmapDescriptor.fromBytes // and BitmapDescriptor.fromAssetImage. final List iconConfig = bitmapDescriptor.toJson() as List; if (iconConfig[0] == 'fromAssetImage') { - assert(iconConfig.length >= 2); - // iconConfig[2] contains the DPIs of the screen, but that information is - // already encoded in the iconConfig[1] - final web.HTMLImageElement icon = - web.HTMLImageElement() - ..src = ui_web.assetManager.getAssetUrl(iconConfig[1]! as String); - - final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 3); - _setIconStyle( - icon: icon, - size: size, + return _advancedMarkerIconFromAssetImage( + iconConfig, opacity: opacity, isVisible: isVisible, + rotation: rotation, ); - return icon; } else if (iconConfig[0] == 'fromBytes') { - // Grab the bytes, and put them into a blob. - final List bytes = iconConfig[1]! as List; - // Create a Blob from bytes, but let the browser figure out the encoding. - final web.Blob blob; - - assert( - bytes is Uint8List, - 'The bytes for a BitmapDescriptor icon must be a Uint8List', - ); - - // TODO(ditman): Improve this conversion - // See https://github.com/dart-lang/web/issues/180 - blob = web.Blob([(bytes as Uint8List).toJS].toJS); - - final web.HTMLImageElement icon = - web.HTMLImageElement()..src = web.URL.createObjectURL(blob as JSObject); - - final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); - _setIconStyle( - size: size, - icon: icon, + return _advancedMarkerIconFromBytes( + iconConfig, opacity: opacity, isVisible: isVisible, + rotation: rotation, ); - return icon; } return null; @@ -602,6 +643,8 @@ Future _gmIconFromBitmapDescriptor( (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( bytesMapBitmap.byteData.hashCode, () { + // TODO(ditman): Improve this conversion + // See https://github.com/dart-lang/web/issues/180 final web.Blob blob = web.Blob( [bytesMapBitmap.byteData.toJS].toJS, ); @@ -722,8 +765,8 @@ Future _markerOptionsFromMarker( } /// Gets marker Id from a [marker] object. -MarkerId getMarkerId(Object? marker) { - final JSObject object = marker! as JSObject; +MarkerId getMarkerId(Object marker) { + final JSObject object = marker as JSObject; if (object.isA()) { final gmaps.MVCObject mapObject = marker as gmaps.MVCObject; return MarkerId((mapObject.get('markerId')! as JSString).toDart); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart index 639826e60d0..9af2c585665 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_inspector_web.dart @@ -16,7 +16,7 @@ typedef ConfigurationProvider = MapConfiguration Function(int mapId); /// Function that gets the [ClusterManagersController] for a given `mapId`. typedef ClusterManagersControllerProvider = - ClusterManagersController? Function(int mapId); + ClusterManagersController? Function(int mapId); /// Function that gets the [GroundOverlaysController] for a given `mapId`. typedef GroundOverlaysControllerProvider = diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 33d5fd04c62..e7bd25aaf5e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -7,6 +7,11 @@ part of '../google_maps_flutter_web.dart'; /// The `MarkerController` class wraps a [gmaps.AdvancedMarkerElement] /// or [gmaps.Marker], how it handles events, and its associated (optional) /// [gmaps.InfoWindow] widget. +/// +/// Type parameters: +/// * [T] - The marker type (e.g., [gmaps.Marker] or [gmaps.AdvancedMarkerElement]) +/// * [O] - The options type used to configure the marker +/// (e.g., [gmaps.MarkerOptions] or [gmaps.AdvancedMarkerElementOptions]) abstract class MarkerController { /// Creates a `MarkerController`, which wraps a [gmaps.AdvancedMarkerElement] /// or [gmaps.Marker] object, its `onTap`/`onDrag` behavior, and its @@ -176,7 +181,10 @@ class LegacyMarkerController @override void showInfoWindow() { - assert(_marker != null, 'Cannot `showInfoWindow` on a `remove`d Marker.'); + if (_marker == null) { + throw StateError('Cannot `showInfoWindow` on a `remove`d Marker.'); + } + if (_infoWindow != null) { _infoWindow.open(_marker!.map, _marker); _infoWindowShown = true; @@ -287,7 +295,9 @@ class AdvancedMarkerController @override void showInfoWindow() { - assert(_marker != null, 'Cannot `showInfoWindow` on a `remove`d Marker.'); + if (_marker == null) { + throw StateError('Cannot `showInfoWindow` on a `remove`d Marker.'); + } if (_infoWindow != null) { _infoWindow.open(_marker!.map, _marker); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart index 77cb2e317a9..f43a7d7f74e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering.dart @@ -20,7 +20,7 @@ import 'types.dart'; /// /// [T] must extend [JSObject]. It's not specified in code because our mocking /// framework does not support mocking JSObjects. -class ClusterManagersController extends GeometryController { +class ClusterManagersController extends GeometryController { /// Creates a new [ClusterManagersController] instance. /// /// The [stream] parameter is a required [StreamController] used for diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 3cd6b13201d..1a559b13694 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -11,9 +11,15 @@ part of '../google_maps_flutter_web.dart'; /// * [AdvancedMarkersController] implements the [MarkersController] for the /// advanced [gmaps.AdvancedMarkerElement] class. /// +/// Type parameters: +/// * [T] - The marker type (e.g., [gmaps.Marker] or [gmaps.AdvancedMarkerElement]) +/// * [O] - The options type used to configure the marker +/// (e.g., [gmaps.MarkerOptions] or [gmaps.AdvancedMarkerElementOptions]) +/// /// [T] must extend [JSObject]. It's not specified in code because our mocking /// framework does not support mocking JSObjects. -abstract class MarkersController extends GeometryController { +abstract class MarkersController + extends GeometryController { /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. MarkersController({ required StreamController> stream, From f255a1f4d26da7e61cad5650bd85ae034c396aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Mon, 24 Nov 2025 18:19:52 +0800 Subject: [PATCH 14/23] web advanced marker documentation changes --- .../google_maps_flutter_web/CHANGELOG.md | 7 ++++++- .../google_maps_flutter/google_maps_flutter_web/README.md | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) 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 771e6ae7273..5a6a9000f4d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,6 +1,11 @@ ## 0.6.0 -* Adds Advanced markers support. +* **BREAKING CHANGES**: Adds type constraints to generic type parameters: + * `ClusterManagersController` now requires `T extends Object` + * `MarkersController` now requires `T extends Object` +* Adds support for Google Maps JavaScript API Advanced Markers (`AdvancedMarker`), including new `AdvancedMarkerController` and `AdvancedMarkersController` classes, support for `PinConfig` with customizable background, border, and glyph, and custom marker content via `BitmapDescriptor` (including `AssetMapBitmap`, `BytesMapBitmap`, and `PinConfig`). Advanced markers require the `marker` library - add `&libraries=marker` to your Google Maps API script URL in `web/index.html`. +* Adds `isAdvancedMarkersAvailable()` method to check if advanced markers are supported. +* Refactors marker architecture to support both legacy `Marker` and new `AdvancedMarker` types through unified controller interfaces. ## 0.5.14+3 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index 2dd51dcaf8d..bc697c466d9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -47,6 +47,13 @@ If your app uses Advanced Markers, include `marker` library like this: ``` +## Advanced Markers vs. Legacy Markers + +The Google Maps SDK provides Advanced Markers, which replace the older legacy markers. Advanced Markers offer improved performance, richer customization (including scalable pins, custom HTML-like content, and styling options), and better behavior on vector maps such as collision management and altitude control. Legacy Marker APIs are deprecated, and new features will only be available through the Advanced Marker system. + +For full details, see Google's official documentation: +https://developers.google.com/maps/documentation/javascript/advanced-markers/overview + To request multiple libraries, separate them with commas: ```html From 98d8d50e0c32942c4a09423b3ac3e704bc89a1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Mon, 24 Nov 2025 18:29:42 +0800 Subject: [PATCH 15/23] formatting changes --- .../google_maps_flutter_web/lib/src/marker.dart | 2 +- .../google_maps_flutter_web/lib/src/markers.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index e7bd25aaf5e..70b99f41721 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -184,7 +184,7 @@ class LegacyMarkerController if (_marker == null) { throw StateError('Cannot `showInfoWindow` on a `remove`d Marker.'); } - + if (_infoWindow != null) { _infoWindow.open(_marker!.map, _marker); _infoWindowShown = true; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 1a559b13694..a0fff3446af 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -15,7 +15,7 @@ part of '../google_maps_flutter_web.dart'; /// * [T] - The marker type (e.g., [gmaps.Marker] or [gmaps.AdvancedMarkerElement]) /// * [O] - The options type used to configure the marker /// (e.g., [gmaps.MarkerOptions] or [gmaps.AdvancedMarkerElementOptions]) -/// +/// /// [T] must extend [JSObject]. It's not specified in code because our mocking /// framework does not support mocking JSObjects. abstract class MarkersController From 4d9d914f8374ee6004a03b72aa30a905e7d3e98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Mon, 24 Nov 2025 21:06:41 +0800 Subject: [PATCH 16/23] readme fix --- packages/google_maps_flutter/google_maps_flutter_web/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index bc697c466d9..f6a3a3c64a8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -47,7 +47,7 @@ If your app uses Advanced Markers, include `marker` library like this: ``` -## Advanced Markers vs. Legacy Markers +### Advanced Markers vs. Legacy Markers The Google Maps SDK provides Advanced Markers, which replace the older legacy markers. Advanced Markers offer improved performance, richer customization (including scalable pins, custom HTML-like content, and styling options), and better behavior on vector maps such as collision management and altitude control. Legacy Marker APIs are deprecated, and new features will only be available through the Advanced Marker system. From 5aa1549082df8ce5f2e78bd9754f65248d018c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Mon, 24 Nov 2025 21:37:45 +0800 Subject: [PATCH 17/23] further readme fixes --- .../google_maps_flutter_web/README.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index f6a3a3c64a8..1a0bde9f27a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -40,29 +40,29 @@ circles or legacy markers on a map), include it like this: ``` -If your app uses Advanced Markers, include `marker` library like this: +To request multiple libraries, separate them with commas: + ```html ``` -### Advanced Markers vs. Legacy Markers - -The Google Maps SDK provides Advanced Markers, which replace the older legacy markers. Advanced Markers offer improved performance, richer customization (including scalable pins, custom HTML-like content, and styling options), and better behavior on vector maps such as collision management and altitude control. Legacy Marker APIs are deprecated, and new features will only be available through the Advanced Marker system. +Now you should be able to use the Google Maps plugin normally. -For full details, see Google's official documentation: -https://developers.google.com/maps/documentation/javascript/advanced-markers/overview +## Advanced Markers -To request multiple libraries, separate them with commas: +The Google Maps SDK provides Advanced Markers, which replace the older legacy markers. Advanced Markers offer improved performance, richer customization (including scalable pins, custom HTML-like content, and styling options), and better behavior on vector maps such as collision management and altitude control. Legacy Marker APIs are deprecated, and new features will only be available through the Advanced Marker system. +If your app uses Advanced Markers, include `marker` library like this: ```html ``` -Now you should be able to use the Google Maps plugin normally. +For full details, see Google's official documentation: +https://developers.google.com/maps/documentation/javascript/advanced-markers/overview ## Marker clustering From e3496b892e3807cc55e8c05b41cd5d5830debeae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Tue, 25 Nov 2025 20:46:05 +0800 Subject: [PATCH 18/23] integration test fix --- .../example/integration_test/marker_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart index c769b22c677..facba37667b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart @@ -198,7 +198,7 @@ void main() { expect(() { controller.showInfoWindow(); - }, throwsAssertionError); + }, throwsStateError); }); testWidgets('cannot call hideInfoWindow after remove', ( From 521ae7309e43f21a5aaa865e446907de1133a5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Tue, 25 Nov 2025 21:53:34 +0800 Subject: [PATCH 19/23] flutter analyze fixes --- .../advanced_marker_test.dart | 24 +++---- .../advanced_markers_test.dart | 70 +++++++++---------- .../example/integration_test/marker_test.dart | 10 +-- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart index 46f1a461b6f..3fa58fdee51 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart @@ -102,10 +102,10 @@ void main() { }); testWidgets('update', (WidgetTester tester) async { - final AdvancedMarkerController controller = AdvancedMarkerController( + final controller = AdvancedMarkerController( marker: marker, ); - final gmaps.AdvancedMarkerElementOptions options = + final options = gmaps.AdvancedMarkerElementOptions() ..collisionBehavior = gmaps.CollisionBehavior.OPTIONAL_AND_HIDES_LOWER_PRIORITY @@ -132,7 +132,7 @@ void main() { testWidgets('infoWindow null, showInfoWindow.', ( WidgetTester tester, ) async { - final AdvancedMarkerController controller = AdvancedMarkerController( + final controller = AdvancedMarkerController( marker: marker, ); @@ -142,10 +142,10 @@ void main() { }); testWidgets('showInfoWindow', (WidgetTester tester) async { - final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); - final gmaps.Map map = gmaps.Map(createDivElement()); + final infoWindow = gmaps.InfoWindow(); + final map = gmaps.Map(createDivElement()); marker.map = map; - final AdvancedMarkerController controller = AdvancedMarkerController( + final controller = AdvancedMarkerController( marker: marker, infoWindow: infoWindow, ); @@ -157,10 +157,10 @@ void main() { }); testWidgets('hideInfoWindow', (WidgetTester tester) async { - final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); - final gmaps.Map map = gmaps.Map(createDivElement()); + final infoWindow = gmaps.InfoWindow(); + final map = gmaps.Map(createDivElement()); marker.map = map; - final AdvancedMarkerController controller = AdvancedMarkerController( + final controller = AdvancedMarkerController( marker: marker, infoWindow: infoWindow, ); @@ -175,8 +175,8 @@ void main() { late AdvancedMarkerController controller; setUp(() { - final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); - final gmaps.Map map = gmaps.Map(createDivElement()); + final infoWindow = gmaps.InfoWindow(); + final map = gmaps.Map(createDivElement()); marker.map = map; controller = AdvancedMarkerController( marker: marker, @@ -193,7 +193,7 @@ void main() { testWidgets('cannot call update after remove', ( WidgetTester tester, ) async { - final gmaps.AdvancedMarkerElementOptions options = + final options = gmaps.AdvancedMarkerElementOptions()..gmpDraggable = true; controller.remove(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart index f5c65f2be6b..11b7b9df35e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_markers_test.dart @@ -52,7 +52,7 @@ void main() { }); testWidgets('addMarkers', (WidgetTester tester) async { - final Set markers = { + final markers = { AdvancedMarker(markerId: const MarkerId('1')), AdvancedMarker(markerId: const MarkerId('2')), }; @@ -69,7 +69,7 @@ void main() { gmaps.AdvancedMarkerElement? marker; gmaps.LatLngLiteral? position; - final Set markers = { + final markers = { AdvancedMarker(markerId: const MarkerId('1')), }; await controller.addMarkers(markers); @@ -85,7 +85,7 @@ void main() { expect(position.lng, equals(0)); // Update the marker with draggable and position. - final Set updatedMarkers = { + final updatedMarkers = { AdvancedMarker( markerId: const MarkerId('1'), draggable: true, @@ -111,7 +111,7 @@ void main() { gmaps.AdvancedMarkerElement? marker; gmaps.LatLngLiteral? position; - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), position: const LatLng(42, 54), @@ -129,7 +129,7 @@ void main() { expect(position.lng, equals(54)); // Update the marker without position. - final Set updatedMarkers = { + final updatedMarkers = { AdvancedMarker(markerId: const MarkerId('1'), draggable: true), }; await controller.changeMarkers(updatedMarkers); @@ -147,7 +147,7 @@ void main() { ); testWidgets('removeMarkers', (WidgetTester tester) async { - final Set markers = { + final markers = { AdvancedMarker(markerId: const MarkerId('1')), AdvancedMarker(markerId: const MarkerId('2')), AdvancedMarker(markerId: const MarkerId('3')), @@ -158,7 +158,7 @@ void main() { expect(controller.markers.length, 3); // Remove some markers. - final Set markerIdsToRemove = { + final markerIdsToRemove = { const MarkerId('1'), const MarkerId('3'), }; @@ -172,7 +172,7 @@ void main() { }); testWidgets('InfoWindow show/hide', (WidgetTester tester) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), infoWindow: const InfoWindow(title: 'Title', snippet: 'Snippet'), @@ -195,7 +195,7 @@ void main() { testWidgets('only single InfoWindow is visible', ( WidgetTester tester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), infoWindow: const InfoWindow(title: 'Title', snippet: 'Snippet'), @@ -224,7 +224,7 @@ void main() { testWidgets('markers with custom asset icon work', ( WidgetTester tester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: AssetMapBitmap('assets/red_square.png', imagePixelRatio: 1.0), @@ -234,7 +234,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; expect(icon, isNotNull); @@ -250,7 +250,7 @@ void main() { testWidgets('markers with custom asset icon and pixel ratio work', ( WidgetTester tester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: AssetMapBitmap('assets/red_square.png', imagePixelRatio: 2.0), @@ -260,7 +260,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; expect(icon, isNotNull); @@ -277,7 +277,7 @@ void main() { testWidgets('markers with custom asset icon with width and height work', ( WidgetTester tester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: AssetMapBitmap( @@ -292,7 +292,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; expect(icon, isNotNull); @@ -309,7 +309,7 @@ void main() { testWidgets('markers with missing asset icon should not set size', ( WidgetTester tester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: AssetMapBitmap( @@ -322,7 +322,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; expect(icon, isNotNull); @@ -339,7 +339,7 @@ void main() { WidgetTester tester, ) async { final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: BytesMapBitmap( @@ -352,7 +352,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; expect(icon, isNotNull); @@ -380,7 +380,7 @@ void main() { WidgetTester tester, ) async { final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: BytesMapBitmap(bytes, imagePixelRatio: 1), @@ -390,7 +390,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; expect(icon, isNotNull); @@ -406,7 +406,7 @@ void main() { WidgetTester tester, ) async { final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: BytesMapBitmap(bytes, width: 20, height: 30), @@ -416,7 +416,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLImageElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLImageElement?; expect(icon, isNotNull); @@ -427,7 +427,7 @@ void main() { testWidgets('markers created with text glyph work', ( WidgetTester widgetTester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: BitmapDescriptor.pinConfig( @@ -440,7 +440,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLDivElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; expect(icon, isNotNull); @@ -450,8 +450,8 @@ void main() { // the public API. dom.Element? paragraphElement; final NodeList paragraphs = icon!.querySelectorAll('p'); - for (int i = 0; i < paragraphs.length; i++) { - final dom.Element? paragraph = paragraphs.item(i) as dom.Element?; + for (var i = 0; i < paragraphs.length; i++) { + final paragraph = paragraphs.item(i) as dom.Element?; if (paragraph?.innerHTML.toString() == 'Hey') { paragraphElement = paragraph; break; @@ -465,7 +465,7 @@ void main() { testWidgets('markers created with bitmap glyph work', ( WidgetTester widgetTester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), icon: BitmapDescriptor.pinConfig( @@ -483,7 +483,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLDivElement? icon = + final icon = controller.markers[const MarkerId('1')]?.marker?.content as HTMLDivElement?; expect(icon, isNotNull); @@ -493,8 +493,8 @@ void main() { // the public API. HTMLImageElement? imgElement; final NodeList imgElements = icon!.querySelectorAll('img'); - for (int i = 0; i < imgElements.length; i++) { - final dom.Element? img = imgElements.item(i) as dom.Element?; + for (var i = 0; i < imgElements.length; i++) { + final img = imgElements.item(i) as dom.Element?; final String src = (img! as HTMLImageElement).src; if (src.endsWith('assets/red_square.png')) { imgElement = img as HTMLImageElement; @@ -509,7 +509,7 @@ void main() { testWidgets('InfoWindow snippet can have links', ( WidgetTester tester, ) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), infoWindow: const InfoWindow( @@ -522,7 +522,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLElement? content = + final content = controller.markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; expect(content, isNotNull); @@ -538,7 +538,7 @@ void main() { }); testWidgets('InfoWindow content is clickable', (WidgetTester tester) async { - final Set markers = { + final markers = { AdvancedMarker( markerId: const MarkerId('1'), infoWindow: const InfoWindow( @@ -551,7 +551,7 @@ void main() { await controller.addMarkers(markers); expect(controller.markers.length, 1); - final HTMLElement? content = + final content = controller.markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart index facba37667b..43c571e02bd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart @@ -102,10 +102,10 @@ void main() { }); testWidgets('update', (WidgetTester tester) async { - final LegacyMarkerController controller = LegacyMarkerController( + final controller = LegacyMarkerController( marker: marker, ); - final gmaps.MarkerOptions options = gmaps.MarkerOptions() + final options = gmaps.MarkerOptions() ..draggable = true ..position = gmaps.LatLng(42, 54); @@ -121,7 +121,7 @@ void main() { testWidgets('infoWindow null, showInfoWindow.', ( WidgetTester tester, ) async { - final LegacyMarkerController controller = LegacyMarkerController( + final controller = LegacyMarkerController( marker: marker, ); @@ -134,7 +134,7 @@ void main() { final infoWindow = gmaps.InfoWindow(); final map = gmaps.Map(createDivElement()); marker.set('map', map); - final LegacyMarkerController controller = LegacyMarkerController( + final controller = LegacyMarkerController( marker: marker, infoWindow: infoWindow, ); @@ -149,7 +149,7 @@ void main() { final infoWindow = gmaps.InfoWindow(); final map = gmaps.Map(createDivElement()); marker.set('map', map); - final LegacyMarkerController controller = LegacyMarkerController( + final controller = LegacyMarkerController( marker: marker, infoWindow: infoWindow, ); From 740eba46b6469eb0259003f0e30d9a7e40765186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Wed, 26 Nov 2025 16:02:00 +0800 Subject: [PATCH 20/23] flutter analyze and format fixes for web package --- .../advanced_marker_test.dart | 23 +++--- .../example/integration_test/marker_test.dart | 8 +- .../lib/src/convert.dart | 77 +++++++++---------- .../lib/src/marker_clustering_js_interop.dart | 2 +- .../lib/src/markers.dart | 9 +-- .../lib/src/overlay.dart | 3 +- 6 files changed, 53 insertions(+), 69 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart index 3fa58fdee51..7555d211a9d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart @@ -102,15 +102,12 @@ void main() { }); testWidgets('update', (WidgetTester tester) async { - final controller = AdvancedMarkerController( - marker: marker, - ); - final options = - gmaps.AdvancedMarkerElementOptions() - ..collisionBehavior = - gmaps.CollisionBehavior.OPTIONAL_AND_HIDES_LOWER_PRIORITY - ..gmpDraggable = true - ..position = gmaps.LatLng(42, 54); + final controller = AdvancedMarkerController(marker: marker); + final options = gmaps.AdvancedMarkerElementOptions() + ..collisionBehavior = + gmaps.CollisionBehavior.OPTIONAL_AND_HIDES_LOWER_PRIORITY + ..gmpDraggable = true + ..position = gmaps.LatLng(42, 54); expect(marker.collisionBehavior, gmaps.CollisionBehavior.REQUIRED); expect(marker.gmpDraggable, isFalse); @@ -132,9 +129,7 @@ void main() { testWidgets('infoWindow null, showInfoWindow.', ( WidgetTester tester, ) async { - final controller = AdvancedMarkerController( - marker: marker, - ); + final controller = AdvancedMarkerController(marker: marker); controller.showInfoWindow(); @@ -193,8 +188,8 @@ void main() { testWidgets('cannot call update after remove', ( WidgetTester tester, ) async { - final options = - gmaps.AdvancedMarkerElementOptions()..gmpDraggable = true; + final options = gmaps.AdvancedMarkerElementOptions() + ..gmpDraggable = true; controller.remove(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart index 43c571e02bd..f2bc3e69bda 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart @@ -102,9 +102,7 @@ void main() { }); testWidgets('update', (WidgetTester tester) async { - final controller = LegacyMarkerController( - marker: marker, - ); + final controller = LegacyMarkerController(marker: marker); final options = gmaps.MarkerOptions() ..draggable = true ..position = gmaps.LatLng(42, 54); @@ -121,9 +119,7 @@ void main() { testWidgets('infoWindow null, showInfoWindow.', ( WidgetTester tester, ) async { - final controller = LegacyMarkerController( - marker: marker, - ); + final controller = LegacyMarkerController(marker: marker); controller.showInfoWindow(); 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 5fef6207260..ed963650792 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 @@ -273,10 +273,9 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { ..id = 'gmaps-marker-${marker.markerId.value}-infowindow'; if (markerTitle.isNotEmpty) { - final web.HTMLHeadingElement title = - (web.document.createElement('h3') as web.HTMLHeadingElement) - ..className = 'infowindow-title' - ..innerText = markerTitle; + final title = (web.document.createElement('h3') as web.HTMLHeadingElement) + ..className = 'infowindow-title' + ..innerText = markerTitle; container.appendChild(title); } if (markerSnippet.isNotEmpty) { @@ -366,7 +365,7 @@ void _setIconAnchor({ // Sets the size of the Google Maps icon. void _setIconSize({required gmaps.Size size, required gmaps.Icon icon}) { - final gmaps.Size gmapsSize = gmaps.Size(size.width, size.height); + final gmapsSize = gmaps.Size(size.width, size.height); icon.size = gmapsSize; icon.scaledSize = gmapsSize; } @@ -422,7 +421,7 @@ Future _getBitmapSize(MapBitmap mapBitmap, String url) async { /// /// This method attempts to fetch the image size for a given [url]. Future _fetchBitmapSize(String url) async { - final web.HTMLImageElement image = web.HTMLImageElement()..src = url; + final image = web.HTMLImageElement()..src = url; // Wait for the onLoad or onError event. await Future.any(>[ @@ -455,7 +454,7 @@ Future _advancedMarkerIconFromPinConfig( required bool isVisible, required double rotation, }) async { - final gmaps.PinElementOptions options = gmaps.PinElementOptions() + final options = gmaps.PinElementOptions() ..background = config.backgroundColor != null ? _getCssColor(config.backgroundColor!) : null @@ -468,7 +467,7 @@ Future _advancedMarkerIconFromPinConfig( case final CircleGlyph circleGlyph: options.glyphColor = _getCssColor(circleGlyph.color); case final TextGlyph textGlyph: - final web.HTMLParagraphElement element = web.HTMLParagraphElement(); + final element = web.HTMLParagraphElement(); element.text = textGlyph.text; if (textGlyph.textColor != null) { element.style.color = _getCssColor(textGlyph.textColor!); @@ -489,7 +488,7 @@ Future _advancedMarkerIconFromPinConfig( break; } - final gmaps.PinElement pinElement = gmaps.PinElement(options); + final pinElement = gmaps.PinElement(options); final web.HTMLElement htmlElement = pinElement.element; htmlElement.style ..visibility = isVisible ? 'visible' : 'hidden' @@ -508,7 +507,7 @@ Future _advancedMarkerIconFromMapBitmap( (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( bytesMapBitmap.byteData.hashCode, () { - final web.Blob blob = web.Blob( + final blob = web.Blob( [bytesMapBitmap.byteData.toJS].toJS, ); return web.URL.createObjectURL(blob as JSObject); @@ -520,7 +519,7 @@ Future _advancedMarkerIconFromMapBitmap( _ => throw UnimplementedError(), }; - final web.HTMLImageElement icon = web.HTMLImageElement()..src = url; + final icon = web.HTMLImageElement()..src = url; final gmaps.Size? size = switch (bitmap.bitmapScaling) { MapBitmapScaling.auto => await _getBitmapSize(bitmap, url), @@ -540,7 +539,7 @@ Future _advancedMarkerIconFromAssetImage( assert(iconConfig.length >= 2); // iconConfig[2] contains the DPIs of the screen, but that information is // already encoded in the iconConfig[1] - final web.HTMLImageElement icon = web.HTMLImageElement() + final icon = web.HTMLImageElement() ..src = ui_web.assetManager.getAssetUrl(iconConfig[1]! as String); final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 3); @@ -555,7 +554,7 @@ Future _advancedMarkerIconFromBytes( required double rotation, }) async { // Grab the bytes, and put them into a blob. - final List bytes = iconConfig[1]! as List; + final bytes = iconConfig[1]! as List; // Create a Blob from bytes, but let the browser figure out the encoding. final web.Blob blob; @@ -568,7 +567,7 @@ Future _advancedMarkerIconFromBytes( // See https://github.com/dart-lang/web/issues/180 blob = web.Blob([(bytes as Uint8List).toJS].toJS); - final web.HTMLImageElement icon = web.HTMLImageElement() + final icon = web.HTMLImageElement() ..src = web.URL.createObjectURL(blob as JSObject); final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); @@ -604,7 +603,7 @@ Future _advancedMarkerIconFromBitmapDescriptor( // The following code is for the deprecated BitmapDescriptor.fromBytes // and BitmapDescriptor.fromAssetImage. - final List iconConfig = bitmapDescriptor.toJson() as List; + final iconConfig = bitmapDescriptor.toJson() as List; if (iconConfig[0] == 'fromAssetImage') { return _advancedMarkerIconFromAssetImage( iconConfig, @@ -638,7 +637,7 @@ Future _gmIconFromBitmapDescriptor( () { // TODO(ditman): Improve this conversion // See https://github.com/dart-lang/web/issues/180 - final web.Blob blob = web.Blob( + final blob = web.Blob( [bytesMapBitmap.byteData.toJS].toJS, ); return web.URL.createObjectURL(blob as JSObject); @@ -716,27 +715,26 @@ Future _markerOptionsFromMarker( T? currentMarker, ) async { if (marker is AdvancedMarker) { - final gmaps.AdvancedMarkerElementOptions options = - gmaps.AdvancedMarkerElementOptions() - ..collisionBehavior = _markerCollisionBehaviorToGmCollisionBehavior( - marker.collisionBehavior, - ) - ..content = await _advancedMarkerIconFromBitmapDescriptor( - marker.icon, - opacity: marker.alpha, - isVisible: marker.visible, - rotation: marker.rotation, - ) - ..position = gmaps.LatLng( - marker.position.latitude, - marker.position.longitude, - ) - ..title = sanitizeHtml(marker.infoWindow.title ?? '') - ..zIndex = marker.zIndex - ..gmpDraggable = marker.draggable; + final options = gmaps.AdvancedMarkerElementOptions() + ..collisionBehavior = _markerCollisionBehaviorToGmCollisionBehavior( + marker.collisionBehavior, + ) + ..content = await _advancedMarkerIconFromBitmapDescriptor( + marker.icon, + opacity: marker.alpha, + isVisible: marker.visible, + rotation: marker.rotation, + ) + ..position = gmaps.LatLng( + marker.position.latitude, + marker.position.longitude, + ) + ..title = sanitizeHtml(marker.infoWindow.title ?? '') + ..zIndex = marker.zIndex + ..gmpDraggable = marker.draggable; return options as O; } else { - final gmaps.MarkerOptions options = gmaps.MarkerOptions() + final options = gmaps.MarkerOptions() ..position = gmaps.LatLng( marker.position.latitude, marker.position.longitude, @@ -757,13 +755,12 @@ Future _markerOptionsFromMarker( /// Gets marker Id from a [marker] object. MarkerId getMarkerId(Object marker) { - final JSObject object = marker as JSObject; + final object = marker as JSObject; if (object.isA()) { - final gmaps.MVCObject mapObject = marker as gmaps.MVCObject; + final mapObject = marker as gmaps.MVCObject; return MarkerId((mapObject.get('markerId')! as JSString).toDart); } else if (object.isA()) { - final gmaps.AdvancedMarkerElement element = - marker as gmaps.AdvancedMarkerElement; + final element = marker as gmaps.AdvancedMarkerElement; return MarkerId(element.id); } else { throw ArgumentError( @@ -1005,7 +1002,7 @@ String urlFromMapBitmap(MapBitmap mapBitmap) { (final BytesMapBitmap bytesMapBitmap) => _bitmapBlobUrlCache.putIfAbsent( bytesMapBitmap.byteData.hashCode, () { - final web.Blob blob = web.Blob( + final blob = web.Blob( [bytesMapBitmap.byteData.toJS].toJS, ); return web.URL.createObjectURL(blob as JSObject); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart index 43d07768794..fc639778706 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker_clustering_js_interop.dart @@ -157,7 +157,7 @@ MarkerClusterer createMarkerClusterer( gmaps.Map map, ClusterClickHandler onClusterClickHandler, ) { - final MarkerClustererOptions options = MarkerClustererOptions( + final options = MarkerClustererOptions( map: map, onClusterClick: onClusterClickHandler, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 3a428c25c1b..7dd31d1c544 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -58,8 +58,7 @@ abstract class MarkersController // we make one... if (infoWindowOptions.content != null && infoWindowOptions.content is web.HTMLElement) { - final web.HTMLElement content = - infoWindowOptions.content! as web.HTMLElement; + final content = infoWindowOptions.content! as web.HTMLElement; content.onclick = (JSAny? _) { _onInfoWindowTap(marker.markerId); @@ -233,7 +232,7 @@ class LegacyMarkersController gmaps.MarkerOptions markerOptions, gmaps.InfoWindow? gmInfoWindow, ) async { - final gmaps.Marker gmMarker = gmaps.Marker(markerOptions); + final gmMarker = gmaps.Marker(markerOptions); gmMarker.set('markerId', marker.markerId.value.toJS); if (marker.clusterManagerId != null) { @@ -286,9 +285,7 @@ class AdvancedMarkersController ) async { assert(marker is AdvancedMarker, 'Marker must be an AdvancedMarker.'); - final gmaps.AdvancedMarkerElement gmMarker = gmaps.AdvancedMarkerElement( - markerOptions, - ); + final gmMarker = gmaps.AdvancedMarkerElement(markerOptions); gmMarker.setAttribute('id', marker.markerId.value); if (marker.clusterManagerId != null) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart index ef61e2fb35e..c8054cd4911 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart @@ -40,8 +40,7 @@ class TileOverlayController { return null; } - final web.HTMLImageElement img = - ownerDocument!.createElement('img') as web.HTMLImageElement; + final img = ownerDocument!.createElement('img') as web.HTMLImageElement; img.width = img.height = logicalTileSize; img.hidden = true.toJS; img.setAttribute('decoding', 'async'); From 732d5952325b5793dc2a7653695ad20818858162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Wed, 26 Nov 2025 16:35:06 +0800 Subject: [PATCH 21/23] integration test fix --- .../example/integration_test/advanced_marker_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart index 7555d211a9d..5b464df10a8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/advanced_marker_test.dart @@ -205,7 +205,7 @@ void main() { expect(() { controller.showInfoWindow(); - }, throwsAssertionError); + }, throwsStateError); }); testWidgets('cannot call hideInfoWindow after remove', ( From 69f740da68961ab66d7a7a2f79d3fd2cc9b436f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Wed, 26 Nov 2025 17:37:08 +0800 Subject: [PATCH 22/23] update integration test mocks --- .../google_maps_controller_test.mocks.dart | 115 +++++++++--------- .../google_maps_plugin_test.mocks.dart | 3 +- .../integration_test/overlays_test.mocks.dart | 3 +- 3 files changed, 62 insertions(+), 59 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index 4e9c3782fec..e074e5c6329 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart. // Do not manually edit this file. @@ -26,6 +26,7 @@ import 'google_maps_controller_test.dart' as _i5; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeMarkerController_0 extends _i1.SmartFake implements _i2.MarkerController { @@ -55,12 +56,6 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { ) as _i4.Map); - @override - set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); - @override int get mapId => (super.noSuchMethod( @@ -71,8 +66,14 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController { as int); @override - set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), + set googleMap(_i4.Map? value) => super.noSuchMethod( + Invocation.setter(#googleMap, value), + returnValueForMissingStub: null, + ); + + @override + set mapId(int? value) => super.noSuchMethod( + Invocation.setter(#mapId, value), returnValueForMissingStub: null, ); @@ -125,12 +126,6 @@ class MockHeatmapsController extends _i1.Mock ) as _i4.Map); - @override - set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); - @override int get mapId => (super.noSuchMethod( @@ -141,8 +136,14 @@ class MockHeatmapsController extends _i1.Mock as int); @override - set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), + set googleMap(_i4.Map? value) => super.noSuchMethod( + Invocation.setter(#googleMap, value), + returnValueForMissingStub: null, + ); + + @override + set mapId(int? value) => super.noSuchMethod( + Invocation.setter(#mapId, value), returnValueForMissingStub: null, ); @@ -195,12 +196,6 @@ class MockPolygonsController extends _i1.Mock ) as _i4.Map); - @override - set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); - @override int get mapId => (super.noSuchMethod( @@ -211,8 +206,14 @@ class MockPolygonsController extends _i1.Mock as int); @override - set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), + set googleMap(_i4.Map? value) => super.noSuchMethod( + Invocation.setter(#googleMap, value), + returnValueForMissingStub: null, + ); + + @override + set mapId(int? value) => super.noSuchMethod( + Invocation.setter(#mapId, value), returnValueForMissingStub: null, ); @@ -266,12 +267,6 @@ class MockPolylinesController extends _i1.Mock ) as _i4.Map); - @override - set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); - @override int get mapId => (super.noSuchMethod( @@ -282,8 +277,14 @@ class MockPolylinesController extends _i1.Mock as int); @override - set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), + set googleMap(_i4.Map? value) => super.noSuchMethod( + Invocation.setter(#googleMap, value), + returnValueForMissingStub: null, + ); + + @override + set mapId(int? value) => super.noSuchMethod( + Invocation.setter(#mapId, value), returnValueForMissingStub: null, ); @@ -339,12 +340,6 @@ class MockMarkersController extends _i1.Mock ) as _i4.Map); - @override - set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); - @override int get mapId => (super.noSuchMethod( @@ -355,8 +350,14 @@ class MockMarkersController extends _i1.Mock as int); @override - set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), + set googleMap(_i4.Map? value) => super.noSuchMethod( + Invocation.setter(#googleMap, value), + returnValueForMissingStub: null, + ); + + @override + set mapId(int? value) => super.noSuchMethod( + Invocation.setter(#mapId, value), returnValueForMissingStub: null, ); @@ -464,12 +465,6 @@ class MockTileOverlaysController extends _i1.Mock ) as _i4.Map); - @override - set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); - @override int get mapId => (super.noSuchMethod( @@ -480,8 +475,14 @@ class MockTileOverlaysController extends _i1.Mock as int); @override - set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), + set googleMap(_i4.Map? value) => super.noSuchMethod( + Invocation.setter(#googleMap, value), + returnValueForMissingStub: null, + ); + + @override + set mapId(int? value) => super.noSuchMethod( + Invocation.setter(#mapId, value), returnValueForMissingStub: null, ); @@ -533,12 +534,6 @@ class MockGroundOverlaysController extends _i1.Mock ) as _i4.Map); - @override - set googleMap(_i4.Map? _googleMap) => super.noSuchMethod( - Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null, - ); - @override int get mapId => (super.noSuchMethod( @@ -549,8 +544,14 @@ class MockGroundOverlaysController extends _i1.Mock as int); @override - set mapId(int? _mapId) => super.noSuchMethod( - Invocation.setter(#mapId, _mapId), + set googleMap(_i4.Map? value) => super.noSuchMethod( + Invocation.setter(#googleMap, value), + returnValueForMissingStub: null, + ); + + @override + set mapId(int? value) => super.noSuchMethod( + Invocation.setter(#mapId, value), returnValueForMissingStub: null, ); 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 8e12f5791ad..4a8fa105c25 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 @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_plugin_test.dart. // Do not manually edit this file. @@ -25,6 +25,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeMapConfiguration_0 extends _i1.SmartFake implements _i2.MapConfiguration { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart index 29d641f123b..6abd4a780e6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in google_maps_flutter_web_integration_tests/integration_test/overlays_test.dart. // Do not manually edit this file. @@ -22,6 +22,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeTile_0 extends _i1.SmartFake implements _i2.Tile { _FakeTile_0(Object parent, Invocation parentInvocation) From 276d88bd1a33c014408c767cde4f5ffecdad9f37 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Wed, 26 Nov 2025 13:52:47 +0200 Subject: [PATCH 23/23] fix: MarkersController mock --- .../google_maps_controller_test.dart | 2 +- .../google_maps_controller_test.mocks.dart | 40 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) 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 7f8ff45739f..931465c7b95 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 @@ -35,7 +35,7 @@ gmaps.Map mapShim() => throw UnimplementedError(); MockSpec( fallbackGenerators: {#googleMap: mapShim}, ), - MockSpec>( + MockSpec>( fallbackGenerators: {#googleMap: mapShim}, ), MockSpec( diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index e074e5c6329..c08034d58f1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -319,17 +319,16 @@ class MockPolylinesController extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockMarkersController extends _i1.Mock - implements _i2.MarkersController { + implements _i2.MarkersController { @override - Map<_i3.MarkerId, _i2.MarkerController> get markers => + Map<_i3.MarkerId, _i2.MarkerController> get markers => (super.noSuchMethod( Invocation.getter(#markers), - returnValue: - <_i3.MarkerId, _i2.MarkerController>{}, + returnValue: <_i3.MarkerId, _i2.MarkerController>{}, returnValueForMissingStub: - <_i3.MarkerId, _i2.MarkerController>{}, + <_i3.MarkerId, _i2.MarkerController>{}, ) - as Map<_i3.MarkerId, _i2.MarkerController>); + as Map<_i3.MarkerId, _i2.MarkerController>); @override _i4.Map get googleMap => @@ -371,7 +370,7 @@ class MockMarkersController extends _i1.Mock as _i6.Future); @override - _i6.Future<_i2.MarkerController> createMarkerController( + _i6.Future<_i2.MarkerController> createMarkerController( _i3.Marker? marker, Object? markerOptions, _i4.InfoWindow? gmInfoWindow, @@ -382,20 +381,19 @@ class MockMarkersController extends _i1.Mock markerOptions, gmInfoWindow, ]), - returnValue: - _i6.Future<_i2.MarkerController>.value( - _FakeMarkerController_0( - this, - Invocation.method(#createMarkerController, [ - marker, - markerOptions, - gmInfoWindow, - ]), - ), - ), + returnValue: _i6.Future<_i2.MarkerController>.value( + _FakeMarkerController_0( + this, + Invocation.method(#createMarkerController, [ + marker, + markerOptions, + gmInfoWindow, + ]), + ), + ), returnValueForMissingStub: - _i6.Future<_i2.MarkerController>.value( - _FakeMarkerController_0( + _i6.Future<_i2.MarkerController>.value( + _FakeMarkerController_0( this, Invocation.method(#createMarkerController, [ marker, @@ -405,7 +403,7 @@ class MockMarkersController extends _i1.Mock ), ), ) - as _i6.Future<_i2.MarkerController>); + as _i6.Future<_i2.MarkerController>); @override _i6.Future changeMarkers(Set<_i3.Marker>? markersToChange) =>