Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
94f593b
Add Advanced markers support (web)
aednlaxer Aug 8, 2025
bf9ab89
Set minimum version of `web` package to 1.0.0
aednlaxer Aug 12, 2025
c2f5cb0
Advanced markers package pr fixes
illuminati1911 Sep 26, 2025
ee76359
Unify web package double import
illuminati1911 Sep 26, 2025
6c8a158
Raise google maps flutter web package to version 0.6.0
illuminati1911 Sep 26, 2025
cf9c3a5
Fix get marker id function in google maps flutter web package
illuminati1911 Sep 26, 2025
45b32d3
Updated license headers in the google maps flutter web package
illuminati1911 Sep 26, 2025
97b24fd
Minor refactoring to advanced markers for web
illuminati1911 Oct 17, 2025
4bae6f9
Handle listeners for web markers
illuminati1911 Oct 17, 2025
770f281
Code formatting
illuminati1911 Oct 17, 2025
f30050c
Web package html element refactoring
illuminati1911 Nov 7, 2025
cf1bf48
Web integration test fixes
illuminati1911 Nov 11, 2025
ff2cf6c
General fixes and refactoring
illuminati1911 Nov 18, 2025
f255a1f
web advanced marker documentation changes
illuminati1911 Nov 24, 2025
98d8d50
formatting changes
illuminati1911 Nov 24, 2025
33f1f95
Merge branch 'main' into feature/advanced_markers_google_maps_flutter…
illuminati1911 Nov 24, 2025
4d9d914
readme fix
illuminati1911 Nov 24, 2025
5aa1549
further readme fixes
illuminati1911 Nov 24, 2025
4162635
Merge branch 'main' into feature/advanced_markers_google_maps_flutter…
illuminati1911 Nov 25, 2025
e3496b8
integration test fix
illuminati1911 Nov 25, 2025
521ae73
flutter analyze fixes
illuminati1911 Nov 25, 2025
740eba4
flutter analyze and format fixes for web package
illuminati1911 Nov 26, 2025
732d595
integration test fix
illuminati1911 Nov 26, 2025
69f740d
update integration test mocks
illuminati1911 Nov 26, 2025
276d88b
fix: MarkersController mock
jokerttu Nov 26, 2025
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,5 +1,11 @@
## NEXT

## 0.6.0

* **BREAKING CHANGES**: Adds type constraints to generic type parameters:
* `ClusterManagersController<T>` now requires `T extends Object`
* `MarkersController<T, O>` 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.
* Updates minimum supported SDK version to Flutter 3.35/Dart 3.9.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please list the breaking changes (following repo style), and provide more details about the new APIs, giving someone reading this enough pointers that they could figure out what APIs to go look at to adopt the new markers.

## 0.5.14+3
Expand Down
18 changes: 16 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,7 +32,7 @@ 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
Expand All @@ -44,12 +44,26 @@ 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>
```

Now you should be able to use the Google Maps plugin normally.

## Advanced 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.

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>
```

For full details, see Google's official documentation:
https://developers.google.com/maps/documentation/javascript/advanced-markers/overview

## Marker clustering

If you need marker clustering support, modify the <head> tag to load the [js-markerclusterer](https://github.com/googlemaps/js-markerclusterer#install) library. Ensure you are using the currently supported version `2.5.3`, like so:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// 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.

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 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);

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 controller = AdvancedMarkerController(marker: marker);

controller.showInfoWindow();

expect(controller.infoWindowShown, isFalse);
});

testWidgets('showInfoWindow', (WidgetTester tester) async {
final infoWindow = gmaps.InfoWindow();
final map = gmaps.Map(createDivElement());
marker.map = map;
final controller = AdvancedMarkerController(
marker: marker,
infoWindow: infoWindow,
);

controller.showInfoWindow();

expect(infoWindow.get('map'), map);
expect(controller.infoWindowShown, isTrue);
});

testWidgets('hideInfoWindow', (WidgetTester tester) async {
final infoWindow = gmaps.InfoWindow();
final map = gmaps.Map(createDivElement());
marker.map = map;
final controller = AdvancedMarkerController(
marker: marker,
infoWindow: infoWindow,
);

controller.hideInfoWindow();

expect(infoWindow.get('map'), isNull);
expect(controller.infoWindowShown, isFalse);
});

group('remove', () {
late AdvancedMarkerController controller;

setUp(() {
final infoWindow = gmaps.InfoWindow();
final 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 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();
}, throwsStateError);
});

testWidgets('cannot call hideInfoWindow after remove', (
WidgetTester tester,
) async {
controller.remove();

expect(() {
controller.hideInfoWindow();
}, throwsAssertionError);
});
});
});
}
Loading