Skip to content

Commit 9e21922

Browse files
authored
[google_maps_flutter_web] Initial support for custom overlays (flutter#3538)
This is a resubmission of flutter/plugins#6982 from the now archived flutter plugins repo. I'm submitting the changes from the original author, @AsturaPhoenix. The original description is below. -------- Saves tile bytes to blobs and uses img elements to decode and render. Does not implement opacity, perform caching, or serve placeholder images. **Issue:** Fixes flutter#98596 **Known issues:** - flutter#116132 - AsturaPhoenix/trip_planner_aquamarine#22
1 parent 9074ea9 commit 9e21922

15 files changed

+801
-151
lines changed

packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.5.3
2+
3+
* Initial support for custom overlays. [#98596](https://github.com/flutter/flutter/issues/98596).
4+
15
## 0.5.2
26

37
* Adds options for gesture handling and tilt controls.

packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart

Lines changed: 102 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import 'package:integration_test/integration_test.dart';
1414
import 'package:mockito/annotations.dart';
1515
import 'package:mockito/mockito.dart';
1616

17-
import 'google_maps_controller_test.mocks.dart';
18-
19-
@GenerateMocks(<Type>[], customMocks: <MockSpec<dynamic>>[
20-
MockSpec<CirclesController>(onMissingStub: OnMissingStub.returnDefault),
21-
MockSpec<PolygonsController>(onMissingStub: OnMissingStub.returnDefault),
22-
MockSpec<PolylinesController>(onMissingStub: OnMissingStub.returnDefault),
23-
MockSpec<MarkersController>(onMissingStub: OnMissingStub.returnDefault),
17+
@GenerateNiceMocks(<MockSpec<dynamic>>[
18+
MockSpec<CirclesController>(),
19+
MockSpec<PolygonsController>(),
20+
MockSpec<PolylinesController>(),
21+
MockSpec<MarkersController>(),
22+
MockSpec<TileOverlaysController>(),
2423
])
24+
import 'google_maps_controller_test.mocks.dart';
2525

2626
/// Test Google Map Controller
2727
void main() {
@@ -194,6 +194,15 @@ void main() {
194194
}, throwsAssertionError);
195195
});
196196

197+
testWidgets('cannot updateTileOverlays after dispose',
198+
(WidgetTester tester) async {
199+
controller.dispose();
200+
201+
expect(() {
202+
controller.updateTileOverlays(const <TileOverlay>{});
203+
}, throwsAssertionError);
204+
});
205+
197206
testWidgets('isInfoWindowShown defaults to false',
198207
(WidgetTester tester) async {
199208
controller.dispose();
@@ -208,27 +217,28 @@ void main() {
208217
late MockMarkersController markers;
209218
late MockPolygonsController polygons;
210219
late MockPolylinesController polylines;
220+
late MockTileOverlaysController tileOverlays;
211221
late gmaps.GMap map;
212222

213223
setUp(() {
214224
circles = MockCirclesController();
215225
markers = MockMarkersController();
216226
polygons = MockPolygonsController();
217227
polylines = MockPolylinesController();
228+
tileOverlays = MockTileOverlaysController();
218229
map = gmaps.GMap(html.DivElement());
219230
});
220231

221232
testWidgets('listens to map events', (WidgetTester tester) async {
222-
controller = createController();
223-
controller.debugSetOverrides(
224-
createMap: (_, __) => map,
225-
circles: circles,
226-
markers: markers,
227-
polygons: polygons,
228-
polylines: polylines,
229-
);
230-
231-
controller.init();
233+
controller = createController()
234+
..debugSetOverrides(
235+
createMap: (_, __) => map,
236+
circles: circles,
237+
markers: markers,
238+
polygons: polygons,
239+
polylines: polylines,
240+
)
241+
..init();
232242

233243
// Trigger events on the map, and verify they've been broadcast to the stream
234244
final Stream<MapEvent<Object?>> capturedEvents = stream.stream.take(5);
@@ -258,26 +268,26 @@ void main() {
258268

259269
testWidgets("binds geometry controllers to map's",
260270
(WidgetTester tester) async {
261-
controller = createController();
262-
controller.debugSetOverrides(
263-
createMap: (_, __) => map,
264-
circles: circles,
265-
markers: markers,
266-
polygons: polygons,
267-
polylines: polylines,
268-
);
269-
270-
controller.init();
271+
controller = createController()
272+
..debugSetOverrides(
273+
createMap: (_, __) => map,
274+
circles: circles,
275+
markers: markers,
276+
polygons: polygons,
277+
polylines: polylines,
278+
tileOverlays: tileOverlays,
279+
)
280+
..init();
271281

272282
verify(circles.bindToMap(mapId, map));
273283
verify(markers.bindToMap(mapId, map));
274284
verify(polygons.bindToMap(mapId, map));
275285
verify(polylines.bindToMap(mapId, map));
286+
verify(tileOverlays.bindToMap(mapId, map));
276287
});
277288

278289
testWidgets('renders initial geometry', (WidgetTester tester) async {
279-
controller = createController(
280-
mapObjects: MapObjects(circles: <Circle>{
290+
final MapObjects mapObjects = MapObjects(circles: <Circle>{
281291
const Circle(
282292
circleId: CircleId('circle-1'),
283293
zIndex: 1234,
@@ -320,57 +330,25 @@ void main() {
320330
LatLng(43.354469, -5.851318),
321331
LatLng(43.354762, -5.850824),
322332
])
323-
}));
324-
325-
controller.debugSetOverrides(
326-
circles: circles,
327-
markers: markers,
328-
polygons: polygons,
329-
polylines: polylines,
330-
);
331-
332-
controller.init();
333-
334-
final Set<Circle> capturedCircles =
335-
verify(circles.addCircles(captureAny)).captured[0] as Set<Circle>;
336-
final Set<Marker> capturedMarkers =
337-
verify(markers.addMarkers(captureAny)).captured[0] as Set<Marker>;
338-
final Set<Polygon> capturedPolygons =
339-
verify(polygons.addPolygons(captureAny)).captured[0]
340-
as Set<Polygon>;
341-
final Set<Polyline> capturedPolylines =
342-
verify(polylines.addPolylines(captureAny)).captured[0]
343-
as Set<Polyline>;
344-
345-
expect(capturedCircles.first.circleId.value, 'circle-1');
346-
expect(capturedCircles.first.zIndex, 1234);
347-
expect(capturedMarkers.first.markerId.value, 'marker-1');
348-
expect(capturedMarkers.first.infoWindow.snippet, 'snippet for test');
349-
expect(capturedMarkers.first.infoWindow.title, 'title for test');
350-
expect(capturedPolygons.first.polygonId.value, 'polygon-1');
351-
expect(capturedPolygons.elementAt(1).polygonId.value,
352-
'polygon-2-with-holes');
353-
expect(capturedPolygons.elementAt(1).holes, isNot(null));
354-
expect(capturedPolylines.first.polylineId.value, 'polyline-1');
355-
});
356-
357-
testWidgets('empty infoWindow does not create InfoWindow instance.',
358-
(WidgetTester tester) async {
359-
controller = createController(
360-
mapObjects: MapObjects(markers: <Marker>{
361-
const Marker(markerId: MarkerId('marker-1')),
362-
}));
363-
364-
controller.debugSetOverrides(
365-
markers: markers,
366-
);
367-
368-
controller.init();
369-
370-
final Set<Marker> capturedMarkers =
371-
verify(markers.addMarkers(captureAny)).captured[0] as Set<Marker>;
333+
}, tileOverlays: <TileOverlay>{
334+
const TileOverlay(tileOverlayId: TileOverlayId('overlay-1'))
335+
});
372336

373-
expect(capturedMarkers.first.infoWindow, InfoWindow.noText);
337+
controller = createController(mapObjects: mapObjects)
338+
..debugSetOverrides(
339+
circles: circles,
340+
markers: markers,
341+
polygons: polygons,
342+
polylines: polylines,
343+
tileOverlays: tileOverlays,
344+
)
345+
..init();
346+
347+
verify(circles.addCircles(mapObjects.circles));
348+
verify(markers.addMarkers(mapObjects.markers));
349+
verify(polygons.addPolygons(mapObjects.polygons));
350+
verify(polylines.addPolylines(mapObjects.polylines));
351+
verify(tileOverlays.addTileOverlays(mapObjects.tileOverlays));
374352
});
375353

376354
group('Initialization options', () {
@@ -449,15 +427,12 @@ void main() {
449427
target: LatLng(43.308, -5.6910),
450428
zoom: 12,
451429
),
452-
);
453-
454-
controller.debugSetOverrides(
455-
createMap: (_, gmaps.MapOptions options) {
456-
capturedOptions = options;
457-
return map;
458-
});
459-
460-
controller.init();
430+
)
431+
..debugSetOverrides(createMap: (_, gmaps.MapOptions options) {
432+
capturedOptions = options;
433+
return map;
434+
})
435+
..init();
461436

462437
expect(capturedOptions, isNotNull);
463438
expect(capturedOptions!.zoom, 12);
@@ -467,8 +442,7 @@ void main() {
467442

468443
group('Traffic Layer', () {
469444
testWidgets('by default is disabled', (WidgetTester tester) async {
470-
controller = createController();
471-
controller.init();
445+
controller = createController()..init();
472446
expect(controller.trafficLayer, isNull);
473447
});
474448

@@ -477,9 +451,9 @@ void main() {
477451
controller = createController(
478452
mapConfiguration: const MapConfiguration(
479453
trafficEnabled: true,
480-
));
481-
controller.debugSetOverrides(createMap: (_, __) => map);
482-
controller.init();
454+
))
455+
..debugSetOverrides(createMap: (_, __) => map)
456+
..init();
483457
expect(controller.trafficLayer, isNotNull);
484458
});
485459
});
@@ -496,9 +470,9 @@ void main() {
496470
..zoom = 10
497471
..center = gmaps.LatLng(0, 0),
498472
);
499-
controller = createController();
500-
controller.debugSetOverrides(createMap: (_, __) => map);
501-
controller.init();
473+
controller = createController()
474+
..debugSetOverrides(createMap: (_, __) => map)
475+
..init();
502476
});
503477

504478
group('updateRawOptions', () {
@@ -556,13 +530,9 @@ void main() {
556530

557531
// These are the methods that get forwarded to other controllers, so we just verify calls.
558532
group('Pass-through methods', () {
559-
setUp(() {
560-
controller = createController();
561-
});
562-
563533
testWidgets('updateCircles', (WidgetTester tester) async {
564534
final MockCirclesController mock = MockCirclesController();
565-
controller.debugSetOverrides(circles: mock);
535+
controller = createController()..debugSetOverrides(circles: mock);
566536

567537
final Set<Circle> previous = <Circle>{
568538
const Circle(circleId: CircleId('to-be-updated')),
@@ -589,7 +559,7 @@ void main() {
589559

590560
testWidgets('updateMarkers', (WidgetTester tester) async {
591561
final MockMarkersController mock = MockMarkersController();
592-
controller.debugSetOverrides(markers: mock);
562+
controller = createController()..debugSetOverrides(markers: mock);
593563

594564
final Set<Marker> previous = <Marker>{
595565
const Marker(markerId: MarkerId('to-be-updated')),
@@ -616,7 +586,7 @@ void main() {
616586

617587
testWidgets('updatePolygons', (WidgetTester tester) async {
618588
final MockPolygonsController mock = MockPolygonsController();
619-
controller.debugSetOverrides(polygons: mock);
589+
controller = createController()..debugSetOverrides(polygons: mock);
620590

621591
final Set<Polygon> previous = <Polygon>{
622592
const Polygon(polygonId: PolygonId('to-be-updated')),
@@ -643,7 +613,7 @@ void main() {
643613

644614
testWidgets('updatePolylines', (WidgetTester tester) async {
645615
final MockPolylinesController mock = MockPolylinesController();
646-
controller.debugSetOverrides(polylines: mock);
616+
controller = createController()..debugSetOverrides(polylines: mock);
647617

648618
final Set<Polyline> previous = <Polyline>{
649619
const Polyline(polylineId: PolylineId('to-be-updated')),
@@ -674,11 +644,38 @@ void main() {
674644
}));
675645
});
676646

647+
testWidgets('updateTileOverlays', (WidgetTester tester) async {
648+
final MockTileOverlaysController mock = MockTileOverlaysController();
649+
controller = createController(
650+
mapObjects: MapObjects(tileOverlays: <TileOverlay>{
651+
const TileOverlay(tileOverlayId: TileOverlayId('to-be-updated')),
652+
const TileOverlay(tileOverlayId: TileOverlayId('to-be-removed')),
653+
}))
654+
..debugSetOverrides(tileOverlays: mock);
655+
656+
controller.updateTileOverlays(<TileOverlay>{
657+
const TileOverlay(
658+
tileOverlayId: TileOverlayId('to-be-updated'), visible: false),
659+
const TileOverlay(tileOverlayId: TileOverlayId('to-be-added')),
660+
});
661+
662+
verify(mock.removeTileOverlays(<TileOverlayId>{
663+
const TileOverlayId('to-be-removed'),
664+
}));
665+
verify(mock.addTileOverlays(<TileOverlay>{
666+
const TileOverlay(tileOverlayId: TileOverlayId('to-be-added')),
667+
}));
668+
verify(mock.changeTileOverlays(<TileOverlay>{
669+
const TileOverlay(
670+
tileOverlayId: TileOverlayId('to-be-updated'), visible: false),
671+
}));
672+
});
673+
677674
testWidgets('infoWindow visibility', (WidgetTester tester) async {
678675
final MockMarkersController mock = MockMarkersController();
679676
const MarkerId markerId = MarkerId('marker-with-infowindow');
680677
when(mock.isInfoWindowShown(markerId)).thenReturn(true);
681-
controller.debugSetOverrides(markers: mock);
678+
controller = createController()..debugSetOverrides(markers: mock);
682679

683680
controller.showInfoWindow(markerId);
684681

0 commit comments

Comments
 (0)