Skip to content

Commit

Permalink
[google_maps_flutter_web] Web changes to support heatmaps (#7315)
Browse files Browse the repository at this point in the history
Sequel to:
- #7312

Prequel to:
- #3257
  • Loading branch information
Rexios80 authored Aug 6, 2024
1 parent c6a30bc commit 57fcda0
Show file tree
Hide file tree
Showing 17 changed files with 585 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.10

* Adds support for heatmap layers.

## 0.5.9+2

* Restores support for Dart `^3.3.0` and Flutter `^3.19.0`.
Expand Down
13 changes: 13 additions & 0 deletions packages/google_maps_flutter/google_maps_flutter_web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ If you need marker clustering support, modify the <head> tag to load the [js-mar
</head>
```

## Heatmaps
To use heatmaps, add `&libraries=visualization` to the end of the URL. See [the documentation](https://developers.google.com/maps/documentation/javascript/libraries) for more information.

## Limitations of the web version

The following map options are not available in web, because the map doesn't rotate there:
Expand All @@ -85,3 +88,13 @@ Indoor and building layers are still not available on the web. Traffic is.
Only Android supports "[Lite Mode](https://developers.google.com/maps/documentation/android-sdk/lite)", so the `liteModeEnabled` constructor argument can't be set to `true` on web apps.

Google Maps for web uses `HtmlElementView` to render maps. When a `GoogleMap` is stacked below other widgets, [`package:pointer_interceptor`](https://www.pub.dev/packages/pointer_interceptor) must be used to capture mouse events on the Flutter overlays. See issue [#73830](https://github.com/flutter/flutter/issues/73830).

## Supported Heatmap Options

| Field | Supported |
| ---------------------------- | :-------: |
| Heatmap.dissipating ||
| Heatmap.maxIntensity ||
| Heatmap.minimumZoomIntensity | x |
| Heatmap.maximumZoomIntensity | x |
| HeatmapGradient.colorMapSize | x |
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ gmaps.Map mapShim() => throw UnimplementedError();
MockSpec<CirclesController>(
fallbackGenerators: <Symbol, Function>{#googleMap: mapShim},
),
MockSpec<HeatmapsController>(
fallbackGenerators: <Symbol, Function>{#googleMap: mapShim},
),
MockSpec<PolygonsController>(
fallbackGenerators: <Symbol, Function>{#googleMap: mapShim},
),
Expand Down Expand Up @@ -161,6 +164,20 @@ void main() {
}, throwsAssertionError);
});

testWidgets('cannot updateHeatmaps after dispose',
(WidgetTester tester) async {
controller.dispose();

expect(() {
controller.updateHeatmaps(
HeatmapUpdates.from(
const <Heatmap>{},
const <Heatmap>{},
),
);
}, throwsAssertionError);
});

testWidgets('cannot updatePolygons after dispose',
(WidgetTester tester) async {
controller.dispose();
Expand Down Expand Up @@ -229,6 +246,7 @@ void main() {

group('init', () {
late MockCirclesController circles;
late MockHeatmapsController heatmaps;
late MockMarkersController markers;
late MockPolygonsController polygons;
late MockPolylinesController polylines;
Expand All @@ -237,6 +255,7 @@ void main() {

setUp(() {
circles = MockCirclesController();
heatmaps = MockHeatmapsController();
markers = MockMarkersController();
polygons = MockPolygonsController();
polylines = MockPolylinesController();
Expand All @@ -249,6 +268,7 @@ void main() {
..debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
heatmaps: heatmaps,
markers: markers,
polygons: polygons,
polylines: polylines,
Expand Down Expand Up @@ -287,6 +307,7 @@ void main() {
..debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
heatmaps: heatmaps,
markers: markers,
polygons: polygons,
polylines: polylines,
Expand All @@ -295,6 +316,7 @@ void main() {
..init();

verify(circles.bindToMap(mapId, map));
verify(heatmaps.bindToMap(mapId, map));
verify(markers.bindToMap(mapId, map));
verify(polygons.bindToMap(mapId, map));
verify(polylines.bindToMap(mapId, map));
Expand All @@ -307,6 +329,17 @@ void main() {
circleId: CircleId('circle-1'),
zIndex: 1234,
),
}, heatmaps: <Heatmap>{
const Heatmap(
heatmapId: HeatmapId('heatmap-1'),
data: <WeightedLatLng>[
WeightedLatLng(LatLng(43.355114, -5.851333)),
WeightedLatLng(LatLng(43.354797, -5.851860)),
WeightedLatLng(LatLng(43.354469, -5.851318)),
WeightedLatLng(LatLng(43.354762, -5.850824)),
],
radius: HeatmapRadius.fromPixels(20),
),
}, markers: <Marker>{
const Marker(
markerId: MarkerId('marker-1'),
Expand Down Expand Up @@ -352,6 +385,7 @@ void main() {
controller = createController(mapObjects: mapObjects)
..debugSetOverrides(
circles: circles,
heatmaps: heatmaps,
markers: markers,
polygons: polygons,
polylines: polylines,
Expand All @@ -360,6 +394,7 @@ void main() {
..init();

verify(circles.addCircles(mapObjects.circles));
verify(heatmaps.addHeatmaps(mapObjects.heatmaps));
verify(markers.addMarkers(mapObjects.markers));
verify(polygons.addPolygons(mapObjects.polygons));
verify(polylines.addPolylines(mapObjects.polylines));
Expand Down Expand Up @@ -670,6 +705,76 @@ void main() {
}));
});

testWidgets('updateHeatmaps', (WidgetTester tester) async {
final MockHeatmapsController mock = MockHeatmapsController();
controller.debugSetOverrides(heatmaps: mock);

const List<WeightedLatLng> heatmapPoints = <WeightedLatLng>[
WeightedLatLng(LatLng(37.782, -122.447)),
WeightedLatLng(LatLng(37.782, -122.445)),
WeightedLatLng(LatLng(37.782, -122.443)),
WeightedLatLng(LatLng(37.782, -122.441)),
WeightedLatLng(LatLng(37.782, -122.439)),
WeightedLatLng(LatLng(37.782, -122.437)),
WeightedLatLng(LatLng(37.782, -122.435)),
WeightedLatLng(LatLng(37.785, -122.447)),
WeightedLatLng(LatLng(37.785, -122.445)),
WeightedLatLng(LatLng(37.785, -122.443)),
WeightedLatLng(LatLng(37.785, -122.441)),
WeightedLatLng(LatLng(37.785, -122.439)),
WeightedLatLng(LatLng(37.785, -122.437)),
WeightedLatLng(LatLng(37.785, -122.435))
];

final Set<Heatmap> previous = <Heatmap>{
const Heatmap(
heatmapId: HeatmapId('to-be-updated'),
data: heatmapPoints,
radius: HeatmapRadius.fromPixels(20),
),
const Heatmap(
heatmapId: HeatmapId('to-be-removed'),
data: heatmapPoints,
radius: HeatmapRadius.fromPixels(20),
),
};

final Set<Heatmap> current = <Heatmap>{
const Heatmap(
heatmapId: HeatmapId('to-be-updated'),
data: heatmapPoints,
dissipating: false,
radius: HeatmapRadius.fromPixels(20),
),
const Heatmap(
heatmapId: HeatmapId('to-be-added'),
data: heatmapPoints,
radius: HeatmapRadius.fromPixels(20),
),
};

controller.updateHeatmaps(HeatmapUpdates.from(previous, current));

verify(mock.removeHeatmaps(<HeatmapId>{
const HeatmapId('to-be-removed'),
}));
verify(mock.addHeatmaps(<Heatmap>{
const Heatmap(
heatmapId: HeatmapId('to-be-added'),
data: heatmapPoints,
radius: HeatmapRadius.fromPixels(20),
),
}));
verify(mock.changeHeatmaps(<Heatmap>{
const Heatmap(
heatmapId: HeatmapId('to-be-updated'),
data: heatmapPoints,
dissipating: false,
radius: HeatmapRadius.fromPixels(20),
),
}));
});

testWidgets('updateMarkers', (WidgetTester tester) async {
final MockMarkersController mock = MockMarkersController();
controller = createController()..debugSetOverrides(markers: mock);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,95 @@ class MockCirclesController extends _i1.Mock implements _i2.CirclesController {
);
}

/// A class which mocks [HeatmapsController].
///
/// See the documentation for Mockito's code generation for more information.
class MockHeatmapsController extends _i1.Mock
implements _i2.HeatmapsController {
@override
Map<_i3.HeatmapId, _i2.HeatmapController> get heatmaps => (super.noSuchMethod(
Invocation.getter(#heatmaps),
returnValue: <_i3.HeatmapId, _i2.HeatmapController>{},
returnValueForMissingStub: <_i3.HeatmapId, _i2.HeatmapController>{},
) as Map<_i3.HeatmapId, _i2.HeatmapController>);

@override
_i4.Map get googleMap => (super.noSuchMethod(
Invocation.getter(#googleMap),
returnValue: _i5.mapShim(),
returnValueForMissingStub: _i5.mapShim(),
) as _i4.Map);

@override
set googleMap(_i4.Map? _googleMap) => super.noSuchMethod(
Invocation.setter(
#googleMap,
_googleMap,
),
returnValueForMissingStub: null,
);

@override
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,
),
returnValueForMissingStub: null,
);

@override
void addHeatmaps(Set<_i3.Heatmap>? heatmapsToAdd) => super.noSuchMethod(
Invocation.method(
#addHeatmaps,
[heatmapsToAdd],
),
returnValueForMissingStub: null,
);

@override
void changeHeatmaps(Set<_i3.Heatmap>? heatmapsToChange) => super.noSuchMethod(
Invocation.method(
#changeHeatmaps,
[heatmapsToChange],
),
returnValueForMissingStub: null,
);

@override
void removeHeatmaps(Set<_i3.HeatmapId>? heatmapIdsToRemove) =>
super.noSuchMethod(
Invocation.method(
#removeHeatmaps,
[heatmapIdsToRemove],
),
returnValueForMissingStub: null,
);

@override
void bindToMap(
int? mapId,
_i4.Map? googleMap,
) =>
super.noSuchMethod(
Invocation.method(
#bindToMap,
[
mapId,
googleMap,
],
),
returnValueForMissingStub: null,
);
}

/// A class which mocks [PolygonsController].
///
/// See the documentation for Mockito's code generation for more information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@ void main() {

verify(controller.updateCircles(expectedUpdates));
});
testWidgets('updateHeatmaps', (WidgetTester tester) async {
final HeatmapUpdates expectedUpdates = HeatmapUpdates.from(
const <Heatmap>{},
const <Heatmap>{},
);

await plugin.updateHeatmaps(expectedUpdates, mapId: mapId);

verify(controller.updateHeatmaps(expectedUpdates));
});
// Tile Overlays
testWidgets('updateTileOverlays', (WidgetTester tester) async {
final Set<TileOverlay> expectedOverlays = <TileOverlay>{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class MockGoogleMapController extends _i1.Mock
_i4.DebugSetOptionsFunction? setOptions,
_i4.MarkersController? markers,
_i4.CirclesController? circles,
_i4.HeatmapsController? heatmaps,
_i4.PolygonsController? polygons,
_i4.PolylinesController? polylines,
_i6.ClusterManagersController? clusterManagers,
Expand All @@ -151,6 +152,7 @@ class MockGoogleMapController extends _i1.Mock
#setOptions: setOptions,
#markers: markers,
#circles: circles,
#heatmaps: heatmaps,
#polygons: polygons,
#polylines: polylines,
#clusterManagers: clusterManagers,
Expand Down Expand Up @@ -289,6 +291,15 @@ class MockGoogleMapController extends _i1.Mock
returnValueForMissingStub: null,
);

@override
void updateHeatmaps(_i2.HeatmapUpdates? updates) => super.noSuchMethod(
Invocation.method(
#updateHeatmaps,
[updates],
),
returnValueForMissingStub: null,
);

@override
void updatePolygons(_i2.PolygonUpdates? updates) => super.noSuchMethod(
Invocation.method(
Expand Down
Loading

0 comments on commit 57fcda0

Please sign in to comment.