mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 23:03:11 +08:00
[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:
@ -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
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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 >>></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();
|
||||
|
||||
|
@ -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.',
|
||||
);
|
||||
|
@ -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>[
|
||||
|
@ -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', () {
|
||||
|
@ -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!'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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';
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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?,
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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:
|
||||
|
Reference in New Issue
Block a user