Skip to content

[google_maps_flutter_web] Add Advanced markers support (web) #9773

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.13

* Adds Advanced markers support.

## 0.5.12+2

* Fix broken cameraTargetBounds option on web.
Expand Down
11 changes: 9 additions & 2 deletions packages/google_maps_flutter/google_maps_flutter_web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,26 @@ Modify the `<head>` 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
<script
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=drawing">
</script>
```

If your app uses Advanced Markers, include `marker` library like this:
```html
<script
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=marker">
</script>
```

To request multiple libraries, separate them with commas:

```html
<script
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=drawing,visualization,places">
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=drawing,marker,visualization,places">
</script>
```

Expand Down
Original file line number Diff line number Diff line change
@@ -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<bool> 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<bool> 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<bool>();
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);
});
});
});
}
Loading