[google_maps_flutter_web] Migrate to package:web (#5254)

This PR migrates `google_maps_flutter_web` from `dart:html` to `package:web`.

The `google_maps` package does provide a beta version with support for `package:web`, which is currently what this PR is depending on. Before landing this change, we should change the dependency to the stable release version of that package.

Fixes https://github.com/flutter/flutter/issues/137374

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
N/A
This commit is contained in:
Navaron Bracke
2024-02-28 01:13:06 +01:00
committed by GitHub
parent 5ff00b2d61
commit 7cdcf30f8f
19 changed files with 276 additions and 88 deletions

View File

@ -1,6 +1,7 @@
## NEXT
## 0.5.5
* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1.
* Migrates to `dart:js_interop` and `package:web` APIs.
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.
## 0.5.4+3

View File

@ -3,13 +3,14 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:html' as html;
import 'package:flutter/widgets.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';
// ignore: implementation_imports
import 'package:google_maps_flutter_web/src/utils.dart';
import 'package:integration_test/integration_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
@ -230,7 +231,7 @@ void main() {
polygons = MockPolygonsController();
polylines = MockPolylinesController();
tileOverlays = MockTileOverlaysController();
map = gmaps.GMap(html.DivElement());
map = gmaps.GMap(createDivElement());
});
testWidgets('listens to map events', (WidgetTester tester) async {
@ -471,7 +472,7 @@ void main() {
setUp(() {
map = gmaps.GMap(
html.DivElement(),
createDivElement(),
gmaps.MapOptions()
..zoom = 10
..center = gmaps.LatLng(0, 0),

View File

@ -3,11 +3,12 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:html' as html;
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';
// ignore: implementation_imports
import 'package:google_maps_flutter_web/src/utils.dart';
import 'package:integration_test/integration_test.dart';
/// Test Markers
@ -123,7 +124,7 @@ void main() {
testWidgets('showInfoWindow', (WidgetTester tester) async {
final gmaps.InfoWindow infoWindow = gmaps.InfoWindow();
final gmaps.GMap map = gmaps.GMap(html.DivElement());
final gmaps.GMap map = gmaps.GMap(createDivElement());
marker.set('map', map);
final MarkerController controller = MarkerController(
marker: marker,
@ -138,7 +139,7 @@ void main() {
testWidgets('hideInfoWindow', (WidgetTester tester) async {
final gmaps.InfoWindow infoWindow = gmaps.InfoWindow();
final gmaps.GMap map = gmaps.GMap(html.DivElement());
final gmaps.GMap map = gmaps.GMap(createDivElement());
marker.set('map', map);
final MarkerController controller = MarkerController(
marker: marker,
@ -156,7 +157,7 @@ void main() {
setUp(() {
final gmaps.InfoWindow infoWindow = gmaps.InfoWindow();
final gmaps.GMap map = gmaps.GMap(html.DivElement());
final gmaps.GMap map = gmaps.GMap(createDivElement());
marker.set('map', map);
controller = MarkerController(marker: marker, infoWindow: infoWindow);
});

View File

@ -4,7 +4,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:html' as html;
import 'dart:typed_data';
import 'dart:ui';
@ -12,8 +11,11 @@ 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';
// ignore: implementation_imports
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/web.dart';
import 'resources/icon_image_base64.dart';
@ -28,7 +30,7 @@ void main() {
setUp(() {
events = StreamController<MapEvent<Object?>>();
controller = MarkersController(stream: events);
map = gmaps.GMap(html.DivElement());
map = gmaps.GMap(createDivElement());
controller.bindToMap(123, map);
});
@ -274,11 +276,11 @@ void main() {
controller.addMarkers(markers);
expect(controller.markers.length, 1);
final html.HtmlElement? content = controller.markers[const MarkerId('1')]
?.infoWindow?.content as html.HtmlElement?;
expect(content?.innerHtml, contains('title for test'));
final HTMLElement? content = controller
.markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?;
expect(content?.innerHTML, contains('title for test'));
expect(
content?.innerHtml,
content?.innerHTML,
contains(
'<a href="https://www.google.com">Go to Google &gt;&gt;&gt;</a>',
));
@ -299,8 +301,8 @@ void main() {
controller.addMarkers(markers);
expect(controller.markers.length, 1);
final html.HtmlElement? content = controller.markers[const MarkerId('1')]
?.infoWindow?.content as html.HtmlElement?;
final HTMLElement? content = controller
.markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?;
content?.click();

View File

@ -3,13 +3,14 @@
// found in the LICENSE file.
import 'dart:convert';
import 'dart:html' as html;
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_platform_interface/google_maps_flutter_platform_interface.dart';
import 'package:google_maps_flutter_web/google_maps_flutter_web.dart';
import 'package:integration_test/integration_test.dart';
import 'package:web/web.dart';
import 'resources/tile16_base64.dart';
@ -43,8 +44,10 @@ void main() {
final gmaps.Size size = controller.gmMapType.tileSize!;
expect(size.width, TileOverlayController.logicalTileSize);
expect(size.height, TileOverlayController.logicalTileSize);
expect(controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document),
null);
expect(
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document),
null,
);
});
testWidgets('produces image tiles', (WidgetTester tester) async {
@ -55,16 +58,16 @@ void main() {
),
);
final html.ImageElement img =
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document)!
as html.ImageElement;
final HTMLImageElement img =
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document)!
as HTMLImageElement;
expect(img.naturalWidth, 0);
expect(img.naturalHeight, 0);
expect(img.hidden, true);
// Wait until the image is fully loaded and decoded before re-reading its attributes.
await img.onLoad.first;
await img.decode();
await img.decode().toDart;
expect(img.hidden, false);
expect(img.naturalWidth, 16);
@ -79,9 +82,9 @@ void main() {
),
);
{
final html.ImageElement img =
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document)!
as html.ImageElement;
final HTMLImageElement img =
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document)!
as HTMLImageElement;
await null; // let `getTile` `then` complete
expect(
img.src,
@ -95,10 +98,12 @@ void main() {
tileProvider: TestTileProvider(),
));
{
final html.ImageElement img =
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document)!
as html.ImageElement;
final HTMLImageElement img =
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document)!
as HTMLImageElement;
await img.onLoad.first;
expect(
img.src,
isNotEmpty,
@ -109,7 +114,7 @@ void main() {
controller.update(const TileOverlay(tileOverlayId: id));
{
expect(
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document),
controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document),
null,
reason: 'Setting a null tileProvider should work.',
);

View File

@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:html' as html;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
@ -11,9 +10,12 @@ import 'package:google_maps/google_maps.dart' as gmaps;
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'
hide GoogleMapController;
// ignore: implementation_imports
import 'package:google_maps_flutter_web/src/utils.dart';
import 'package:integration_test/integration_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:web/web.dart';
@GenerateNiceMocks(<MockSpec<dynamic>>[MockSpec<TileProvider>()])
import 'overlays_test.mocks.dart';
@ -38,13 +40,13 @@ void main() {
/// 0.
void probeTiles() {
for (final gmaps.MapType? mapType in map.overlayMapTypes!.array!) {
mapType?.getTile!(gmaps.Point(0, 0), 0, html.document);
mapType?.getTile!(gmaps.Point(0, 0), 0, document);
}
}
setUp(() {
controller = TileOverlaysController();
map = gmaps.GMap(html.DivElement());
map = gmaps.GMap(createDivElement());
controller.googleMap = map;
tileProviders = <MockTileProvider>[

View File

@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:html' as html;
import 'dart:ui';
import 'package:flutter_test/flutter_test.dart';
@ -11,6 +10,8 @@ import 'package:google_maps/google_maps.dart' as gmaps;
import 'package:google_maps/google_maps_geometry.dart' as geometry;
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
import 'package:google_maps_flutter_web/google_maps_flutter_web.dart';
// ignore: implementation_imports
import 'package:google_maps_flutter_web/src/utils.dart';
import 'package:integration_test/integration_test.dart';
// This value is used when comparing the results of
@ -25,7 +26,7 @@ void main() {
late gmaps.GMap map;
setUp(() {
map = gmaps.GMap(html.DivElement());
map = gmaps.GMap(createDivElement());
});
group('CirclesController', () {

View File

@ -20,6 +20,9 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return const Text('Testing... Look at the console output for results!');
return const Directionality(
textDirection: TextDirection.ltr,
child: Text('Testing... Look at the console output for results!'),
);
}
}

View File

@ -3,8 +3,8 @@ publish_to: none
# Tests require flutter beta or greater to run.
environment:
sdk: ^3.1.0
flutter: ">=3.13.0"
sdk: ^3.3.0
flutter: ">=3.19.0"
dependencies:
flutter:
@ -12,12 +12,13 @@ dependencies:
google_maps_flutter_platform_interface: ^2.4.0
google_maps_flutter_web:
path: ../
web: ^0.5.0
dev_dependencies:
build_runner: ^2.1.1
flutter_test:
sdk: flutter
google_maps: ^6.1.0
google_maps: ^7.1.0
google_maps_flutter: ^2.2.0 # Needed for projection_test.dart
http: ">=0.13.0 <2.0.0"
integration_test:

View File

@ -6,8 +6,7 @@ library google_maps_flutter_web;
import 'dart:async';
import 'dart:convert';
import 'dart:html' hide VoidCallback;
import 'dart:js_util';
import 'dart:js_interop';
import 'dart:ui_web' as ui_web;
import 'package:collection/collection.dart';
@ -20,10 +19,14 @@ import 'package:google_maps/google_maps.dart' as gmaps;
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';
import 'src/dom_window_extension.dart';
import 'src/google_maps_inspector_web.dart';
import 'src/map_styler.dart';
import 'src/third_party/to_screen_location/to_screen_location.dart';
import 'src/types.dart';
import 'src/utils.dart';
part 'src/circle.dart';
part 'src/circles.dart';

View File

@ -28,7 +28,7 @@ double _getCssOpacity(Color color) {
// mapToolbarEnabled is unused in web, there's no "map toolbar"
// myLocationButtonEnabled Widget not available in web yet, it needs to be built on top of the maps widget
// See: https://developers.google.com/maps/documentation/javascript/examples/control-custom
// myLocationEnabled needs to be built through dart:html navigator.geolocation
// myLocationEnabled needs to be built through `navigator.geolocation` from package:web.
// See: https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html
// trafficEnabled is handled when creating the GMap object, since it needs to be added as a layer.
// trackCameraPosition is just a boolean value that indicates if the map has an onCameraMove handler.
@ -139,10 +139,11 @@ List<gmaps.MapTypeStyle> _mapStyles(String? mapStyleJson) {
styles =
(json.decode(mapStyleJson, reviver: (Object? key, Object? value) {
if (value is Map && _isJsonMapStyle(value as Map<String, Object?>)) {
List<Object?> stylers = <Object?>[];
List<MapStyler> stylers = <MapStyler>[];
if (value['stylers'] != null) {
stylers = (value['stylers']! as List<Object?>)
.map<Object?>((Object? e) => e != null ? jsify(e) : null)
.whereType<Map<String, Object?>>()
.map(MapStyler.fromJson)
.toList();
}
return gmaps.MapTypeStyle()
@ -203,30 +204,44 @@ 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 = DivElement()
final HTMLElement container = createDivElement()
..id = 'gmaps-marker-${marker.markerId.value}-infowindow';
if (markerTitle.isNotEmpty) {
final HtmlElement title = HeadingElement.h3()
..className = 'infowindow-title'
..innerText = markerTitle;
container.children.add(title);
final HTMLHeadingElement title =
(document.createElement('h3') as HTMLHeadingElement)
..className = 'infowindow-title'
..innerText = markerTitle;
container.appendChild(title);
}
if (markerSnippet.isNotEmpty) {
final HtmlElement snippet = DivElement()
..className = 'infowindow-snippet'
final 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) {
final GoogleMapsTrustedTypePolicy trustedTypePolicy =
window.nullableTrustedTypes!.getGoogleMapsTrustedTypesPolicy(
GoogleMapsTrustedTypePolicyOptions(
createHTML: (String html, JSAny? arguments) {
return sanitizeHtml(html);
}.toJS,
),
);
snippet.trustedInnerHTML =
trustedTypePolicy.createHTML(markerSnippet, null);
} else {
// `sanitizeHtml` is used to clean the (potential) user input from (potential)
// XSS attacks through the contents of the marker InfoWindow.
// See: https://pub.dev/documentation/sanitize_html/latest/sanitize_html/sanitizeHtml.html
// See: b/159137885, b/159598165
// The NodeTreeSanitizer.trusted just tells setInnerHtml to leave the output
// of `sanitizeHtml` untouched.
// ignore: unsafe_html
..setInnerHtml(
sanitizeHtml(markerSnippet),
treeSanitizer: NodeTreeSanitizer.trusted,
);
container.children.add(snippet);
snippet.innerHTML = sanitizeHtml(markerSnippet);
}
container.appendChild(snippet);
}
return gmaps.InfoWindowOptions()
@ -274,8 +289,18 @@ gmaps.Icon? _gmIconFromBitmapDescriptor(BitmapDescriptor bitmapDescriptor) {
// Grab the bytes, and put them into a blob
final List<int> bytes = iconConfig[1]! as List<int>;
// Create a Blob from bytes, but let the browser figure out the encoding
final Blob blob = Blob(<dynamic>[bytes]);
icon = gmaps.Icon()..url = Url.createObjectUrlFromBlob(blob);
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(<JSUint8Array>[(bytes as Uint8List).toJS].toJS);
icon = gmaps.Icon()..url = URL.createObjectURL(blob as JSObject);
final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2);
if (size != null) {

View File

@ -0,0 +1,89 @@
// 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:js_interop';
import 'package:web/web.dart' as web;
/// This extension gives [web.Window] a nullable getter to the `trustedTypes`
/// property, which is used to check for feature support.
extension NullableTrustedTypesGetter on web.Window {
/// (Nullable) Bindings to window.trustedTypes.
///
/// This may be null if the browser doesn't support the Trusted Types API.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API
@JS('trustedTypes')
external GoogleMapsTrustedTypePolicyFactory? get nullableTrustedTypes;
}
// TODO(ditman): remove this extension type when we depend on package:web 0.5.1
/// This extension exists as a stop gap until `package:web 0.5.1` is released.
/// That version provides the `TrustedTypes` API.
@JS('TrustedTypePolicyFactory')
extension type GoogleMapsTrustedTypePolicyFactory._(JSObject _)
implements JSObject {
/// The `TrustedTypePolicy` for Google Maps Flutter.
static GoogleMapsTrustedTypePolicy? _policy;
@JS('createPolicy')
external GoogleMapsTrustedTypePolicy _createPolicy(
String policyName, [
GoogleMapsTrustedTypePolicyOptions policyOptions,
]);
/// Get a new [GoogleMapsTrustedTypePolicy].
///
/// If a policy already exists, it will be returned.
/// Otherwise, a new policy is created.
///
/// Because of we only cache one _policy, this method
/// specifically hardcoded to the GoogleMaps use case.
GoogleMapsTrustedTypePolicy getGoogleMapsTrustedTypesPolicy(
GoogleMapsTrustedTypePolicyOptions policyOptions,
) {
const String policyName = 'google_maps_flutter_sanitize';
_policy ??= _createPolicy(policyName, policyOptions);
return _policy!;
}
}
// TODO(ditman): remove this extension type when we depend on package:web 0.5.1
/// This extension exists as a stop gap until `package:web 0.5.1` is released.
/// That version provides the `TrustedTypes` API.
@JS('TrustedTypePolicy')
extension type GoogleMapsTrustedTypePolicy._(JSObject _) implements JSObject {
/// Create a new `TrustedHTML` instance with the given [input] and [arguments].
external GoogleMapsTrustedHTML createHTML(
String input,
JSAny? arguments,
);
}
// TODO(ditman): remove this extension type when we depend on package:web 0.5.1
/// This extension exists as a stop gap until `package:web 0.5.1` is released.
/// That version provides the `TrustedTypes` API.
@JS('TrustedTypePolicyOptions')
extension type GoogleMapsTrustedTypePolicyOptions._(JSObject _)
implements JSObject {
/// Create a new `TrustedTypePolicyOptions` instance.
external factory GoogleMapsTrustedTypePolicyOptions({
JSFunction createHTML,
});
}
// TODO(ditman): remove this extension type when we depend on package:web 0.5.1
/// This extension exists as a stop gap until `package:web 0.5.1` is released.
/// That version provides the `TrustedTypes` API.
@JS('TrustedHTML')
extension type GoogleMapsTrustedHTML._(JSObject _) implements JSObject {}
/// This extension provides a setter for the [web.HTMLElement] `innerHTML` property,
/// that accepts trusted HTML only.
extension TrustedInnerHTML on web.HTMLElement {
/// Set the inner HTML of this element to the given [trustedHTML].
@JS('innerHTML')
external set trustedInnerHTML(GoogleMapsTrustedHTML trustedHTML);
}

View File

@ -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.GMap Function(
HtmlElement div, gmaps.MapOptions options);
HTMLElement div, gmaps.MapOptions options);
/// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered.
class GoogleMapController {
@ -36,7 +36,7 @@ class GoogleMapController {
// Register the view factory that will hold the `_div` that holds the map in the DOM.
// The `_div` needs to be created outside of the ViewFactory (and cached!) so we can
// use it to create the [gmaps.GMap] in the `init()` method of this class.
_div = DivElement()
_div = createDivElement()
..id = _getViewType(mapId)
..style.width = '100%'
..style.height = '100%';
@ -74,7 +74,7 @@ class GoogleMapController {
// The Flutter widget that contains the rendered Map.
HtmlElementView? _widget;
late HtmlElement _div;
late HTMLElement _div;
/// The Flutter widget that will contain the rendered Map. Used for caching.
Widget? get widget {
@ -138,7 +138,7 @@ class GoogleMapController {
DebugCreateMapFunction? _overrideCreateMap;
gmaps.GMap _createMap(HtmlElement div, gmaps.MapOptions options) {
gmaps.GMap _createMap(HTMLElement div, gmaps.MapOptions options) {
if (_overrideCreateMap != null) {
return _overrideCreateMap!(div, options);
}

View File

@ -0,0 +1,40 @@
// 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:js_interop';
/// The interop type for a Google Maps Map Styler.
///
/// See: https://developers.google.com/maps/documentation/javascript/style-reference#stylers
@JS()
@staticInterop
@anonymous
extension type MapStyler._(JSObject _) implements JSObject {
/// Create a new [MapStyler] instance.
external factory MapStyler({
String? hue,
num? lightness,
num? saturation,
num? gamma,
// ignore: non_constant_identifier_names
bool? invert_lightness,
String? visibility,
String? color,
int? weight,
});
/// Create a new [MapStyler] instance from the given [json].
factory MapStyler.fromJson(Map<String, Object?> json) {
return MapStyler(
hue: json['hue'] as String?,
lightness: json['lightness'] as num?,
saturation: json['saturation'] as num?,
gamma: json['gamma'] as num?,
invert_lightness: json['invert_lightness'] as bool?,
visibility: json['visibility'] as String?,
color: json['color'] as String?,
weight: json['weight'] as int?,
);
}
}

View File

@ -69,12 +69,12 @@ class MarkerController {
/// This cannot be called after [remove].
void update(
gmaps.MarkerOptions options, {
HtmlElement? newInfoWindowContent,
HTMLElement? newInfoWindowContent,
}) {
assert(_marker != null, 'Cannot `update` Marker after calling `remove`.');
_marker!.options = options;
if (_infoWindow != null && newInfoWindowContent != null) {
_infoWindow!.content = newInfoWindowContent;
_infoWindow.content = newInfoWindowContent;
}
}
@ -94,7 +94,7 @@ class MarkerController {
void hideInfoWindow() {
assert(_marker != null, 'Cannot `hideInfoWindow` on a `remove`d Marker.');
if (_infoWindow != null) {
_infoWindow!.close();
_infoWindow.close();
_infoWindowShown = false;
}
}
@ -105,7 +105,7 @@ class MarkerController {
void showInfoWindow() {
assert(_marker != null, 'Cannot `showInfoWindow` on a `remove`d Marker.');
if (_infoWindow != null) {
_infoWindow!.open(_marker!.map, _marker);
_infoWindow.open(_marker!.map, _marker);
_infoWindowShown = true;
}
}

View File

@ -39,11 +39,12 @@ 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;
content.onClick.listen((_) {
infoWindowOptions.content is HTMLElement) {
final HTMLElement content = infoWindowOptions.content! as HTMLElement;
content.onclick = (JSAny? _) {
_onInfoWindowTap(marker.markerId);
});
}.toJS;
}
}
@ -91,7 +92,7 @@ class MarkersController extends GeometryController {
_infoWindowOptionsFromMarker(marker);
markerController.update(
markerOptions,
newInfoWindowContent: infoWindow?.content as HtmlElement?,
newInfoWindowContent: infoWindow?.content as HTMLElement?,
);
}
}

View File

@ -33,7 +33,7 @@ class TileOverlayController {
}
/// Renders a Tile for gmaps; delegating to the configured [TileProvider].
HtmlElement? _getTile(
HTMLElement? _getTile(
gmaps.Point? tileCoord,
num? zoom,
Document? ownerDocument,
@ -42,10 +42,10 @@ class TileOverlayController {
return null;
}
final ImageElement img =
ownerDocument!.createElement('img') as ImageElement;
final HTMLImageElement img =
ownerDocument!.createElement('img') as HTMLImageElement;
img.width = img.height = logicalTileSize;
img.hidden = true;
img.hidden = true.toJS;
img.setAttribute('decoding', 'async');
_tileOverlay.tileProvider!
@ -55,12 +55,14 @@ class TileOverlayController {
return;
}
// Using img lets us take advantage of native decoding.
final String src = Url.createObjectUrl(Blob(<Object?>[tile.data]));
final String src = URL.createObjectURL(
Blob(<JSUint8Array>[tile.data!.toJS].toJS) as JSObject,
);
img.src = src;
img.addEventListener('load', (_) {
img.hidden = false;
Url.revokeObjectUrl(src);
});
img.onload = (JSAny? _) {
img.hidden = false.toJS;
URL.revokeObjectURL(src);
}.toJS;
});
return img;

View File

@ -0,0 +1,10 @@
// 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 'package:web/web.dart';
/// Convenience method to create a new [HTMLDivElement] element.
HTMLDivElement createDivElement() {
return document.createElement('div') as HTMLDivElement;
}

View File

@ -2,11 +2,11 @@ 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.4+3
version: 0.5.5
environment:
sdk: ">=3.1.0 <4.0.0"
flutter: ">=3.13.0"
sdk: ^3.3.0
flutter: ">=3.19.0"
flutter:
plugin:
@ -22,10 +22,11 @@ dependencies:
sdk: flutter
flutter_web_plugins:
sdk: flutter
google_maps: ^6.1.0
google_maps: ^7.1.0
google_maps_flutter_platform_interface: ^2.4.0
sanitize_html: ^2.0.0
stream_transform: ^2.0.0
web: ^0.5.0
dev_dependencies:
flutter_test: