Skip to content

Commit

Permalink
[google_maps_flutter_web] Initial support for custom overlays (flutte…
Browse files Browse the repository at this point in the history
…r#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
  • Loading branch information
elitree authored Jul 31, 2023
1 parent 9074ea9 commit 9e21922
Show file tree
Hide file tree
Showing 15 changed files with 801 additions and 151 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.3

* Initial support for custom overlays. [#98596](https://github.com/flutter/flutter/issues/98596).

## 0.5.2

* Adds options for gesture handling and tilt controls.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import 'package:integration_test/integration_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';

import 'google_maps_controller_test.mocks.dart';

@GenerateMocks(<Type>[], customMocks: <MockSpec<dynamic>>[
MockSpec<CirclesController>(onMissingStub: OnMissingStub.returnDefault),
MockSpec<PolygonsController>(onMissingStub: OnMissingStub.returnDefault),
MockSpec<PolylinesController>(onMissingStub: OnMissingStub.returnDefault),
MockSpec<MarkersController>(onMissingStub: OnMissingStub.returnDefault),
@GenerateNiceMocks(<MockSpec<dynamic>>[
MockSpec<CirclesController>(),
MockSpec<PolygonsController>(),
MockSpec<PolylinesController>(),
MockSpec<MarkersController>(),
MockSpec<TileOverlaysController>(),
])
import 'google_maps_controller_test.mocks.dart';

/// Test Google Map Controller
void main() {
Expand Down Expand Up @@ -194,6 +194,15 @@ void main() {
}, throwsAssertionError);
});

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

expect(() {
controller.updateTileOverlays(const <TileOverlay>{});
}, throwsAssertionError);
});

testWidgets('isInfoWindowShown defaults to false',
(WidgetTester tester) async {
controller.dispose();
Expand All @@ -208,27 +217,28 @@ void main() {
late MockMarkersController markers;
late MockPolygonsController polygons;
late MockPolylinesController polylines;
late MockTileOverlaysController tileOverlays;
late gmaps.GMap map;

setUp(() {
circles = MockCirclesController();
markers = MockMarkersController();
polygons = MockPolygonsController();
polylines = MockPolylinesController();
tileOverlays = MockTileOverlaysController();
map = gmaps.GMap(html.DivElement());
});

testWidgets('listens to map events', (WidgetTester tester) async {
controller = createController();
controller.debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
);

controller.init();
controller = createController()
..debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
)
..init();

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

testWidgets("binds geometry controllers to map's",
(WidgetTester tester) async {
controller = createController();
controller.debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
);

controller.init();
controller = createController()
..debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
tileOverlays: tileOverlays,
)
..init();

verify(circles.bindToMap(mapId, map));
verify(markers.bindToMap(mapId, map));
verify(polygons.bindToMap(mapId, map));
verify(polylines.bindToMap(mapId, map));
verify(tileOverlays.bindToMap(mapId, map));
});

testWidgets('renders initial geometry', (WidgetTester tester) async {
controller = createController(
mapObjects: MapObjects(circles: <Circle>{
final MapObjects mapObjects = MapObjects(circles: <Circle>{
const Circle(
circleId: CircleId('circle-1'),
zIndex: 1234,
Expand Down Expand Up @@ -320,57 +330,25 @@ void main() {
LatLng(43.354469, -5.851318),
LatLng(43.354762, -5.850824),
])
}));

controller.debugSetOverrides(
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
);

controller.init();

final Set<Circle> capturedCircles =
verify(circles.addCircles(captureAny)).captured[0] as Set<Circle>;
final Set<Marker> capturedMarkers =
verify(markers.addMarkers(captureAny)).captured[0] as Set<Marker>;
final Set<Polygon> capturedPolygons =
verify(polygons.addPolygons(captureAny)).captured[0]
as Set<Polygon>;
final Set<Polyline> capturedPolylines =
verify(polylines.addPolylines(captureAny)).captured[0]
as Set<Polyline>;

expect(capturedCircles.first.circleId.value, 'circle-1');
expect(capturedCircles.first.zIndex, 1234);
expect(capturedMarkers.first.markerId.value, 'marker-1');
expect(capturedMarkers.first.infoWindow.snippet, 'snippet for test');
expect(capturedMarkers.first.infoWindow.title, 'title for test');
expect(capturedPolygons.first.polygonId.value, 'polygon-1');
expect(capturedPolygons.elementAt(1).polygonId.value,
'polygon-2-with-holes');
expect(capturedPolygons.elementAt(1).holes, isNot(null));
expect(capturedPolylines.first.polylineId.value, 'polyline-1');
});

testWidgets('empty infoWindow does not create InfoWindow instance.',
(WidgetTester tester) async {
controller = createController(
mapObjects: MapObjects(markers: <Marker>{
const Marker(markerId: MarkerId('marker-1')),
}));

controller.debugSetOverrides(
markers: markers,
);

controller.init();

final Set<Marker> capturedMarkers =
verify(markers.addMarkers(captureAny)).captured[0] as Set<Marker>;
}, tileOverlays: <TileOverlay>{
const TileOverlay(tileOverlayId: TileOverlayId('overlay-1'))
});

expect(capturedMarkers.first.infoWindow, InfoWindow.noText);
controller = createController(mapObjects: mapObjects)
..debugSetOverrides(
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
tileOverlays: tileOverlays,
)
..init();

verify(circles.addCircles(mapObjects.circles));
verify(markers.addMarkers(mapObjects.markers));
verify(polygons.addPolygons(mapObjects.polygons));
verify(polylines.addPolylines(mapObjects.polylines));
verify(tileOverlays.addTileOverlays(mapObjects.tileOverlays));
});

group('Initialization options', () {
Expand Down Expand Up @@ -449,15 +427,12 @@ void main() {
target: LatLng(43.308, -5.6910),
zoom: 12,
),
);

controller.debugSetOverrides(
createMap: (_, gmaps.MapOptions options) {
capturedOptions = options;
return map;
});

controller.init();
)
..debugSetOverrides(createMap: (_, gmaps.MapOptions options) {
capturedOptions = options;
return map;
})
..init();

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

group('Traffic Layer', () {
testWidgets('by default is disabled', (WidgetTester tester) async {
controller = createController();
controller.init();
controller = createController()..init();
expect(controller.trafficLayer, isNull);
});

Expand All @@ -477,9 +451,9 @@ void main() {
controller = createController(
mapConfiguration: const MapConfiguration(
trafficEnabled: true,
));
controller.debugSetOverrides(createMap: (_, __) => map);
controller.init();
))
..debugSetOverrides(createMap: (_, __) => map)
..init();
expect(controller.trafficLayer, isNotNull);
});
});
Expand All @@ -496,9 +470,9 @@ void main() {
..zoom = 10
..center = gmaps.LatLng(0, 0),
);
controller = createController();
controller.debugSetOverrides(createMap: (_, __) => map);
controller.init();
controller = createController()
..debugSetOverrides(createMap: (_, __) => map)
..init();
});

