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 d766e0d57968..68e6a3f5b0b9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 0.4.0+2 +* Updates conversion of `BitmapDescriptor.fromBytes` marker icons to support the + new `size` parameter. Issue [#73789](https://github.com/flutter/flutter/issues/73789). * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 0.4.0+1 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 9565935bd8ed..a7fe6bae347b 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,7 +1,8 @@ -// Mocks generated by Mockito 5.2.0 from annotations +// Mocks generated by Mockito 5.3.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:google_maps/google_maps.dart' as _i2; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' as _i4; @@ -17,8 +18,12 @@ import 'package:mockito/mockito.dart' as _i1; // 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 _FakeGMap_0 extends _i1.Fake implements _i2.GMap {} +class _FakeGMap_0 extends _i1.SmartFake implements _i2.GMap { + _FakeGMap_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} /// A class which mocks [CirclesController]. /// @@ -27,18 +32,21 @@ class MockCirclesController extends _i1.Mock implements _i3.CirclesController { @override Map<_i4.CircleId, _i3.CircleController> get circles => (super.noSuchMethod(Invocation.getter(#circles), - returnValue: <_i4.CircleId, _i3.CircleController>{}) + returnValue: <_i4.CircleId, _i3.CircleController>{}, + returnValueForMissingStub: <_i4.CircleId, _i3.CircleController>{}) as Map<_i4.CircleId, _i3.CircleController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -67,20 +75,23 @@ class MockCirclesController extends _i1.Mock implements _i3.CirclesController { class MockPolygonsController extends _i1.Mock implements _i3.PolygonsController { @override - Map<_i4.PolygonId, _i3.PolygonController> get polygons => - (super.noSuchMethod(Invocation.getter(#polygons), - returnValue: <_i4.PolygonId, _i3.PolygonController>{}) - as Map<_i4.PolygonId, _i3.PolygonController>); + Map<_i4.PolygonId, _i3.PolygonController> get polygons => (super.noSuchMethod( + Invocation.getter(#polygons), + returnValue: <_i4.PolygonId, _i3.PolygonController>{}, + returnValueForMissingStub: <_i4.PolygonId, _i3.PolygonController>{}) + as Map<_i4.PolygonId, _i3.PolygonController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -109,20 +120,23 @@ class MockPolygonsController extends _i1.Mock class MockPolylinesController extends _i1.Mock implements _i3.PolylinesController { @override - Map<_i4.PolylineId, _i3.PolylineController> get lines => - (super.noSuchMethod(Invocation.getter(#lines), - returnValue: <_i4.PolylineId, _i3.PolylineController>{}) - as Map<_i4.PolylineId, _i3.PolylineController>); + Map<_i4.PolylineId, _i3.PolylineController> get lines => (super.noSuchMethod( + Invocation.getter(#lines), + returnValue: <_i4.PolylineId, _i3.PolylineController>{}, + returnValueForMissingStub: <_i4.PolylineId, _i3.PolylineController>{}) + as Map<_i4.PolylineId, _i3.PolylineController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -152,18 +166,21 @@ class MockMarkersController extends _i1.Mock implements _i3.MarkersController { @override Map<_i4.MarkerId, _i3.MarkerController> get markers => (super.noSuchMethod(Invocation.getter(#markers), - returnValue: <_i4.MarkerId, _i3.MarkerController>{}) + returnValue: <_i4.MarkerId, _i3.MarkerController>{}, + returnValueForMissingStub: <_i4.MarkerId, _i3.MarkerController>{}) as Map<_i4.MarkerId, _i3.MarkerController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -191,7 +208,7 @@ class MockMarkersController extends _i1.Mock implements _i3.MarkersController { @override bool isInfoWindowShown(_i4.MarkerId? markerId) => (super.noSuchMethod(Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false) as bool); + returnValue: false, returnValueForMissingStub: false) as bool); @override void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), 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 18012f471a53..04cbc4a3416d 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,7 +1,8 @@ -// Mocks generated by Mockito 5.2.0 from annotations +// Mocks generated by Mockito 5.3.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_plugin_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i2; import 'package:google_maps/google_maps.dart' as _i5; @@ -19,16 +20,29 @@ import 'package:mockito/mockito.dart' as _i1; // 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 _FakeStreamController_0 extends _i1.Fake - implements _i2.StreamController {} +class _FakeStreamController_0 extends _i1.SmartFake + implements _i2.StreamController { + _FakeStreamController_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} -class _FakeLatLngBounds_1 extends _i1.Fake implements _i3.LatLngBounds {} +class _FakeLatLngBounds_1 extends _i1.SmartFake implements _i3.LatLngBounds { + _FakeLatLngBounds_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} -class _FakeScreenCoordinate_2 extends _i1.Fake - implements _i3.ScreenCoordinate {} +class _FakeScreenCoordinate_2 extends _i1.SmartFake + implements _i3.ScreenCoordinate { + _FakeScreenCoordinate_2(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} -class _FakeLatLng_3 extends _i1.Fake implements _i3.LatLng {} +class _FakeLatLng_3 extends _i1.SmartFake implements _i3.LatLng { + _FakeLatLng_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} /// A class which mocks [GoogleMapController]. /// @@ -36,19 +50,23 @@ class _FakeLatLng_3 extends _i1.Fake implements _i3.LatLng {} class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapController { @override - _i2.StreamController<_i3.MapEvent> get stream => - (super.noSuchMethod(Invocation.getter(#stream), - returnValue: _FakeStreamController_0<_i3.MapEvent>()) - as _i2.StreamController<_i3.MapEvent>); + _i2.StreamController<_i3.MapEvent> get stream => (super.noSuchMethod( + Invocation.getter(#stream), + returnValue: _FakeStreamController_0<_i3.MapEvent>( + this, Invocation.getter(#stream)), + returnValueForMissingStub: _FakeStreamController_0<_i3.MapEvent>( + this, Invocation.getter(#stream))) as _i2 + .StreamController<_i3.MapEvent>); @override - _i2.Stream<_i3.MapEvent> get events => - (super.noSuchMethod(Invocation.getter(#events), - returnValue: Stream<_i3.MapEvent>.empty()) - as _i2.Stream<_i3.MapEvent>); + _i2.Stream<_i3.MapEvent> get events => (super.noSuchMethod( + Invocation.getter(#events), + returnValue: _i2.Stream<_i3.MapEvent>.empty(), + returnValueForMissingStub: _i2.Stream<_i3.MapEvent>.empty()) + as _i2.Stream<_i3.MapEvent>); @override bool get isInitialized => - (super.noSuchMethod(Invocation.getter(#isInitialized), returnValue: false) - as bool); + (super.noSuchMethod(Invocation.getter(#isInitialized), + returnValue: false, returnValueForMissingStub: false) as bool); @override void debugSetOverrides( {_i4.DebugCreateMapFunction? createMap, @@ -78,29 +96,43 @@ class MockGoogleMapController extends _i1.Mock returnValueForMissingStub: null); @override _i2.Future<_i3.LatLngBounds> getVisibleRegion() => (super.noSuchMethod( - Invocation.method(#getVisibleRegion, []), - returnValue: Future<_i3.LatLngBounds>.value(_FakeLatLngBounds_1())) - as _i2.Future<_i3.LatLngBounds>); + Invocation.method(#getVisibleRegion, []), + returnValue: _i2.Future<_i3.LatLngBounds>.value( + _FakeLatLngBounds_1(this, Invocation.method(#getVisibleRegion, []))), + returnValueForMissingStub: _i2.Future<_i3.LatLngBounds>.value( + _FakeLatLngBounds_1( + this, Invocation.method(#getVisibleRegion, [])))) as _i2 + .Future<_i3.LatLngBounds>); @override _i2.Future<_i3.ScreenCoordinate> getScreenCoordinate(_i3.LatLng? latLng) => (super.noSuchMethod(Invocation.method(#getScreenCoordinate, [latLng]), - returnValue: - Future<_i3.ScreenCoordinate>.value(_FakeScreenCoordinate_2())) + returnValue: _i2.Future<_i3.ScreenCoordinate>.value( + _FakeScreenCoordinate_2( + this, Invocation.method(#getScreenCoordinate, [latLng]))), + returnValueForMissingStub: _i2.Future<_i3.ScreenCoordinate>.value( + _FakeScreenCoordinate_2( + this, Invocation.method(#getScreenCoordinate, [latLng])))) as _i2.Future<_i3.ScreenCoordinate>); @override _i2.Future<_i3.LatLng> getLatLng(_i3.ScreenCoordinate? screenCoordinate) => (super.noSuchMethod(Invocation.method(#getLatLng, [screenCoordinate]), - returnValue: Future<_i3.LatLng>.value(_FakeLatLng_3())) - as _i2.Future<_i3.LatLng>); + returnValue: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( + this, Invocation.method(#getLatLng, [screenCoordinate]))), + returnValueForMissingStub: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( + this, Invocation.method(#getLatLng, [screenCoordinate])))) as _i2 + .Future<_i3.LatLng>); @override _i2.Future moveCamera(_i3.CameraUpdate? cameraUpdate) => (super.noSuchMethod(Invocation.method(#moveCamera, [cameraUpdate]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i2.Future); + returnValue: _i2.Future.value(), + returnValueForMissingStub: _i2.Future.value()) + as _i2.Future); @override _i2.Future getZoomLevel() => (super.noSuchMethod(Invocation.method(#getZoomLevel, []), - returnValue: Future.value(0.0)) as _i2.Future); + returnValue: _i2.Future.value(0.0), + returnValueForMissingStub: _i2.Future.value(0.0)) + as _i2.Future); @override void updateCircles(_i3.CircleUpdates? updates) => super.noSuchMethod(Invocation.method(#updateCircles, [updates]), @@ -128,7 +160,7 @@ class MockGoogleMapController extends _i1.Mock @override bool isInfoWindowShown(_i3.MarkerId? markerId) => (super.noSuchMethod(Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false) as bool); + returnValue: false, returnValueForMissingStub: false) as bool); @override void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); 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 90195ec6397b..e4c4dd7c0cfe 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 @@ -5,8 +5,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; -import 'dart:js_util' show getProperty; import 'dart:typed_data'; +import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; @@ -155,22 +155,46 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - expect(controller.markers[const MarkerId('1')]?.marker?.icon, isNotNull); - - final String blobUrl = getProperty( - controller.markers[const MarkerId('1')]!.marker!.icon!, - 'url', - ); + final gmaps.Icon? icon = + controller.markers[const MarkerId('1')]?.marker?.icon as gmaps.Icon?; + expect(icon, isNotNull); + final String blobUrl = icon!.url!; 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 Marker'); }); + // https://github.com/flutter/flutter/issues/73789 + testWidgets('markers with custom bitmap icon pass size to sdk', + (WidgetTester tester) async { + final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + final Set markers = { + Marker( + markerId: const MarkerId('1'), + icon: BitmapDescriptor.fromBytes(bytes, size: const Size(20, 30)), + ), + }; + + controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final gmaps.Icon? icon = + controller.markers[const MarkerId('1')]?.marker?.icon as gmaps.Icon?; + expect(icon, isNotNull); + + final gmaps.Size size = icon!.size!; + final gmaps.Size scaledSize = icon.scaledSize!; + + expect(size.width, 20); + expect(size.height, 30); + expect(scaledSize.width, 20); + expect(scaledSize.height, 30); + }); + // https://github.com/flutter/flutter/issues/67854 testWidgets('InfoWindow snippet can have links', (WidgetTester tester) async { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 250bb5468fa7..2b09950cc00d 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 @@ -228,14 +228,25 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { // and the marker.infoWindow.anchor property. } -// Computes the options for a new [gmaps.Marker] from an incoming set of options -// [marker], and the existing marker registered with the map: [currentMarker]. -// Preserves the position from the [currentMarker], if set. -gmaps.MarkerOptions _markerOptionsFromMarker( - Marker marker, - gmaps.Marker? currentMarker, -) { - final List iconConfig = marker.icon.toJson() as List; +// Attempts to extract a [gmaps.Size] from `iconConfig[sizeIndex]`. +gmaps.Size? _gmSizeFromIconConfig(List iconConfig, int sizeIndex) { + gmaps.Size? size; + if (iconConfig.length >= sizeIndex + 1) { + final List? rawIconSize = iconConfig[sizeIndex] as List?; + if (rawIconSize != null) { + size = gmaps.Size( + rawIconSize[0] as num?, + rawIconSize[1] as num?, + ); + } + } + return size; +} + +// Converts a [BitmapDescriptor] into a [gmaps.Icon] that can be used in Markers. +gmaps.Icon? _gmIconFromBitmapDescriptor(BitmapDescriptor bitmapDescriptor) { + final List iconConfig = bitmapDescriptor.toJson() as List; + gmaps.Icon? icon; if (iconConfig != null) { @@ -243,17 +254,11 @@ gmaps.MarkerOptions _markerOptionsFromMarker( assert(iconConfig.length >= 2); // iconConfig[2] contains the DPIs of the screen, but that information is // already encoded in the iconConfig[1] - icon = gmaps.Icon() ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]! as String); - // iconConfig[3] may contain the [width, height] of the image, if passed! - if (iconConfig.length >= 4 && iconConfig[3] != null) { - final List rawIconSize = iconConfig[3]! as List; - final gmaps.Size size = gmaps.Size( - rawIconSize[0] as num?, - rawIconSize[1] as num?, - ); + final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 3); + if (size != null) { icon ..size = size ..scaledSize = size; @@ -264,8 +269,26 @@ gmaps.MarkerOptions _markerOptionsFromMarker( // Create a Blob from bytes, but let the browser figure out the encoding final Blob blob = Blob([bytes]); icon = gmaps.Icon()..url = Url.createObjectUrlFromBlob(blob); + + final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); + if (size != null) { + icon + ..size = size + ..scaledSize = size; + } } } + + 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]. +// Preserves the position from the [currentMarker], if set. +gmaps.MarkerOptions _markerOptionsFromMarker( + Marker marker, + gmaps.Marker? currentMarker, +) { return gmaps.MarkerOptions() ..position = currentMarker?.position ?? gmaps.LatLng( @@ -277,7 +300,7 @@ gmaps.MarkerOptions _markerOptionsFromMarker( ..visible = marker.visible ..opacity = marker.alpha ..draggable = marker.draggable - ..icon = icon; + ..icon = _gmIconFromBitmapDescriptor(marker.icon); // TODO(ditman): Compute anchor properly, otherwise infowindows attach to the wrong spot. // Flat and Rotation are not supported directly on the web. } 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 9670e0f28e2e..731b90547638 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/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.4.0+1 +version: 0.4.0+2 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,7 @@ dependencies: flutter_web_plugins: sdk: flutter google_maps: ^6.1.0 - google_maps_flutter_platform_interface: ^2.2.0 + google_maps_flutter_platform_interface: ^2.2.2 sanitize_html: ^2.0.0 stream_transform: ^2.0.0