group('updateRawOptions', () {
Expand Down Expand Up @@ -556,13 +530,9 @@ void main() {

// These are the methods that get forwarded to other controllers, so we just verify calls.
group('Pass-through methods', () {
setUp(() {
controller = createController();
});

testWidgets('updateCircles', (WidgetTester tester) async {
final MockCirclesController mock = MockCirclesController();
controller.debugSetOverrides(circles: mock);
controller = createController()..debugSetOverrides(circles: mock);

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

testWidgets('updateMarkers', (WidgetTester tester) async {
final MockMarkersController mock = MockMarkersController();
controller.debugSetOverrides(markers: mock);
controller = createController()..debugSetOverrides(markers: mock);

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

testWidgets('updatePolygons', (WidgetTester tester) async {
final MockPolygonsController mock = MockPolygonsController();
controller.debugSetOverrides(polygons: mock);
controller = createController()..debugSetOverrides(polygons: mock);

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

testWidgets('updatePolylines', (WidgetTester tester) async {
final MockPolylinesController mock = MockPolylinesController();
controller.debugSetOverrides(polylines: mock);
controller = createController()..debugSetOverrides(polylines: mock);

final Set<Polyline> previous = <Polyline>{
const Polyline(polylineId: PolylineId('to-be-updated')),
Expand Down Expand Up @@ -674,11 +644,38 @@ void main() {
}));
});

testWidgets('updateTileOverlays', (WidgetTester tester) async {
final MockTileOverlaysController mock = MockTileOverlaysController();
controller = createController(
mapObjects: MapObjects(tileOverlays: <TileOverlay>{
const TileOverlay(tileOverlayId: TileOverlayId('to-be-updated')),
const TileOverlay(tileOverlayId: TileOverlayId('to-be-removed')),
}))
..debugSetOverrides(tileOverlays: mock);

controller.updateTileOverlays(<TileOverlay>{
const TileOverlay(
tileOverlayId: TileOverlayId('to-be-updated'), visible: false),
const TileOverlay(tileOverlayId: TileOverlayId('to-be-added')),
});

verify(mock.removeTileOverlays(<TileOverlayId>{
const TileOverlayId('to-be-removed'),
}));
verify(mock.addTileOverlays(<TileOverlay>{
const TileOverlay(tileOverlayId: TileOverlayId('to-be-added')),
}));
verify(mock.changeTileOverlays(<TileOverlay>{
const TileOverlay(
tileOverlayId: TileOverlayId('to-be-updated'), visible: false),
}));
});

testWidgets('infoWindow visibility', (WidgetTester tester) async {
final MockMarkersController mock = MockMarkersController();
const MarkerId markerId = MarkerId('marker-with-infowindow');
when(mock.isInfoWindowShown(markerId)).thenReturn(true);
controller.debugSetOverrides(markers: mock);
controller = createController()..debugSetOverrides(markers: mock);

controller.showInfoWindow(markerId);

Expand Down
Loading

0 comments on commit 9e21922

Please sign in to comment.