From d2e53de75ac37fd50890c4a6fcf2d19fc892f706 Mon Sep 17 00:00:00 2001 From: chung2012 Date: Fri, 6 Mar 2020 18:57:29 +0800 Subject: [PATCH 01/58] Partially implement google_maps_flutter_web --- lib/google_maps_flutter_web.dart | 148 +++++++++++++++++++++++++++++++ pubspec.yaml | 33 +++++++ 2 files changed, 181 insertions(+) create mode 100644 lib/google_maps_flutter_web.dart create mode 100644 pubspec.yaml diff --git a/lib/google_maps_flutter_web.dart b/lib/google_maps_flutter_web.dart new file mode 100644 index 000000000000..3394490575e0 --- /dev/null +++ b/lib/google_maps_flutter_web.dart @@ -0,0 +1,148 @@ +import 'dart:async'; +import 'dart:html'; +import 'dart:ui' as ui; + +import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/gestures.dart'; + +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:google_maps/google_maps.dart'; +//import 'package:js/js.dart'; + +/// The web implementation of [GoogleMapsFlutterPlatform]. +/// +/// This class implements the `package:google_maps_flutter` functionality for the web. +class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { + + /// Registers this class as the default instance of [GoogleMapsFlutterPlatform]. + static void registerWith(Registrar registrar) { + GoogleMapsFlutterPlatform.instance = GoogleMapsPlugin(); + } + + static Future get platformVersion async { + return "1.0"; + } + + int _id ; + HtmlElementView _mapView; + MapOptions _mapOptions; + DivElement _map; + + @override + Future init(int id) { + _id = id; + _map = DivElement() + ..id = 'plugins.flutter.io/google_maps_$id' +// ..style.width = "100%" +// ..style.height = "100%" +// ..style.border = 'none' + ; + _mapOptions = MapOptions() + ..zoom = 8 + ..center = LatLng(-34.397, 150.644) + ; + GMap(_map, _mapOptions); + ui.platformViewRegistry.registerViewFactory( + 'plugins.flutter.io/google_maps_$_id', + (int viewId) => _map, + ); + } + + @override + void setMethodCallHandler(dynamic call) { + throw UnimplementedError( + 'setMethodCallHandler() has not been implemented.'); + } + + @override + Future updateMapOptions(Map optionsUpdate) { + throw UnimplementedError('updateMapOptions() has not been implemented.'); + } + + @override + Future updateMarkers(Map markerUpdates) { + throw UnimplementedError('updateMarkers() has not been implemented.'); + } + + @override + Future updatePolygons(Map polygonUpdates) { + throw UnimplementedError('updatePolygons() has not been implemented.'); + } + + @override + Future updatePolylines(Map polylineUpdates) { + throw UnimplementedError('updatePolylines() has not been implemented.'); + } + + @override + Future updateCircles(Map circleUpdates) { + throw UnimplementedError('updateCircles() has not been implemented.'); + } + + @override + Future animateCamera(dynamic cameraUpdate) { + throw UnimplementedError('animateCamera() has not been implemented.'); + } + + @override + Future moveCamera(dynamic cameraUpdate) { + throw UnimplementedError('moveCamera() has not been implemented.'); + } + + @override + Future setMapStyle(String mapStyle) { + throw UnimplementedError('setMapStyle() has not been implemented.'); + } + + @override + Future> getVisibleRegion() { + throw UnimplementedError('getVisibleRegion() has not been implemented.'); + } + + @override + Future> getLatLng(dynamic latLng) { + throw UnimplementedError('getLatLng() has not been implemented.'); + } + + @override + Future> getScreenCoordinate(dynamic screenCoordinateInJson) { + throw UnimplementedError('getScreenCoordinate() has not been implemented.'); + } + + @override + Future showMarkerInfoWindow(String markerId) { + throw UnimplementedError( + 'showMarkerInfoWindow() has not been implemented.'); + } + + @override + Future hideMarkerInfoWindow(String markerId) { + throw UnimplementedError( + 'hideMarkerInfoWindow() has not been implemented.'); + } + + @override + Future isMarkerInfoWindowShown(String markerId) { + throw UnimplementedError('updateMapOptions() has not been implemented.'); + } + + @override + Future getZoomLevel() { + throw UnimplementedError('getZoomLevel() has not been implemented.'); + } + + @override + Widget buildView( + Map creationParams, + Set> gestureRecognizers, + PlatformViewCreatedCallback onPlatformViewCreated) { + init(0); + _mapView = HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$_id'); + return _mapView; + } + +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 000000000000..1cfb03186f4d --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,33 @@ +name: google_maps_flutter_web +description: Web platform implementation of google_maps_flutter +homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter +version: 0.0.1 + +flutter: + plugin: + platforms: + web: + pluginClass: GoogleMapsPlugin + fileName: google_maps_flutter_web.dart + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + meta: ^1.1.7 + google_maps_flutter_platform_interface: + path: ../google_maps_flutter_platform_interface + google_maps: ">=3.0.0 <4.0.0" + +dev_dependencies: + flutter_test: + sdk: flutter + url_launcher: ^5.2.5 + pedantic: ^1.8.0 + google_maps_flutter: + path: ../google_maps_flutter + +environment: + sdk: ">=2.0.0-dev.28.0 <3.0.0" + flutter: ">=1.10.0 <2.0.0" From b397672f0994bbb841ae17587f3723a8413e76ad Mon Sep 17 00:00:00 2001 From: chung2012 Date: Fri, 20 Mar 2020 23:04:53 +0800 Subject: [PATCH 02/58] default loc; --- lib/google_maps_flutter_web.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/google_maps_flutter_web.dart b/lib/google_maps_flutter_web.dart index 3394490575e0..11abe32912aa 100644 --- a/lib/google_maps_flutter_web.dart +++ b/lib/google_maps_flutter_web.dart @@ -42,8 +42,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { // ..style.border = 'none' ; _mapOptions = MapOptions() - ..zoom = 8 - ..center = LatLng(-34.397, 150.644) + ..zoom = 7 + ..center = LatLng(4.7438427,107.9170665) ; GMap(_map, _mapOptions); ui.platformViewRegistry.registerViewFactory( From 239b8c931dca83d73e70c4550a29d2ec3b2c7d32 Mon Sep 17 00:00:00 2001 From: chung2012 Date: Wed, 1 Apr 2020 00:54:48 +0800 Subject: [PATCH 03/58] buildView document --- lib/google_maps_flutter_web.dart | 272 +++++++++++++++++++++++++------ 1 file changed, 223 insertions(+), 49 deletions(-) diff --git a/lib/google_maps_flutter_web.dart b/lib/google_maps_flutter_web.dart index 11abe32912aa..bcb6e0c5ec1f 100644 --- a/lib/google_maps_flutter_web.dart +++ b/lib/google_maps_flutter_web.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:html'; import 'dart:ui' as ui; +import 'dart:math' show sqrt; import 'package:flutter/widgets.dart'; import 'package:flutter/material.dart'; @@ -10,7 +11,7 @@ import 'package:flutter/gestures.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:google_maps/google_maps.dart'; +import 'package:google_maps/google_maps.dart' as gm; //import 'package:js/js.dart'; /// The web implementation of [GoogleMapsFlutterPlatform]. @@ -27,122 +28,295 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return "1.0"; } - int _id ; - HtmlElementView _mapView; - MapOptions _mapOptions; - DivElement _map; + int _id = 0 ; + List _mapList = List(); @override - Future init(int id) { - _id = id; - _map = DivElement() - ..id = 'plugins.flutter.io/google_maps_$id' -// ..style.width = "100%" -// ..style.height = "100%" -// ..style.border = 'none' - ; - _mapOptions = MapOptions() - ..zoom = 7 - ..center = LatLng(4.7438427,107.9170665) - ; - GMap(_map, _mapOptions); - ui.platformViewRegistry.registerViewFactory( - 'plugins.flutter.io/google_maps_$_id', - (int viewId) => _map, - ); + Future init(int mapId) { } @override - void setMethodCallHandler(dynamic call) { - throw UnimplementedError( - 'setMethodCallHandler() has not been implemented.'); + Future updateMapOptions( + Map optionsUpdate, { + @required int mapId, + }) { +// throw UnimplementedError('updateMapOptions() has not been implemented.'); } @override - Future updateMapOptions(Map optionsUpdate) { - throw UnimplementedError('updateMapOptions() has not been implemented.'); - } - - @override - Future updateMarkers(Map markerUpdates) { + Future updateMarkers( + MarkerUpdates markerUpdates, { + @required int mapId, + }) { throw UnimplementedError('updateMarkers() has not been implemented.'); } @override - Future updatePolygons(Map polygonUpdates) { + Future updatePolygons( + PolygonUpdates polygonUpdates, { + @required int mapId, + }) { throw UnimplementedError('updatePolygons() has not been implemented.'); } @override - Future updatePolylines(Map polylineUpdates) { + Future updatePolylines( + PolylineUpdates polylineUpdates, { + @required int mapId, + }) { throw UnimplementedError('updatePolylines() has not been implemented.'); } @override - Future updateCircles(Map circleUpdates) { + Future updateCircles( + CircleUpdates circleUpdates, { + @required int mapId, + }) { throw UnimplementedError('updateCircles() has not been implemented.'); } @override - Future animateCamera(dynamic cameraUpdate) { + Future animateCamera( + CameraUpdate cameraUpdate, { + @required int mapId, + }) { throw UnimplementedError('animateCamera() has not been implemented.'); } @override - Future moveCamera(dynamic cameraUpdate) { + Future moveCamera( + CameraUpdate cameraUpdate, { + @required int mapId, + }) { throw UnimplementedError('moveCamera() has not been implemented.'); } @override - Future setMapStyle(String mapStyle) { + Future setMapStyle( + String mapStyle, { + @required int mapId, + }) { throw UnimplementedError('setMapStyle() has not been implemented.'); } @override - Future> getVisibleRegion() { + Future getVisibleRegion({ + @required int mapId, + }) { throw UnimplementedError('getVisibleRegion() has not been implemented.'); } @override - Future> getLatLng(dynamic latLng) { - throw UnimplementedError('getLatLng() has not been implemented.'); + Future getScreenCoordinate( + LatLng latLng, { + @required int mapId, + }) { + throw UnimplementedError('getScreenCoordinate() has not been implemented.'); } @override - Future> getScreenCoordinate(dynamic screenCoordinateInJson) { - throw UnimplementedError('getScreenCoordinate() has not been implemented.'); + Future getLatLng( + ScreenCoordinate screenCoordinate, { + @required int mapId, + }) { + throw UnimplementedError('getLatLng() has not been implemented.'); } @override - Future showMarkerInfoWindow(String markerId) { + Future showMarkerInfoWindow( + MarkerId markerId, { + @required int mapId, + }) { throw UnimplementedError( 'showMarkerInfoWindow() has not been implemented.'); } @override - Future hideMarkerInfoWindow(String markerId) { + Future hideMarkerInfoWindow( + MarkerId markerId, { + @required int mapId, + }) { throw UnimplementedError( 'hideMarkerInfoWindow() has not been implemented.'); } @override - Future isMarkerInfoWindowShown(String markerId) { + Future isMarkerInfoWindowShown( + MarkerId markerId, { + @required int mapId, + }) { throw UnimplementedError('updateMapOptions() has not been implemented.'); } @override - Future getZoomLevel() { + Future getZoomLevel({ + @required int mapId, + }) { throw UnimplementedError('getZoomLevel() has not been implemented.'); } + // The following are the 11 possible streams of data from the native side + // into the plugin + + @override + Stream onCameraMoveStarted({@required int mapId}) { + throw UnimplementedError('onCameraMoveStarted() has not been implemented.'); + } + + @override + Stream onCameraMove({@required int mapId}) { + throw UnimplementedError('onCameraMove() has not been implemented.'); + } + + @override + Stream onCameraIdle({@required int mapId}) { + throw UnimplementedError('onCameraMove() has not been implemented.'); + } + + @override + Stream onMarkerTap({@required int mapId}) { + throw UnimplementedError('onMarkerTap() has not been implemented.'); + } + + @override + Stream onInfoWindowTap({@required int mapId}) { + throw UnimplementedError('onInfoWindowTap() has not been implemented.'); + } + + @override + Stream onMarkerDragEnd({@required int mapId}) { + throw UnimplementedError('onMarkerDragEnd() has not been implemented.'); + } + + @override + Stream onPolylineTap({@required int mapId}) { + throw UnimplementedError('onPolylineTap() has not been implemented.'); + } + + @override + Stream onPolygonTap({@required int mapId}) { + throw UnimplementedError('onPolygonTap() has not been implemented.'); + } + + @override + Stream onCircleTap({@required int mapId}) { + throw UnimplementedError('onCircleTap() has not been implemented.'); + } + + @override + Stream onTap({@required int mapId}) { + throw UnimplementedError('onTap() has not been implemented.'); + } + + @override + Stream onLongPress({@required int mapId}) { + throw UnimplementedError('onLongPress() has not been implemented.'); + } + @override Widget buildView( Map creationParams, Set> gestureRecognizers, PlatformViewCreatedCallback onPlatformViewCreated) { - init(0); - _mapView = HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$_id'); - return _mapView; + + gm.MapOptions options; + DivElement div; + + List> circlesToAdd= null; + + creationParams.forEach((key, value) { + if(key == 'options') updateMapOptions(value); +// else if(key == 'markersToAdd') updateMarkers(value); +// else if(key == 'polygonsToAdd') updatePolygons(value); +// else if(key == 'polylinesToAdd') updatePolylines(value); + + else if(key == 'circlesToAdd') { + circlesToAdd = value; +// List> list = value; +// Set current = Set (); +// list.forEach((circle) { +// current.add( +// Circle( +// circleId: CircleId( circle['circleId'] ), +// consumeTapEvents:circle['consumeTapEvents'], +// fillColor:Color(circle['fillColor']), +// center:LatLng.fromJson(circle['center']), +// radius:circle['radius'], +// strokeColor:Color(circle['strokeColor']), +// strokeWidth:circle['strokeWidth'], +// visible:circle['visible'], +// zIndex:circle['zIndex'] +// ) +// ); +// }); +// updateCircles(CircleUpdates.from(null, current)); + + } else if(key == 'initialCameraPosition') { + print('initialCameraPosition => $value'); + CameraPosition cameraPos = CameraPosition.fromMap(value); + print(cameraPos.target.latitude); + options = gm.MapOptions() + ..zoom = cameraPos.zoom + ..center = gm.LatLng( + cameraPos.target.latitude, + cameraPos.target.longitude + ) + ; + } else { + print('un-handle >>$key'); + } + } + ); + + int id=_id++; + div = DivElement() + ..id = 'plugins.flutter.io/google_maps_$id' + ; + ui.platformViewRegistry.registerViewFactory( + 'plugins.flutter.io/google_maps_$id', + (int viewId) => div, + ); + HtmlElementView html = HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$id'); + _mapList.add(GMap(html, gm.GMap(div, options))); + + + ///TODO move to updateCircles + if(circlesToAdd != null) { + circlesToAdd.forEach((circle) { + Circle c = Circle( + circleId: CircleId( circle['circleId'] ), + consumeTapEvents:circle['consumeTapEvents'], + fillColor:Color(circle['fillColor']), + center:LatLng.fromJson(circle['center']), + radius:circle['radius'], + strokeColor:Color(circle['strokeColor']), + strokeWidth:circle['strokeWidth'], + visible:circle['visible'], + zIndex:circle['zIndex'] + ); +//https://github.com/cylyl/dart-google-maps/blob/master/example/05-drawing_on_map/circle-simple/page.dart + final populationOptions = gm.CircleOptions() + ..strokeColor = c.strokeColor.toString() + ..strokeOpacity = 0.8 + ..strokeWeight = c.strokeWidth + ..fillColor = c.fillColor.toString() + ..fillOpacity = 0.35 + ..map = _mapList.elementAt(id).gmap + ..center = gm.LatLng(c.center.latitude,c.center.longitude) + ..radius = circle['radius'] + ; + gm.Circle(populationOptions); + + }); + } + return html; } } + +class GMap { + final HtmlElementView html; + final gm.GMap gmap; + GMap(this.html, + this.gmap); +} \ No newline at end of file From 0e980a5425cbe2178eed02532c2fb85954c68a2d Mon Sep 17 00:00:00 2001 From: chung2012 Date: Thu, 2 Apr 2020 01:40:47 +0800 Subject: [PATCH 04/58] Circle --- lib/google_maps_flutter_web.dart | 318 +-------------------------- lib/src/circle.dart | 77 +++++++ lib/src/circles.dart | 90 ++++++++ lib/src/google_map_web.dart | 78 +++++++ lib/src/google_maps_flutter_web.dart | 257 ++++++++++++++++++++++ pubspec.yaml | 1 + 6 files changed, 514 insertions(+), 307 deletions(-) create mode 100644 lib/src/circle.dart create mode 100644 lib/src/circles.dart create mode 100644 lib/src/google_map_web.dart create mode 100644 lib/src/google_maps_flutter_web.dart diff --git a/lib/google_maps_flutter_web.dart b/lib/google_maps_flutter_web.dart index bcb6e0c5ec1f..8b1effe0ef82 100644 --- a/lib/google_maps_flutter_web.dart +++ b/lib/google_maps_flutter_web.dart @@ -1,322 +1,26 @@ +library google_maps_flutter_web; + import 'dart:async'; import 'dart:html'; import 'dart:ui' as ui; import 'dart:math' show sqrt; +import 'dart:collection'; +import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/gestures.dart'; +import 'package:stream_transform/stream_transform.dart'; + import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:google_maps/google_maps.dart' as gm; +import 'package:google_maps/google_maps.dart' as GoogleMap; //import 'package:js/js.dart'; -/// The web implementation of [GoogleMapsFlutterPlatform]. -/// -/// This class implements the `package:google_maps_flutter` functionality for the web. -class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { - - /// Registers this class as the default instance of [GoogleMapsFlutterPlatform]. - static void registerWith(Registrar registrar) { - GoogleMapsFlutterPlatform.instance = GoogleMapsPlugin(); - } - - static Future get platformVersion async { - return "1.0"; - } - - int _id = 0 ; - List _mapList = List(); - - @override - Future init(int mapId) { - } - - @override - Future updateMapOptions( - Map optionsUpdate, { - @required int mapId, - }) { -// throw UnimplementedError('updateMapOptions() has not been implemented.'); - } - - @override - Future updateMarkers( - MarkerUpdates markerUpdates, { - @required int mapId, - }) { - throw UnimplementedError('updateMarkers() has not been implemented.'); - } - - @override - Future updatePolygons( - PolygonUpdates polygonUpdates, { - @required int mapId, - }) { - throw UnimplementedError('updatePolygons() has not been implemented.'); - } - - @override - Future updatePolylines( - PolylineUpdates polylineUpdates, { - @required int mapId, - }) { - throw UnimplementedError('updatePolylines() has not been implemented.'); - } - - @override - Future updateCircles( - CircleUpdates circleUpdates, { - @required int mapId, - }) { - throw UnimplementedError('updateCircles() has not been implemented.'); - } - - @override - Future animateCamera( - CameraUpdate cameraUpdate, { - @required int mapId, - }) { - throw UnimplementedError('animateCamera() has not been implemented.'); - } - - @override - Future moveCamera( - CameraUpdate cameraUpdate, { - @required int mapId, - }) { - throw UnimplementedError('moveCamera() has not been implemented.'); - } - - @override - Future setMapStyle( - String mapStyle, { - @required int mapId, - }) { - throw UnimplementedError('setMapStyle() has not been implemented.'); - } - - @override - Future getVisibleRegion({ - @required int mapId, - }) { - throw UnimplementedError('getVisibleRegion() has not been implemented.'); - } - - @override - Future getScreenCoordinate( - LatLng latLng, { - @required int mapId, - }) { - throw UnimplementedError('getScreenCoordinate() has not been implemented.'); - } - - @override - Future getLatLng( - ScreenCoordinate screenCoordinate, { - @required int mapId, - }) { - throw UnimplementedError('getLatLng() has not been implemented.'); - } - - @override - Future showMarkerInfoWindow( - MarkerId markerId, { - @required int mapId, - }) { - throw UnimplementedError( - 'showMarkerInfoWindow() has not been implemented.'); - } - - @override - Future hideMarkerInfoWindow( - MarkerId markerId, { - @required int mapId, - }) { - throw UnimplementedError( - 'hideMarkerInfoWindow() has not been implemented.'); - } - - @override - Future isMarkerInfoWindowShown( - MarkerId markerId, { - @required int mapId, - }) { - throw UnimplementedError('updateMapOptions() has not been implemented.'); - } - - @override - Future getZoomLevel({ - @required int mapId, - }) { - throw UnimplementedError('getZoomLevel() has not been implemented.'); - } - - // The following are the 11 possible streams of data from the native side - // into the plugin - - @override - Stream onCameraMoveStarted({@required int mapId}) { - throw UnimplementedError('onCameraMoveStarted() has not been implemented.'); - } - - @override - Stream onCameraMove({@required int mapId}) { - throw UnimplementedError('onCameraMove() has not been implemented.'); - } - - @override - Stream onCameraIdle({@required int mapId}) { - throw UnimplementedError('onCameraMove() has not been implemented.'); - } - - @override - Stream onMarkerTap({@required int mapId}) { - throw UnimplementedError('onMarkerTap() has not been implemented.'); - } - - @override - Stream onInfoWindowTap({@required int mapId}) { - throw UnimplementedError('onInfoWindowTap() has not been implemented.'); - } - - @override - Stream onMarkerDragEnd({@required int mapId}) { - throw UnimplementedError('onMarkerDragEnd() has not been implemented.'); - } - - @override - Stream onPolylineTap({@required int mapId}) { - throw UnimplementedError('onPolylineTap() has not been implemented.'); - } - - @override - Stream onPolygonTap({@required int mapId}) { - throw UnimplementedError('onPolygonTap() has not been implemented.'); - } - - @override - Stream onCircleTap({@required int mapId}) { - throw UnimplementedError('onCircleTap() has not been implemented.'); - } - - @override - Stream onTap({@required int mapId}) { - throw UnimplementedError('onTap() has not been implemented.'); - } - - @override - Stream onLongPress({@required int mapId}) { - throw UnimplementedError('onLongPress() has not been implemented.'); - } - - @override - Widget buildView( - Map creationParams, - Set> gestureRecognizers, - PlatformViewCreatedCallback onPlatformViewCreated) { - - gm.MapOptions options; - DivElement div; - - List> circlesToAdd= null; - - creationParams.forEach((key, value) { - if(key == 'options') updateMapOptions(value); -// else if(key == 'markersToAdd') updateMarkers(value); -// else if(key == 'polygonsToAdd') updatePolygons(value); -// else if(key == 'polylinesToAdd') updatePolylines(value); - - else if(key == 'circlesToAdd') { - circlesToAdd = value; -// List> list = value; -// Set current = Set (); -// list.forEach((circle) { -// current.add( -// Circle( -// circleId: CircleId( circle['circleId'] ), -// consumeTapEvents:circle['consumeTapEvents'], -// fillColor:Color(circle['fillColor']), -// center:LatLng.fromJson(circle['center']), -// radius:circle['radius'], -// strokeColor:Color(circle['strokeColor']), -// strokeWidth:circle['strokeWidth'], -// visible:circle['visible'], -// zIndex:circle['zIndex'] -// ) -// ); -// }); -// updateCircles(CircleUpdates.from(null, current)); - - } else if(key == 'initialCameraPosition') { - print('initialCameraPosition => $value'); - CameraPosition cameraPos = CameraPosition.fromMap(value); - print(cameraPos.target.latitude); - options = gm.MapOptions() - ..zoom = cameraPos.zoom - ..center = gm.LatLng( - cameraPos.target.latitude, - cameraPos.target.longitude - ) - ; - } else { - print('un-handle >>$key'); - } - } - ); - - int id=_id++; - div = DivElement() - ..id = 'plugins.flutter.io/google_maps_$id' - ; - ui.platformViewRegistry.registerViewFactory( - 'plugins.flutter.io/google_maps_$id', - (int viewId) => div, - ); - HtmlElementView html = HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$id'); - _mapList.add(GMap(html, gm.GMap(div, options))); - - - ///TODO move to updateCircles - if(circlesToAdd != null) { - circlesToAdd.forEach((circle) { - Circle c = Circle( - circleId: CircleId( circle['circleId'] ), - consumeTapEvents:circle['consumeTapEvents'], - fillColor:Color(circle['fillColor']), - center:LatLng.fromJson(circle['center']), - radius:circle['radius'], - strokeColor:Color(circle['strokeColor']), - strokeWidth:circle['strokeWidth'], - visible:circle['visible'], - zIndex:circle['zIndex'] - ); -//https://github.com/cylyl/dart-google-maps/blob/master/example/05-drawing_on_map/circle-simple/page.dart - final populationOptions = gm.CircleOptions() - ..strokeColor = c.strokeColor.toString() - ..strokeOpacity = 0.8 - ..strokeWeight = c.strokeWidth - ..fillColor = c.fillColor.toString() - ..fillOpacity = 0.35 - ..map = _mapList.elementAt(id).gmap - ..center = gm.LatLng(c.center.latitude,c.center.longitude) - ..radius = circle['radius'] - ; - gm.Circle(populationOptions); - - }); - } - return html; - } - -} - -class GMap { - final HtmlElementView html; - final gm.GMap gmap; - GMap(this.html, - this.gmap); -} \ No newline at end of file +part 'src/google_maps_flutter_web.dart'; +part 'src/google_map_web.dart'; +part 'src/circle.dart'; +part 'src/circles.dart'; \ No newline at end of file diff --git a/lib/src/circle.dart b/lib/src/circle.dart new file mode 100644 index 000000000000..cafcd22039c1 --- /dev/null +++ b/lib/src/circle.dart @@ -0,0 +1,77 @@ +part of google_maps_flutter_web; + +GoogleMap.CircleOptions _circleOptionsFromCircle(GoogleMap.GMap googleMap, Circle circle) { + final populationOptions = GoogleMap.CircleOptions() + ..strokeColor = '#'+circle.strokeColor.value.toRadixString(16) + ..strokeOpacity = 0.8 + ..strokeWeight = circle.strokeWidth + ..fillColor = '#'+circle.fillColor.value.toRadixString(16) + ..fillOpacity = 0.35 + ..map = googleMap + ..center = GoogleMap.LatLng(circle.center.latitude,circle.center.longitude) + ..radius = circle.radius + ..visible = circle.visible + ; + return populationOptions; +} + + +CircleUpdates _circleFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((circle) { + CircleId circleId = CircleId(circle['circleId']); + current.add( + Circle( + circleId: circleId, + consumeTapEvents: circle['consumeTapEvents'], + fillColor: Color(circle['fillColor']), + center: LatLng.fromJson(circle['center']), + radius: circle['radius'], + strokeColor: Color(circle['strokeColor']), + strokeWidth: circle['strokeWidth'], + visible: circle['visible'], + zIndex: circle['zIndex'], + ) + ); + }); + return CircleUpdates.from(null, current); + } + return null; +} + +/// +class CircleController { + + GoogleMap.Circle _circle; + + bool consumeTapEvents = false; + + ui.VoidCallback ontab; + + /// + CircleController({@required GoogleMap.Circle circle, this.consumeTapEvents, this.ontab}){ + _circle = circle; + if(consumeTapEvents) { + circle.onClick.listen((event) { + if(ontab !=null) ontab.call(); + }); + } + } + + set circle (GoogleMap.Circle circle) => {_circle = circle}; + + ///TODO + void update(GoogleMap.CircleOptions options) { + _circle.options = options; + } + + void remove() { + _circle.visible = false; + _circle.radius = 0; + _circle.map = null; + _circle = null; + //_circle.remove(); + } +} \ No newline at end of file diff --git a/lib/src/circles.dart b/lib/src/circles.dart new file mode 100644 index 000000000000..3880fac55b89 --- /dev/null +++ b/lib/src/circles.dart @@ -0,0 +1,90 @@ +part of google_maps_flutter_web; + +///TODO +class CirclesController{ + GoogleMap.GMap googleMap; + final Map _circleIdToController; + + GoogleMapController googleMapController; + + + ///TODO + CirclesController({ + @required this.googleMapController + }) + : _circleIdToController = Map(); + + + ///TODO + void addCircles(Set circlesToAdd) { + if(circlesToAdd != null) { + circlesToAdd.forEach((circle) { + addCircle(circle); + }); + } + } + + ///TODO + void setGoogleMap(GoogleMap.GMap googleMap) { + this.googleMap = googleMap; + } + + /// add [GoogleMap.Circle] to [GoogleMap.GMap]. + void addCircle(Circle circle) { + if(circle == null) return; + final populationOptions = _circleOptionsFromCircle(googleMap, circle); + GoogleMap.Circle gmCircle = GoogleMap.Circle(populationOptions); + CircleController controller = CircleController( + circle: gmCircle, + consumeTapEvents:circle.consumeTapEvents, + ontab:(){ googleMapController.onCircleTap(circle.circleId);}); + _circleIdToController[circle.circleId.value] = controller; + } + + + void changeCircles(Set circlesToChange) { + if (circlesToChange != null) { + circlesToChange.forEach((circleToChange) { + changeCircle(circleToChange); + }); + } + } + + void changeCircle(Circle circle) { + if (circle == null) { return;} + + CircleController circleController = _circleIdToController[circle.circleId.value]; + if (circleController != null) { + circleController.update( + _circleOptionsFromCircle(googleMap, circle)); + } + } + + void removeCircles(Set circleIdsToRemove) { + if (circleIdsToRemove == null) {return;} + circleIdsToRemove.forEach((circleId) { + if(circleId != null) { + final CircleController circleController = _circleIdToController[circleId + .value]; + if(circleController != null) { + circleController.remove(); + } + } + }); + } + + ///String circleId = googleMapsCircleIdToDartCircleId.get(googleCircleId); + // if (circleId == null) { + // return false; + // } + // methodChannel.invokeMethod("circle#onTap", Convert.circleIdToJson(circleId)); + // CircleController circleController = circleIdToController.get(circleId); + // if (circleController != null) { + // return circleController.consumeTapEvents(); + // } + // return false; + bool onCircleTap(String googleCircleId) { + + } + +} \ No newline at end of file diff --git a/lib/src/google_map_web.dart b/lib/src/google_map_web.dart new file mode 100644 index 000000000000..9ca29dc90e52 --- /dev/null +++ b/lib/src/google_map_web.dart @@ -0,0 +1,78 @@ +part of google_maps_flutter_web; + +///TODO +class GoogleMapController { + ///TODO + final int mapId; + + ///TODO + HtmlElementView html; + + ///TODO + GoogleMap.GMap googleMap; + ///TODO + DivElement div; + ///TODO + final GoogleMap.MapOptions options; + + CameraPosition position; + + + CirclesController circlesController; + + Set initialCircles; + + final StreamController streamController; + + ///TODO + GoogleMapController.build({ + @required this.mapId, + @required this.streamController, + @required this.options, + @required this.position, + @required this.initialCircles, onPlatformViewCreated, + }) { + circlesController = CirclesController(googleMapController: this); + html = HtmlElementView( + viewType: 'plugins.flutter.io/google_maps_$mapId' + ); + onPlatformViewCreated.call(mapId); + div = DivElement() + ..id = 'plugins.flutter.io/google_maps_$mapId' + ; + ui.platformViewRegistry.registerViewFactory( + 'plugins.flutter.io/google_maps_$mapId', + (int viewId) => div, + ); + + googleMap = GoogleMap.GMap(div, options); + + onMapReady(googleMap); + setInitialCircles(initialCircles); + + } + + void updateInitialCircles() { + if(initialCircles == null) return; + circlesController.addCircles(initialCircles); + } + + void onMapReady(GoogleMap.GMap googleMap) { + this.googleMap = googleMap; + //set googlemap listener + circlesController.setGoogleMap(googleMap); + updateInitialCircles(); + } + + void setInitialCircles(Set initialCircles) { + this.initialCircles = initialCircles; + if (googleMap != null) { + updateInitialCircles(); + } + } + + void onCircleTap(CircleId circleId) { + streamController.add(CircleTapEvent(mapId, circleId)); + } +} + diff --git a/lib/src/google_maps_flutter_web.dart b/lib/src/google_maps_flutter_web.dart new file mode 100644 index 000000000000..66fe2c0aebbe --- /dev/null +++ b/lib/src/google_maps_flutter_web.dart @@ -0,0 +1,257 @@ +part of google_maps_flutter_web; + +/// The web implementation of [GoogleMapsFlutterPlatform]. +/// +/// This class implements the `package:google_maps_flutter` functionality for the web. +class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { + + /// Registers this class as the default instance of [GoogleMapsFlutterPlatform]. + static void registerWith(Registrar registrar) { + GoogleMapsFlutterPlatform.instance = GoogleMapsPlugin(); + } + + static Future get platformVersion async { + return "1.0"; + } + + int _id = 0 ; + HashMap _mapById = HashMap(); + final StreamController _controller = + StreamController.broadcast(); + + Stream _events(int mapId) => + _controller.stream.where((event) => event.mapId == mapId); + + @override + Future init(int mapId) { + } + + @override + Future updateMapOptions( + Map optionsUpdate, { + @required int mapId, + }) { +// //throw UnimplementedError('updateMapOptions() has not been implemented.'); + } + + @override + Future updateMarkers( + MarkerUpdates markerUpdates, { + @required int mapId, + }) { +// //throw UnimplementedError('updateMarkers() has not been implemented.'); + } + + @override + Future updatePolygons( + PolygonUpdates polygonUpdates, { + @required int mapId, + }) { +// //throw UnimplementedError('updatePolygons() has not been implemented.'); + } + + @override + Future updatePolylines( + PolylineUpdates polylineUpdates, { + @required int mapId, + }) { +// //throw UnimplementedError('updatePolylines() has not been implemented.'); + } + + @override + Future updateCircles( + CircleUpdates circleUpdates, { + @required int mapId, + }) { + _mapById[mapId].circlesController.addCircles(circleUpdates.circlesToAdd); + _mapById[mapId].circlesController.changeCircles(circleUpdates.circlesToChange); + _mapById[mapId].circlesController.removeCircles(circleUpdates.circleIdsToRemove); + } + + @override + Future animateCamera( + CameraUpdate cameraUpdate, { + @required int mapId, + }) { + //throw UnimplementedError('animateCamera() has not been implemented.'); + } + + @override + Future moveCamera( + CameraUpdate cameraUpdate, { + @required int mapId, + }) { + //throw UnimplementedError('moveCamera() has not been implemented.'); + } + + @override + Future setMapStyle( + String mapStyle, { + @required int mapId, + }) { + //throw UnimplementedError('setMapStyle() has not been implemented.'); + } + + @override + Future getVisibleRegion({ + @required int mapId, + }) { + //throw UnimplementedError('getVisibleRegion() has not been implemented.'); + } + + @override + Future getScreenCoordinate( + LatLng latLng, { + @required int mapId, + }) { + //throw UnimplementedError('getScreenCoordinate() has not been implemented.'); + } + + @override + Future getLatLng( + ScreenCoordinate screenCoordinate, { + @required int mapId, + }) { + //throw UnimplementedError('getLatLng() has not been implemented.'); + } + + @override + Future showMarkerInfoWindow( + MarkerId markerId, { + @required int mapId, + }) { + //throw UnimplementedError( 'showMarkerInfoWindow() has not been implemented.'); + } + + @override + Future hideMarkerInfoWindow( + MarkerId markerId, { + @required int mapId, + }) { + //throw UnimplementedError( 'hideMarkerInfoWindow() has not been implemented.'); + } + + @override + Future isMarkerInfoWindowShown( + MarkerId markerId, { + @required int mapId, + }) { + //throw UnimplementedError('updateMapOptions() has not been implemented.'); + } + + @override + Future getZoomLevel({ + @required int mapId, + }) { + //throw UnimplementedError('getZoomLevel() has not been implemented.'); + } + + // The following are the 11 possible streams of data from the native side + // into the plugin + + @override + Stream onCameraMoveStarted({@required int mapId}) { + return _events(mapId); + } + + @override + Stream onCameraMove({@required int mapId}) { + return _events(mapId); + } + + @override + Stream onCameraIdle({@required int mapId}) { + return _events(mapId); + } + + @override + Stream onMarkerTap({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onInfoWindowTap({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerDragEnd({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onPolylineTap({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onPolygonTap({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onCircleTap({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onTap({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onLongPress({@required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Widget buildView( + Map creationParams, + Set> gestureRecognizers, + PlatformViewCreatedCallback onPlatformViewCreated) { + GoogleMap.MapOptions options; + CameraPosition position; + + CircleUpdates initialCircles = null; + + creationParams.forEach((key, value) { + if(key == 'options') { updateMapOptions(value);} +// else if(key == 'markersToAdd') updateMarkers(value); +// else if(key == 'polygonsToAdd') updatePolygons(value); +// else if(key == 'polylinesToAdd') updatePolylines(value); + + else if(key == 'circlesToAdd') { + initialCircles = _circleFromParams(value); + } else if(key == 'initialCameraPosition') { + position = CameraPosition.fromMap(value); + } else { + print('un-handle >>$key'); + } + }); + + options = GoogleMap.MapOptions() + ..zoom = position.zoom + ..center = GoogleMap.LatLng( + position.target.latitude, + position.target.longitude + ) + ; + + _mapById[_id] = + GoogleMapController.build( + mapId: _id, + streamController: _controller, + onPlatformViewCreated: onPlatformViewCreated, + options: options, + position: position, + initialCircles: initialCircles != null ? initialCircles.circlesToAdd : null, + ) + ; + ///TODO not create redundent view. + return _mapById[_id++].html; + } + + + +} + diff --git a/pubspec.yaml b/pubspec.yaml index 1cfb03186f4d..a0360c618f4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: google_maps_flutter_platform_interface: path: ../google_maps_flutter_platform_interface google_maps: ">=3.0.0 <4.0.0" + stream_transform: ^1.2.0 dev_dependencies: flutter_test: From 188c8ddb077513e98ebf304bbff74ea76cfd1fa2 Mon Sep 17 00:00:00 2001 From: chung2012 Date: Fri, 3 Apr 2020 01:38:55 +0800 Subject: [PATCH 05/58] onCircleTap --- lib/google_maps_flutter_web.dart | 10 +- lib/src/circle.dart | 6 +- lib/src/circles.dart | 46 ++++----- lib/src/google_map_web.dart | 105 ++++++++++++++++---- lib/src/google_maps_flutter_web.dart | 120 ++++++++++++++--------- lib/src/marker.dart | 92 ++++++++++++++++++ lib/src/markers.dart | 140 +++++++++++++++++++++++++++ lib/src/options.dart | 28 ++++++ lib/src/polygon.dart | 87 +++++++++++++++++ lib/src/polygons.dart | 77 +++++++++++++++ lib/src/polyline.dart | 86 ++++++++++++++++ lib/src/polylines.dart | 76 +++++++++++++++ 12 files changed, 775 insertions(+), 98 deletions(-) create mode 100644 lib/src/marker.dart create mode 100644 lib/src/markers.dart create mode 100644 lib/src/options.dart create mode 100644 lib/src/polygon.dart create mode 100644 lib/src/polygons.dart create mode 100644 lib/src/polyline.dart create mode 100644 lib/src/polylines.dart diff --git a/lib/google_maps_flutter_web.dart b/lib/google_maps_flutter_web.dart index 8b1effe0ef82..d7e8caacd76d 100644 --- a/lib/google_maps_flutter_web.dart +++ b/lib/google_maps_flutter_web.dart @@ -5,6 +5,7 @@ import 'dart:html'; import 'dart:ui' as ui; import 'dart:math' show sqrt; import 'dart:collection'; +import 'dart:convert'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -23,4 +24,11 @@ import 'package:google_maps/google_maps.dart' as GoogleMap; part 'src/google_maps_flutter_web.dart'; part 'src/google_map_web.dart'; part 'src/circle.dart'; -part 'src/circles.dart'; \ No newline at end of file +part 'src/circles.dart'; +part 'src/polygon.dart'; +part 'src/polygons.dart'; +part 'src/polyline.dart'; +part 'src/polylines.dart'; +part 'src/marker.dart'; +part 'src/markers.dart'; +part 'src/options.dart'; \ No newline at end of file diff --git a/lib/src/circle.dart b/lib/src/circle.dart index cafcd22039c1..65227dbe94ef 100644 --- a/lib/src/circle.dart +++ b/lib/src/circle.dart @@ -1,13 +1,12 @@ part of google_maps_flutter_web; -GoogleMap.CircleOptions _circleOptionsFromCircle(GoogleMap.GMap googleMap, Circle circle) { +GoogleMap.CircleOptions _circleOptionsFromCircle(Circle circle) { final populationOptions = GoogleMap.CircleOptions() ..strokeColor = '#'+circle.strokeColor.value.toRadixString(16) ..strokeOpacity = 0.8 ..strokeWeight = circle.strokeWidth ..fillColor = '#'+circle.fillColor.value.toRadixString(16) - ..fillOpacity = 0.35 - ..map = googleMap + ..fillOpacity = 0.6 ..center = GoogleMap.LatLng(circle.center.latitude,circle.center.longitude) ..radius = circle.radius ..visible = circle.visible @@ -15,7 +14,6 @@ GoogleMap.CircleOptions _circleOptionsFromCircle(GoogleMap.GMap googleMap, Circl return populationOptions; } - CircleUpdates _circleFromParams(value) { if (value != null) { List> list = value; diff --git a/lib/src/circles.dart b/lib/src/circles.dart index 3880fac55b89..8c789dce58e0 100644 --- a/lib/src/circles.dart +++ b/lib/src/circles.dart @@ -1,21 +1,16 @@ part of google_maps_flutter_web; -///TODO -class CirclesController{ - GoogleMap.GMap googleMap; +class CirclesController extends AbstractController { + final Map _circleIdToController; GoogleMapController googleMapController; - - ///TODO CirclesController({ @required this.googleMapController }) : _circleIdToController = Map(); - - ///TODO void addCircles(Set circlesToAdd) { if(circlesToAdd != null) { circlesToAdd.forEach((circle) { @@ -24,20 +19,16 @@ class CirclesController{ } } - ///TODO - void setGoogleMap(GoogleMap.GMap googleMap) { - this.googleMap = googleMap; - } - /// add [GoogleMap.Circle] to [GoogleMap.GMap]. void addCircle(Circle circle) { if(circle == null) return; - final populationOptions = _circleOptionsFromCircle(googleMap, circle); + final populationOptions = _circleOptionsFromCircle(circle); GoogleMap.Circle gmCircle = GoogleMap.Circle(populationOptions); + gmCircle.map = googleMap; CircleController controller = CircleController( circle: gmCircle, consumeTapEvents:circle.consumeTapEvents, - ontab:(){ googleMapController.onCircleTap(circle.circleId);}); + ontab:(){ onCircleTap(circle.circleId);}); _circleIdToController[circle.circleId.value] = controller; } @@ -52,11 +43,10 @@ class CirclesController{ void changeCircle(Circle circle) { if (circle == null) { return;} - CircleController circleController = _circleIdToController[circle.circleId.value]; if (circleController != null) { circleController.update( - _circleOptionsFromCircle(googleMap, circle)); + _circleOptionsFromCircle(circle)); } } @@ -68,23 +58,21 @@ class CirclesController{ .value]; if(circleController != null) { circleController.remove(); + _circleIdToController.remove(circleId.value); } } }); } - ///String circleId = googleMapsCircleIdToDartCircleId.get(googleCircleId); - // if (circleId == null) { - // return false; - // } - // methodChannel.invokeMethod("circle#onTap", Convert.circleIdToJson(circleId)); - // CircleController circleController = circleIdToController.get(circleId); - // if (circleController != null) { - // return circleController.consumeTapEvents(); - // } - // return false; - bool onCircleTap(String googleCircleId) { - + bool onCircleTap(CircleId circleId) { + googleMapController.onCircleTap(circleId); + final CircleController circleController = _circleIdToController[circleId + .value]; + if(circleController != null) { + return circleController.consumeTapEvents; + } + return false; } -} \ No newline at end of file +} + diff --git a/lib/src/google_map_web.dart b/lib/src/google_map_web.dart index 9ca29dc90e52..8ec225e600f3 100644 --- a/lib/src/google_map_web.dart +++ b/lib/src/google_map_web.dart @@ -4,25 +4,26 @@ part of google_maps_flutter_web; class GoogleMapController { ///TODO final int mapId; - ///TODO HtmlElementView html; - ///TODO GoogleMap.GMap googleMap; ///TODO DivElement div; ///TODO - final GoogleMap.MapOptions options; - - CameraPosition position; + final GoogleMap.MapOptions options; + final StreamController streamController; + CameraPosition position; + CirclesController circlesController; + PolygonsController polygonsController; + PolylinesController polylinesController; + MarkersController markersController; - CirclesController circlesController; - - Set initialCircles; - - final StreamController streamController; + Set initialCircles; + Set initialPolygons; + Set initialPolylines; + Set initialMarkers; ///TODO GoogleMapController.build({ @@ -30,13 +31,20 @@ class GoogleMapController { @required this.streamController, @required this.options, @required this.position, - @required this.initialCircles, onPlatformViewCreated, + @required onPlatformViewCreated, + @required this.initialCircles, + @required this.initialPolygons, + @required this.initialPolylines, + @required this.initialMarkers, }) { circlesController = CirclesController(googleMapController: this); + polygonsController = PolygonsController(googleMapController: this); + polylinesController = PolylinesController(googleMapController: this); + markersController = MarkersController(googleMapController: this); html = HtmlElementView( viewType: 'plugins.flutter.io/google_maps_$mapId' ); - onPlatformViewCreated.call(mapId); +// onPlatformViewCreated.call(mapId); div = DivElement() ..id = 'plugins.flutter.io/google_maps_$mapId' ; @@ -44,24 +52,22 @@ class GoogleMapController { 'plugins.flutter.io/google_maps_$mapId', (int viewId) => div, ); - googleMap = GoogleMap.GMap(div, options); - onMapReady(googleMap); - setInitialCircles(initialCircles); - - } - void updateInitialCircles() { - if(initialCircles == null) return; - circlesController.addCircles(initialCircles); } void onMapReady(GoogleMap.GMap googleMap) { this.googleMap = googleMap; //set googlemap listener circlesController.setGoogleMap(googleMap); + polygonsController.setGoogleMap(googleMap); + polylinesController.setGoogleMap(googleMap); + markersController.setGoogleMap(googleMap); updateInitialCircles(); + updateInitialPolygons(); + updateInitialPolylines(); + updateInitialMarkers(); } void setInitialCircles(Set initialCircles) { @@ -71,8 +77,67 @@ class GoogleMapController { } } + void updateInitialCircles() { + if(initialCircles == null) return; + circlesController.addCircles(initialCircles); + } + + void setInitialPolygons(Set initialPolygons) { + this.initialPolygons = initialPolygons; + if (googleMap != null) { + updateInitialPolygons(); + } + } + + void setInitialPolylines(Set initialPolylines) { + this.initialPolylines = initialPolylines; + if (googleMap != null) { + updateInitialPolylines(); + } + } + + void setInitialMarkers(Set initialMarkers) { + this.initialMarkers = initialMarkers; + if (googleMap != null) { + updateInitialMarkers(); + } + } + + void updateInitialPolygons() { + if(initialPolygons == null) return; + polygonsController.addPolygons(initialPolygons); + } + + void updateInitialPolylines() { + if(initialPolylines == null) return; + polylinesController.addPolylines(initialPolylines); + } + + void updateInitialMarkers() { + if(initialMarkers == null) return; + markersController.addMarkers(initialMarkers); + } + void onCircleTap(CircleId circleId) { streamController.add(CircleTapEvent(mapId, circleId)); } + + void onPolygonTap(PolygonId polygonId) { + streamController.add(PolygonTapEvent(mapId, polygonId)); + } + + void onPolylineTap(PolylineId polylineId) { + streamController.add(PolylineTapEvent(mapId, polylineId)); + } + + void onMarkerTap(MarkerId markerId) { + streamController.add(MarkerTapEvent(mapId, markerId)); + } } +abstract class AbstractController { + GoogleMap.GMap googleMap; + void setGoogleMap(GoogleMap.GMap googleMap) { + this.googleMap = googleMap; + } +} \ No newline at end of file diff --git a/lib/src/google_maps_flutter_web.dart b/lib/src/google_maps_flutter_web.dart index 66fe2c0aebbe..00e5085a1746 100644 --- a/lib/src/google_maps_flutter_web.dart +++ b/lib/src/google_maps_flutter_web.dart @@ -24,6 +24,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @override Future init(int mapId) { + mapId = _id; + print('init $mapId'); +// throw Exception('>>'); } @override @@ -31,7 +34,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map optionsUpdate, { @required int mapId, }) { -// //throw UnimplementedError('updateMapOptions() has not been implemented.'); + print('mapId:$mapId'); +// _mapById[mapId].googleMap.options(options); +// throw UnimplementedError('updateMapOptions() has not been implemented.'); } @override @@ -39,7 +44,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerUpdates markerUpdates, { @required int mapId, }) { -// //throw UnimplementedError('updateMarkers() has not been implemented.'); + _mapById[mapId].markersController + .addMarkers(markerUpdates.markersToAdd); + _mapById[mapId].markersController + .changeMarkers(markerUpdates.markersToChange); + _mapById[mapId].markersController + .removeMarkers(markerUpdates.markerIdsToRemove); } @override @@ -47,7 +57,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { PolygonUpdates polygonUpdates, { @required int mapId, }) { -// //throw UnimplementedError('updatePolygons() has not been implemented.'); + _mapById[mapId].polygonsController + .addPolygons(polygonUpdates.polygonsToAdd); + _mapById[mapId].polygonsController + .changePolygons(polygonUpdates.polygonsToChange); + _mapById[mapId].polygonsController + .removePolygons(polygonUpdates.polygonIdsToRemove); } @override @@ -55,7 +70,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { PolylineUpdates polylineUpdates, { @required int mapId, }) { -// //throw UnimplementedError('updatePolylines() has not been implemented.'); + _mapById[mapId].polylinesController + .addPolylines(polylineUpdates.polylinesToAdd); + _mapById[mapId].polylinesController + .changePolylines(polylineUpdates.polylinesToChange); + _mapById[mapId].polylinesController + .removePolylines(polylineUpdates.polylineIdsToRemove); } @override @@ -63,9 +83,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { CircleUpdates circleUpdates, { @required int mapId, }) { - _mapById[mapId].circlesController.addCircles(circleUpdates.circlesToAdd); - _mapById[mapId].circlesController.changeCircles(circleUpdates.circlesToChange); - _mapById[mapId].circlesController.removeCircles(circleUpdates.circleIdsToRemove); + _mapById[mapId].circlesController + .addCircles(circleUpdates.circlesToAdd); + _mapById[mapId].circlesController + .changeCircles(circleUpdates.circlesToChange); + _mapById[mapId].circlesController + .removeCircles(circleUpdates.circleIdsToRemove); } @override @@ -73,7 +96,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { CameraUpdate cameraUpdate, { @required int mapId, }) { - //throw UnimplementedError('animateCamera() has not been implemented.'); + throw UnimplementedError('animateCamera() has not been implemented.'); } @override @@ -81,7 +104,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { CameraUpdate cameraUpdate, { @required int mapId, }) { - //throw UnimplementedError('moveCamera() has not been implemented.'); + throw UnimplementedError('moveCamera() has not been implemented.'); } @override @@ -89,14 +112,14 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { String mapStyle, { @required int mapId, }) { - //throw UnimplementedError('setMapStyle() has not been implemented.'); + throw UnimplementedError('setMapStyle() has not been implemented.'); } @override Future getVisibleRegion({ @required int mapId, }) { - //throw UnimplementedError('getVisibleRegion() has not been implemented.'); + throw UnimplementedError('getVisibleRegion() has not been implemented.'); } @override @@ -104,7 +127,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { LatLng latLng, { @required int mapId, }) { - //throw UnimplementedError('getScreenCoordinate() has not been implemented.'); + throw UnimplementedError('getScreenCoordinate() has not been implemented.'); } @override @@ -112,7 +135,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { ScreenCoordinate screenCoordinate, { @required int mapId, }) { - //throw UnimplementedError('getLatLng() has not been implemented.'); + throw UnimplementedError('getLatLng() has not been implemented.'); } @override @@ -120,7 +143,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerId markerId, { @required int mapId, }) { - //throw UnimplementedError( 'showMarkerInfoWindow() has not been implemented.'); + throw UnimplementedError( 'showMarkerInfoWindow() has not been implemented.'); } @override @@ -128,7 +151,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerId markerId, { @required int mapId, }) { - //throw UnimplementedError( 'hideMarkerInfoWindow() has not been implemented.'); + throw UnimplementedError( 'hideMarkerInfoWindow() has not been implemented.'); } @override @@ -136,14 +159,14 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerId markerId, { @required int mapId, }) { - //throw UnimplementedError('updateMapOptions() has not been implemented.'); + throw UnimplementedError('updateMapOptions() has not been implemented.'); } @override Future getZoomLevel({ @required int mapId, }) { - //throw UnimplementedError('getZoomLevel() has not been implemented.'); + throw UnimplementedError('getZoomLevel() has not been implemented.'); } // The following are the 11 possible streams of data from the native side @@ -151,17 +174,17 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @override Stream onCameraMoveStarted({@required int mapId}) { - return _events(mapId); + return _events(mapId).whereType(); } @override Stream onCameraMove({@required int mapId}) { - return _events(mapId); + return _events(mapId).whereType(); } @override Stream onCameraIdle({@required int mapId}) { - return _events(mapId); + return _events(mapId).whereType(); } @override @@ -209,49 +232,58 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map creationParams, Set> gestureRecognizers, PlatformViewCreatedCallback onPlatformViewCreated) { - GoogleMap.MapOptions options; + GoogleMap.MapOptions options = GoogleMap.MapOptions(); CameraPosition position; - CircleUpdates initialCircles = null; + CircleUpdates initialCircles = null; + PolygonUpdates initialPolygons = null; + PolylineUpdates initialPolylines = null; + MarkerUpdates initialMarkers = null; creationParams.forEach((key, value) { - if(key == 'options') { updateMapOptions(value);} -// else if(key == 'markersToAdd') updateMarkers(value); -// else if(key == 'polygonsToAdd') updatePolygons(value); -// else if(key == 'polylinesToAdd') updatePolylines(value); - - else if(key == 'circlesToAdd') { + if(key == 'options') { + _optionsFromParams(options, value); + } else if(key == 'markersToAdd') { + initialMarkers = _markerFromParams(value); + } else if(key == 'polygonsToAdd') { + initialPolygons = _polygonFromParams(value); + } else if(key == 'polylinesToAdd') { + initialPolylines = _polylineFromParams(value); + } else if(key == 'circlesToAdd') { initialCircles = _circleFromParams(value); } else if(key == 'initialCameraPosition') { position = CameraPosition.fromMap(value); + options.zoom = position.zoom; + options.center = GoogleMap.LatLng( + position.target.latitude, + position.target.longitude + ); } else { print('un-handle >>$key'); } }); - options = GoogleMap.MapOptions() - ..zoom = position.zoom - ..center = GoogleMap.LatLng( - position.target.latitude, - position.target.longitude - ) - ; - _mapById[_id] = - GoogleMapController.build( + + _mapById[_id] = + GoogleMapController.build( mapId: _id, streamController: _controller, onPlatformViewCreated: onPlatformViewCreated, options: options, position: position, - initialCircles: initialCircles != null ? initialCircles.circlesToAdd : null, - ) - ; - ///TODO not create redundent view. + initialCircles: initialCircles != null + ? initialCircles.circlesToAdd + : null, + initialPolygons: initialPolygons != null ? initialPolygons + .polygonsToAdd : null, + initialPolylines: initialPolylines != null ? initialPolylines + .polylinesToAdd : null, + ) + ; + onPlatformViewCreated.call(_id); + ///TODO not create redundent view. return _mapById[_id++].html; } - - - } diff --git a/lib/src/marker.dart b/lib/src/marker.dart new file mode 100644 index 000000000000..1b2f45958741 --- /dev/null +++ b/lib/src/marker.dart @@ -0,0 +1,92 @@ +part of google_maps_flutter_web; + +MarkerUpdates _markerFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((marker) { + MarkerId markerId = MarkerId(marker['markerId']); + Offset offset = Offset( + (marker['anchor'][0]), + (marker['anchor'][1])); + current.add( + Marker( + markerId: markerId, + alpha: marker['alpha'], + anchor: offset, + consumeTapEvents: marker['consumeTapEvents'], + draggable: marker['draggable'], + flat: marker['flat'], + icon: BitmapDescriptor.defaultMarker, + infoWindow: InfoWindow( + title: marker['infoWindow']['title'] ?? '', + snippet: marker['snippet'], + anchor : offset, + ), + position: LatLng.fromJson(marker['position']), + rotation: marker['rotation'], + visible: marker['visible'], + zIndex: marker['zIndex'], + ) + ); + }); + return MarkerUpdates.from(null, current); + } + return null; +} + +GoogleMap.MarkerOptions _markerOptionsFromMarker(GoogleMap.GMap googleMap, + Marker marker) { + + final goldStar = GoogleMap.GSymbol() + ..path = + 'M 125,5 155,90 245,90 175,145 200,230 125,180 50,230 75,145 5,90 95,90 z' + ..fillColor = 'yellow' + ..fillOpacity = 0.8 + ..scale = 1 + ..strokeColor = 'gold' + ..strokeWeight = 14; + + return GoogleMap.MarkerOptions() + ..position = GoogleMap.LatLng(marker.position.latitude, + marker.position.longitude) + ..title = marker.infoWindow.title + ..icon = goldStar + ///TODO + ; +} + +class MarkerController { + + GoogleMap.Marker _marker; + bool consumeTapEvents = false; + ui.VoidCallback ontab; + + /// + MarkerController({ + @required GoogleMap.Marker marker, + bool consumeTapEvents, + this.ontab + }){ + _marker = marker; + if(consumeTapEvents) { + marker.onClick.listen((event) { + if(ontab !=null) ontab.call(); + }); + } + } + + + set marker (GoogleMap.Marker marker) => {_marker = marker}; + + void update(GoogleMap.MarkerOptions options) { + _marker.options = options; + } + + void remove() { + _marker.visible = false; + _marker.map = null; + _marker = null; + //_marker.remove(); + } +} \ No newline at end of file diff --git a/lib/src/markers.dart b/lib/src/markers.dart new file mode 100644 index 000000000000..c5cab4a4485f --- /dev/null +++ b/lib/src/markers.dart @@ -0,0 +1,140 @@ +part of google_maps_flutter_web; + +class MarkersController extends AbstractController { + + final Map _markerIdToController; + + GoogleMapController googleMapController; + + MarkersController({ + @required this.googleMapController + }): _markerIdToController = Map(); + + void addMarkers(Set markersToAdd) { + if(markersToAdd != null) { + markersToAdd.forEach((marker) { + _addMarker(marker); + }); + } + } + + void _addMarker(Marker marker){ + if(marker == null) return; + final populationOptions = _markerOptionsFromMarker(googleMap, marker); + GoogleMap.Marker gmMarker = GoogleMap.Marker(populationOptions); + gmMarker.map = googleMap; + MarkerController controller = MarkerController( + marker: gmMarker, + consumeTapEvents:marker.consumeTapEvents, + ontab:(){ onMarkerTap(marker.markerId);}); + _markerIdToController[marker.markerId.value] = controller; + } + + void changeMarkers(Set markersToChange) { + if (markersToChange != null) { + markersToChange.forEach((markerToChange) { + changeMarker(markerToChange); + }); + } + } + + void changeMarker(Marker marker) { + if (marker == null) { return;} + MarkerController markerController = _markerIdToController[marker.markerId.value]; + if (markerController != null) { + markerController.update( + _markerOptionsFromMarker(googleMap, marker)); + } + } + + void removeMarkers(Set markerIdsToRemove) { + if (markerIdsToRemove == null) {return;} + markerIdsToRemove.forEach((markerId) { + if(markerId != null) { + final MarkerController markerController = _markerIdToController[markerId + .value]; + if(markerController != null) { + markerController.remove(); + _markerIdToController.remove(markerId.value); + } + } + }); + } + + bool onMarkerTap(MarkerId markerId) { + googleMapController.onMarkerTap(markerId); + final MarkerController markerController = _markerIdToController[markerId + .value]; + if(markerController != null) { + return markerController.consumeTapEvents; + } + return false; + } + + void showMarkerInfoWindow(String markerId, dynamic result) { + throw UnimplementedError('unimplemented.'); + + /** + * MarkerController markerController = markerIdToController.get(markerId); + if (markerController != null) { + markerController.showInfoWindow(); + result.success(null); + } else { + result.error("Invalid markerId", "showInfoWindow called with invalid markerId", null); + } + */ + } + + void isInfoWindowShown(String markerId, dynamic result) { + throw UnimplementedError('unimplemented.'); + /** + * MarkerController markerController = markerIdToController.get(markerId); + if (markerController != null) { + result.success(markerController.isInfoWindowShown()); + } else { + result.error("Invalid markerId", "isInfoWindowShown called with invalid markerId", null); + } + */ + } + + void hideMarkerInfoWindow(String markerId, dynamic result) { + throw UnimplementedError('unimplemented.'); + /** + * MarkerController markerController = markerIdToController.get(markerId); + if (markerController != null) { + markerController.hideInfoWindow(); + result.success(null); + } else { + result.error("Invalid markerId", "hideInfoWindow called with invalid markerId", null); + } + */ + } + + void onInfoWindowTap(String googleMarkerId) { + throw UnimplementedError('unimplemented.'); + /** + * String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId); + if (markerId == null) { + return; + } + methodChannel.invokeMethod("infoWindow#onTap", Convert.markerIdToJson(markerId)); + */ + } + + void onMarkerDragEnd(String googleMarkerId, LatLng latLng) { + throw UnimplementedError('unimplemented.'); + /** + * String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId); + if (markerId == null) { + return; + } + final Map data = new HashMap<>(); + data.put("markerId", markerId); + data.put("position", Convert.latLngToJson(latLng)); + methodChannel.invokeMethod("marker#onDragEnd", data); + */ + } + + +} + diff --git a/lib/src/options.dart b/lib/src/options.dart new file mode 100644 index 000000000000..aa94f641c97b --- /dev/null +++ b/lib/src/options.dart @@ -0,0 +1,28 @@ +part of google_maps_flutter_web; + +void _optionsFromParams(GoogleMap.MapOptions options, + Map optionsUpdate) { + print('>'+ optionsUpdate.toString()); +// compassEnabled: true +// mapToolbarEnabled: true +// cameraTargetBounds: [null] +// mapType: 1 +// ..mapTypeId(optionsUpdate['mapType']) +// minMaxZoomPreference: [null +// null] +// rotateGesturesEnabled: true +// scrollGesturesEnabled: true +// tiltGesturesEnabled: true +// zoomGesturesEnabled: true +// trackCameraPosition: true +// myLocationEnabled: true +// myLocationButtonEnabled: true +// padding: [0 +// 0 +// 0 +// 0] +// indoorEnabled: true +// trafficEnabled: false +// buildingsEnabled: true + ; +} \ No newline at end of file diff --git a/lib/src/polygon.dart b/lib/src/polygon.dart new file mode 100644 index 000000000000..ffe34bd6c26f --- /dev/null +++ b/lib/src/polygon.dart @@ -0,0 +1,87 @@ +part of google_maps_flutter_web; + +PolygonUpdates _polygonFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((polygon) { + PolygonId polygonId = PolygonId(polygon['polygonId']); + List points = []; + List jsonPoints = polygon['points']; + jsonPoints.forEach((p) { + points.add(LatLng.fromJson(p)); + }); + current.add( + Polygon( + polygonId: polygonId, + consumeTapEvents: polygon['consumeTapEvents'], + fillColor: Color(polygon['fillColor']), + geodesic: polygon['geodesic'], + strokeColor: Color(polygon['strokeColor']), + strokeWidth: polygon['strokeWidth'], + visible: polygon['visible'], + zIndex: polygon['zIndex'], + points: points, + ) + ); + }); + return PolygonUpdates.from(null, current); + } + return null; +} + +GoogleMap.PolygonOptions _polygonOptionsFromPolygon(GoogleMap.GMap googleMap, + Polygon polygon) { + List paths = []; + polygon.points.forEach((point) { + paths.add(GoogleMap.LatLng(point.latitude, point.longitude)); + }); + return GoogleMap.PolygonOptions() + ..paths = paths + ..strokeColor = '#'+polygon.strokeColor.value.toRadixString(16) + ..strokeOpacity = 0.8 + ..strokeWeight = polygon.strokeWidth + ..fillColor = '#'+polygon.fillColor.value.toRadixString(16) + ..fillOpacity = 0.35 + ..visible = polygon.visible + ..zIndex = polygon.zIndex + ..geodesic = polygon.geodesic + ; +} + + +/// +class PolygonController { + + GoogleMap.Polygon _polygon; + bool consumeTapEvents = false; + ui.VoidCallback ontab; + + /// + PolygonController({ + @required GoogleMap.Polygon polygon, + bool consumeTapEvents, + this.ontab + }){ + _polygon = polygon; + if(consumeTapEvents) { + polygon.onClick.listen((event) { + if(ontab !=null) ontab.call(); + }); + } + } + + + set polygon (GoogleMap.Polygon polygon) => {_polygon = polygon}; + + void update(GoogleMap.PolygonOptions options) { + _polygon.options = options; + } + + void remove() { + _polygon.visible = false; + _polygon.map = null; + _polygon = null; + //_polygon.remove(); + } +} \ No newline at end of file diff --git a/lib/src/polygons.dart b/lib/src/polygons.dart new file mode 100644 index 000000000000..7fa89bae343f --- /dev/null +++ b/lib/src/polygons.dart @@ -0,0 +1,77 @@ +part of google_maps_flutter_web; + +class PolygonsController extends AbstractController { + + final Map _polygonIdToController; + + GoogleMapController googleMapController; + + PolygonsController({ + @required this.googleMapController + }): _polygonIdToController = Map(); + + void addPolygons(Set polygonsToAdd) { + if(polygonsToAdd != null) { + polygonsToAdd.forEach((polygon) { + _addPolygon(polygon); + }); + } + } + + void _addPolygon(Polygon polygon){ + if(polygon == null) return; + final populationOptions = _polygonOptionsFromPolygon(googleMap, polygon); + GoogleMap.Polygon gmPolygon = GoogleMap.Polygon(populationOptions); + gmPolygon.map = googleMap; + PolygonController controller = PolygonController( + polygon: gmPolygon, + consumeTapEvents:polygon.consumeTapEvents, + ontab:(){ onPolygonTap(polygon.polygonId);}); + _polygonIdToController[polygon.polygonId.value] = controller; + } + + void changePolygons(Set polygonsToChange) { + if (polygonsToChange != null) { + polygonsToChange.forEach((polygonToChange) { + changePolygon(polygonToChange); + }); + } + } + + void changePolygon(Polygon polygon) { + if (polygon == null) { return;} + + PolygonController polygonController = _polygonIdToController[polygon.polygonId.value]; + if (polygonController != null) { + polygonController.update( + _polygonOptionsFromPolygon(googleMap, polygon)); + } + } + + void removePolygons(Set polygonIdsToRemove) { + if (polygonIdsToRemove == null) {return;} + polygonIdsToRemove.forEach((polygonId) { + if(polygonId != null) { + final PolygonController polygonController = _polygonIdToController[polygonId + .value]; + if(polygonController != null) { + polygonController.remove(); + _polygonIdToController.remove(polygonId.value); + } + } + }); + } + + bool onPolygonTap(PolygonId polygonId) { + googleMapController.onPolygonTap(polygonId); + final PolygonController polygonController = _polygonIdToController[polygonId + .value]; + if(polygonController != null) { + return polygonController.consumeTapEvents; + } + return false; + } + + +} + diff --git a/lib/src/polyline.dart b/lib/src/polyline.dart new file mode 100644 index 000000000000..6f80f49242b9 --- /dev/null +++ b/lib/src/polyline.dart @@ -0,0 +1,86 @@ +part of google_maps_flutter_web; + +PolylineUpdates _polylineFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((polyline) { + PolylineId polylineId = PolylineId(polyline['polylineId']); + List points = []; + List jsonPoints = polyline['points']; + jsonPoints.forEach((p) { + points.add(LatLng.fromJson(p)); + }); + current.add( + Polyline( + polylineId: polylineId, + consumeTapEvents: polyline['consumeTapEvents'], + color: Color(polyline['color']), + geodesic: polyline['geodesic'], + visible: polyline['visible'], + zIndex: polyline['zIndex'], + width: polyline['width'], + points: points, +// endCap = Cap.buttCap, +// jointType = JointType.mitered, +// patterns = const [], +// startCap = Cap.buttCap, + ) + ); + }); + return PolylineUpdates.from(null, current); + } + return null; +} + +GoogleMap.PolylineOptions _polylineOptionsFromPolyline(GoogleMap.GMap googleMap, + Polyline polyline) { + List paths = []; + polyline.points.forEach((point) { + paths.add(GoogleMap.LatLng(point.latitude, point.longitude)); + }); + return GoogleMap.PolylineOptions() + ..path = paths + ..strokeOpacity = 1.0 + ..strokeWeight = polyline.width + ..strokeColor = '#'+polyline.color.value.toRadixString(16) + ..visible = polyline.visible + ..zIndex = polyline.zIndex + ..geodesic = polyline.geodesic + ; +} + +class PolylineController { + + GoogleMap.Polyline _polyline; + bool consumeTapEvents = false; + ui.VoidCallback ontab; + + /// + PolylineController({ + @required GoogleMap.Polyline polyline, + bool consumeTapEvents, + this.ontab + }){ + _polyline = polyline; + if(consumeTapEvents) { + polyline.onClick.listen((event) { + if(ontab !=null) ontab.call(); + }); + } + } + + + set polyline (GoogleMap.Polyline polyline) => {_polyline = polyline}; + + void update(GoogleMap.PolylineOptions options) { + _polyline.options = options; + } + + void remove() { + _polyline.visible = false; + _polyline.map = null; + _polyline = null; + //_polyline.remove(); + } +} \ No newline at end of file diff --git a/lib/src/polylines.dart b/lib/src/polylines.dart new file mode 100644 index 000000000000..0d23a1a559f3 --- /dev/null +++ b/lib/src/polylines.dart @@ -0,0 +1,76 @@ +part of google_maps_flutter_web; + +class PolylinesController extends AbstractController { + + final Map _polylineIdToController; + + GoogleMapController googleMapController; + + PolylinesController({ + @required this.googleMapController + }): _polylineIdToController = Map(); + + void addPolylines(Set polylinesToAdd) { + if(polylinesToAdd != null) { + polylinesToAdd.forEach((polyline) { + _addPolyline(polyline); + }); + } + } + + void _addPolyline(Polyline polyline){ + if(polyline == null) return; + final populationOptions = _polylineOptionsFromPolyline(googleMap, polyline); + GoogleMap.Polyline gmPolyline = GoogleMap.Polyline(populationOptions); + gmPolyline.map = googleMap; + PolylineController controller = PolylineController( + polyline: gmPolyline, + consumeTapEvents:polyline.consumeTapEvents, + ontab:(){ onPolylineTap(polyline.polylineId);}); + _polylineIdToController[polyline.polylineId.value] = controller; + } + + void changePolylines(Set polylinesToChange) { + if (polylinesToChange != null) { + polylinesToChange.forEach((polylineToChange) { + changePolyline(polylineToChange); + }); + } + } + + void changePolyline(Polyline polyline) { + if (polyline == null) { return;} + PolylineController polylineController = _polylineIdToController[polyline.polylineId.value]; + if (polylineController != null) { + polylineController.update( + _polylineOptionsFromPolyline(googleMap, polyline)); + } + } + + void removePolylines(Set polylineIdsToRemove) { + if (polylineIdsToRemove == null) {return;} + polylineIdsToRemove.forEach((polylineId) { + if(polylineId != null) { + final PolylineController polylineController = _polylineIdToController[polylineId + .value]; + if(polylineController != null) { + polylineController.remove(); + _polylineIdToController.remove(polylineId.value); + } + } + }); + } + + bool onPolylineTap(PolylineId polylineId) { + googleMapController.onPolylineTap(polylineId); + final PolylineController polylineController = _polylineIdToController[polylineId + .value]; + if(polylineController != null) { + return polylineController.consumeTapEvents; + } + return false; + } + + +} + From 7ec2505f793070794dbdececa1c5358d44bac9e2 Mon Sep 17 00:00:00 2001 From: chung2012 Date: Thu, 16 Apr 2020 00:49:22 +0800 Subject: [PATCH 06/58] Marker events --- lib/src/google_map_web.dart | 10 ++++ lib/src/google_maps_flutter_web.dart | 2 + lib/src/marker.dart | 87 +++++++++++++++++++++------- lib/src/markers.dart | 81 +++++++++----------------- lib/src/polyline.dart | 8 ++- 5 files changed, 113 insertions(+), 75 deletions(-) diff --git a/lib/src/google_map_web.dart b/lib/src/google_map_web.dart index 8ec225e600f3..55ca07badacb 100644 --- a/lib/src/google_map_web.dart +++ b/lib/src/google_map_web.dart @@ -133,6 +133,16 @@ class GoogleMapController { void onMarkerTap(MarkerId markerId) { streamController.add(MarkerTapEvent(mapId, markerId)); } + + void onMarkerDragEnd(MarkerId markerId, GoogleMap.LatLng latLng) { + streamController.add(MarkerDragEndEvent(mapId, + LatLng(latLng.lat, latLng.lng), markerId)); + } + + void onInfoWindowTap(MarkerId markerId) { + streamController.add(InfoWindowTapEvent(mapId, markerId)); + } + } abstract class AbstractController { diff --git a/lib/src/google_maps_flutter_web.dart b/lib/src/google_maps_flutter_web.dart index 00e5085a1746..f6a543288133 100644 --- a/lib/src/google_maps_flutter_web.dart +++ b/lib/src/google_maps_flutter_web.dart @@ -279,6 +279,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { .polygonsToAdd : null, initialPolylines: initialPolylines != null ? initialPolylines .polylinesToAdd : null, + initialMarkers: initialMarkers != null ? initialMarkers + .markersToAdd : null, ) ; onPlatformViewCreated.call(_id); diff --git a/lib/src/marker.dart b/lib/src/marker.dart index 1b2f45958741..944cbd860c6d 100644 --- a/lib/src/marker.dart +++ b/lib/src/marker.dart @@ -35,45 +35,76 @@ MarkerUpdates _markerFromParams(value) { return null; } +GoogleMap.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { + return GoogleMap.InfoWindowOptions() + ..content = marker.infoWindow.snippet + ..zIndex = marker.zIndex + ..position = GoogleMap.LatLng( + marker.position.latitude, + marker.position.longitude) + ; +} + GoogleMap.MarkerOptions _markerOptionsFromMarker(GoogleMap.GMap googleMap, Marker marker) { - final goldStar = GoogleMap.GSymbol() - ..path = - 'M 125,5 155,90 245,90 175,145 200,230 125,180 50,230 75,145 5,90 95,90 z' - ..fillColor = 'yellow' - ..fillOpacity = 0.8 - ..scale = 1 - ..strokeColor = 'gold' - ..strokeWeight = 14; + dynamic iconConfig = marker.icon.toJson(); + dynamic icon; - return GoogleMap.MarkerOptions() - ..position = GoogleMap.LatLng(marker.position.latitude, - marker.position.longitude) - ..title = marker.infoWindow.title - ..icon = goldStar - ///TODO - ; + if(iconConfig[0] == 'defaultMarker') icon = ''; + else if(iconConfig[0] == 'fromAssetImage') { + print('TODO:' + iconConfig); + Image mImage = Image.asset(iconConfig[1] ); +// ui.Image image = mImage.; + } + return GoogleMap.MarkerOptions() + ..position = GoogleMap.LatLng(marker.position.latitude, + marker.position.longitude) + ..title = marker.infoWindow.title + ..zIndex = marker.zIndex + ..visible = marker.visible + ..opacity = marker.alpha + ..draggable = marker.draggable + ..icon = icon// this.icon = BitmapDescriptor.defaultMarker, + ..anchorPoint = GoogleMap.Point(marker.anchor.dx, marker.anchor.dy) + //marker.rotation + //https://stackoverflow.com/questions/6800613/rotating-image-marker-image-on-google-map-v3/28819037 +// this.flat = false, + ; } class MarkerController { GoogleMap.Marker _marker; + GoogleMap.InfoWindow infoWindow; bool consumeTapEvents = false; ui.VoidCallback ontab; - + ui.VoidCallback onInfoWindowTap; + LatLngCallback onDragEnd; + bool infoWindowShown = false; /// MarkerController({ @required GoogleMap.Marker marker, - bool consumeTapEvents, - this.ontab + this.infoWindow, + this.consumeTapEvents, + this.ontab, + this.onDragEnd, + this.onInfoWindowTap, }){ _marker = marker; if(consumeTapEvents) { - marker.onClick.listen((event) { - if(ontab !=null) ontab.call(); + } + if(ontab !=null){ + marker.onClick.listen((event) {ontab.call(); }); + } + if(_marker.draggable) { + marker.onDragend.listen((event) { + if(onDragEnd !=null) onDragEnd.call(event.latLng); }); } + if(onInfoWindowTap !=null) { + infoWindow.addListener('click', onInfoWindowTap); + } } @@ -89,4 +120,20 @@ class MarkerController { _marker = null; //_marker.remove(); } + + void hideInfoWindow() { + if(infoWindow != null) { + infoWindow.close(); + infoWindowShown = false; + } + } + + void showMarkerInfoWindow() { + infoWindow.open(_marker.map); + infoWindowShown = true; + } + + bool isInfoWindowShown() { + return infoWindowShown; + } } \ No newline at end of file diff --git a/lib/src/markers.dart b/lib/src/markers.dart index c5cab4a4485f..22067a0a782d 100644 --- a/lib/src/markers.dart +++ b/lib/src/markers.dart @@ -20,13 +20,20 @@ class MarkersController extends AbstractController { void _addMarker(Marker marker){ if(marker == null) return; + final infoWindoOptions = _infoWindowOPtionsFromMarker(marker); + GoogleMap.InfoWindow gmInfoWindow = GoogleMap.InfoWindow(infoWindoOptions); final populationOptions = _markerOptionsFromMarker(googleMap, marker); GoogleMap.Marker gmMarker = GoogleMap.Marker(populationOptions); gmMarker.map = googleMap; MarkerController controller = MarkerController( marker: gmMarker, + infoWindow : gmInfoWindow, consumeTapEvents:marker.consumeTapEvents, - ontab:(){ onMarkerTap(marker.markerId);}); + ontab:(){onMarkerTap(marker.markerId);}, + onDragEnd :(GoogleMap.LatLng latLng){ + onMarkerDragEnd(marker.markerId, latLng);}, + onInfoWindowTap : (){onInfoWindowTap(marker.markerId);} + ); _markerIdToController[marker.markerId.value] = controller; } @@ -72,69 +79,35 @@ class MarkersController extends AbstractController { } void showMarkerInfoWindow(String markerId, dynamic result) { - throw UnimplementedError('unimplemented.'); - - /** - * MarkerController markerController = markerIdToController.get(markerId); - if (markerController != null) { - markerController.showInfoWindow(); - result.success(null); - } else { - result.error("Invalid markerId", "showInfoWindow called with invalid markerId", null); - } - */ + MarkerController markerController = _markerIdToController[markerId]; + if (markerController != null) { + markerController.showMarkerInfoWindow(); + } } - void isInfoWindowShown(String markerId, dynamic result) { - throw UnimplementedError('unimplemented.'); - /** - * MarkerController markerController = markerIdToController.get(markerId); - if (markerController != null) { - result.success(markerController.isInfoWindowShown()); - } else { - result.error("Invalid markerId", "isInfoWindowShown called with invalid markerId", null); - } - */ + bool isInfoWindowShown(String markerId) { + MarkerController markerController = _markerIdToController[markerId]; + if (markerController != null) { + return markerController.isInfoWindowShown(); + } + return false; } void hideMarkerInfoWindow(String markerId, dynamic result) { - throw UnimplementedError('unimplemented.'); - /** - * MarkerController markerController = markerIdToController.get(markerId); - if (markerController != null) { - markerController.hideInfoWindow(); - result.success(null); - } else { - result.error("Invalid markerId", "hideInfoWindow called with invalid markerId", null); - } - */ + MarkerController markerController = _markerIdToController[markerId]; + if (markerController != null) { + markerController.hideInfoWindow(); + } } - void onInfoWindowTap(String googleMarkerId) { - throw UnimplementedError('unimplemented.'); - /** - * String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId); - if (markerId == null) { - return; - } - methodChannel.invokeMethod("infoWindow#onTap", Convert.markerIdToJson(markerId)); - */ + void onInfoWindowTap(MarkerId markerId) { + googleMapController.onInfoWindowTap(markerId); } - void onMarkerDragEnd(String googleMarkerId, LatLng latLng) { - throw UnimplementedError('unimplemented.'); - /** - * String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId); - if (markerId == null) { - return; - } - final Map data = new HashMap<>(); - data.put("markerId", markerId); - data.put("position", Convert.latLngToJson(latLng)); - methodChannel.invokeMethod("marker#onDragEnd", data); - */ + void onMarkerDragEnd(MarkerId markerId, GoogleMap.LatLng latLng) { + googleMapController.onMarkerDragEnd(markerId, latLng); } - } +typedef LatLngCallback = void Function(GoogleMap.LatLng latLng); \ No newline at end of file diff --git a/lib/src/polyline.dart b/lib/src/polyline.dart index 6f80f49242b9..03d37d5e905c 100644 --- a/lib/src/polyline.dart +++ b/lib/src/polyline.dart @@ -39,15 +39,21 @@ GoogleMap.PolylineOptions _polylineOptionsFromPolyline(GoogleMap.GMap googleMap, polyline.points.forEach((point) { paths.add(GoogleMap.LatLng(point.latitude, point.longitude)); }); + return GoogleMap.PolylineOptions() ..path = paths ..strokeOpacity = 1.0 ..strokeWeight = polyline.width - ..strokeColor = '#'+polyline.color.value.toRadixString(16) + ..strokeColor = '#'+polyline.color.value.toRadixString(16).substring(0,6) ..visible = polyline.visible ..zIndex = polyline.zIndex ..geodesic = polyline.geodesic ; +// this.endCap = Cap.buttCap, +// this.jointType = JointType.mitered, +// this.patterns = const [], +// this.startCap = Cap.buttCap, +// this.width = 10, } class PolylineController { From 266f47381f418b6be9f218d06bd1eb57c08e8142 Mon Sep 17 00:00:00 2001 From: chung2012 Date: Fri, 17 Apr 2020 17:46:25 +0800 Subject: [PATCH 07/58] refactor convert function implement : * tab * long press * moveCamera * getVisibleRegion * getScreenCoordinate * getLatLng * getZoomLevel --- lib/google_maps_flutter_web.dart | 5 +- lib/src/circle.dart | 39 --- lib/src/convert.dart | 265 ++++++++++++++++++ ...p_web.dart => google_maps_controller.dart} | 13 + lib/src/google_maps_flutter_web.dart | 176 +++++++++--- lib/src/marker.dart | 73 ----- lib/src/markers.dart | 4 +- lib/src/options.dart | 28 -- lib/src/polygon.dart | 49 ---- lib/src/polyline.dart | 55 ---- 10 files changed, 424 insertions(+), 283 deletions(-) create mode 100644 lib/src/convert.dart rename lib/src/{google_map_web.dart => google_maps_controller.dart} (92%) delete mode 100644 lib/src/options.dart diff --git a/lib/google_maps_flutter_web.dart b/lib/google_maps_flutter_web.dart index d7e8caacd76d..eabd1e1c346c 100644 --- a/lib/google_maps_flutter_web.dart +++ b/lib/google_maps_flutter_web.dart @@ -6,6 +6,7 @@ import 'dart:ui' as ui; import 'dart:math' show sqrt; import 'dart:collection'; import 'dart:convert'; +import 'dart:typed_data'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -22,7 +23,7 @@ import 'package:google_maps/google_maps.dart' as GoogleMap; //import 'package:js/js.dart'; part 'src/google_maps_flutter_web.dart'; -part 'src/google_map_web.dart'; +part 'src/google_maps_controller.dart'; part 'src/circle.dart'; part 'src/circles.dart'; part 'src/polygon.dart'; @@ -31,4 +32,4 @@ part 'src/polyline.dart'; part 'src/polylines.dart'; part 'src/marker.dart'; part 'src/markers.dart'; -part 'src/options.dart'; \ No newline at end of file +part 'src/convert.dart'; \ No newline at end of file diff --git a/lib/src/circle.dart b/lib/src/circle.dart index 65227dbe94ef..e17906dd345c 100644 --- a/lib/src/circle.dart +++ b/lib/src/circle.dart @@ -1,44 +1,5 @@ part of google_maps_flutter_web; -GoogleMap.CircleOptions _circleOptionsFromCircle(Circle circle) { - final populationOptions = GoogleMap.CircleOptions() - ..strokeColor = '#'+circle.strokeColor.value.toRadixString(16) - ..strokeOpacity = 0.8 - ..strokeWeight = circle.strokeWidth - ..fillColor = '#'+circle.fillColor.value.toRadixString(16) - ..fillOpacity = 0.6 - ..center = GoogleMap.LatLng(circle.center.latitude,circle.center.longitude) - ..radius = circle.radius - ..visible = circle.visible - ; - return populationOptions; -} - -CircleUpdates _circleFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((circle) { - CircleId circleId = CircleId(circle['circleId']); - current.add( - Circle( - circleId: circleId, - consumeTapEvents: circle['consumeTapEvents'], - fillColor: Color(circle['fillColor']), - center: LatLng.fromJson(circle['center']), - radius: circle['radius'], - strokeColor: Color(circle['strokeColor']), - strokeWidth: circle['strokeWidth'], - visible: circle['visible'], - zIndex: circle['zIndex'], - ) - ); - }); - return CircleUpdates.from(null, current); - } - return null; -} - /// class CircleController { diff --git a/lib/src/convert.dart b/lib/src/convert.dart new file mode 100644 index 000000000000..36bfc1cd07e8 --- /dev/null +++ b/lib/src/convert.dart @@ -0,0 +1,265 @@ +part of google_maps_flutter_web; + +void _optionsFromParams(GoogleMap.MapOptions options, + Map optionsUpdate) { + print('>'+ optionsUpdate.toString()); +// compassEnabled: true +// mapToolbarEnabled: true +// cameraTargetBounds: [null] +// mapType: 1 +// ..mapTypeId(optionsUpdate['mapType']) +// minMaxZoomPreference: [null +// null] +// rotateGesturesEnabled: true +// scrollGesturesEnabled: true +// tiltGesturesEnabled: true +// zoomGesturesEnabled: true +// trackCameraPosition: true +// myLocationEnabled: true +// myLocationButtonEnabled: true +// padding: [0 +// 0 +// 0 +// 0] +// indoorEnabled: true +// trafficEnabled: false +// buildingsEnabled: true + ; +} + + +PolylineUpdates _polylineFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((polyline) { + PolylineId polylineId = PolylineId(polyline['polylineId']); + List points = []; + List jsonPoints = polyline['points']; + jsonPoints.forEach((p) { + points.add(LatLng.fromJson(p)); + }); + current.add( + Polyline( + polylineId: polylineId, + consumeTapEvents: polyline['consumeTapEvents'], + color: Color(polyline['color']), + geodesic: polyline['geodesic'], + visible: polyline['visible'], + zIndex: polyline['zIndex'], + width: polyline['width'], + points: points, +// endCap = Cap.buttCap, +// jointType = JointType.mitered, +// patterns = const [], +// startCap = Cap.buttCap, + ) + ); + }); + return PolylineUpdates.from(null, current); + } + return null; +} + +GoogleMap.PolylineOptions _polylineOptionsFromPolyline(GoogleMap.GMap googleMap, + Polyline polyline) { + List paths = []; + polyline.points.forEach((point) { + paths.add(_latlngToGmLatlng(point)); + }); + + return GoogleMap.PolylineOptions() + ..path = paths + ..strokeOpacity = 1.0 + ..strokeWeight = polyline.width + ..strokeColor = '#'+polyline.color.value.toRadixString(16).substring(0,6) + ..visible = polyline.visible + ..zIndex = polyline.zIndex + ..geodesic = polyline.geodesic + ; +// this.endCap = Cap.buttCap, +// this.jointType = JointType.mitered, +// this.patterns = const [], +// this.startCap = Cap.buttCap, +// this.width = 10, +} + + +PolygonUpdates _polygonFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((polygon) { + PolygonId polygonId = PolygonId(polygon['polygonId']); + List points = []; + List jsonPoints = polygon['points']; + jsonPoints.forEach((p) { + points.add(LatLng.fromJson(p)); + }); + current.add( + Polygon( + polygonId: polygonId, + consumeTapEvents: polygon['consumeTapEvents'], + fillColor: Color(polygon['fillColor']), + geodesic: polygon['geodesic'], + strokeColor: Color(polygon['strokeColor']), + strokeWidth: polygon['strokeWidth'], + visible: polygon['visible'], + zIndex: polygon['zIndex'], + points: points, + ) + ); + }); + return PolygonUpdates.from(null, current); + } + return null; +} + +GoogleMap.PolygonOptions _polygonOptionsFromPolygon(GoogleMap.GMap googleMap, + Polygon polygon) { + List paths = []; + polygon.points.forEach((point) { + paths.add(_latlngToGmLatlng(point)); + }); + return GoogleMap.PolygonOptions() + ..paths = paths + ..strokeColor = '#'+polygon.strokeColor.value.toRadixString(16) + ..strokeOpacity = 0.8 + ..strokeWeight = polygon.strokeWidth + ..fillColor = '#'+polygon.fillColor.value.toRadixString(16) + ..fillOpacity = 0.35 + ..visible = polygon.visible + ..zIndex = polygon.zIndex + ..geodesic = polygon.geodesic + ; +} + +GoogleMap.LatLng _latlngToGmLatlng(LatLng latLng){ + return GoogleMap.LatLng(latLng.latitude, latLng.longitude); +} + +LatLng _gmLatlngToLatlng(GoogleMap.LatLng latLng){ + return LatLng(latLng.lat, latLng.lng); +} + +LatLngBounds _gmLatLngBoundsTolatLngBounds(GoogleMap.LatLngBounds latLngBounds){ + return LatLngBounds( + southwest: _gmLatlngToLatlng(latLngBounds.southWest), + northeast: _gmLatlngToLatlng(latLngBounds.northEast), + ); +} + + +MarkerUpdates _markerFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((marker) { + MarkerId markerId = MarkerId(marker['markerId']); + Offset offset = Offset( + (marker['anchor'][0]), + (marker['anchor'][1])); + current.add( + Marker( + markerId: markerId, + alpha: marker['alpha'], + anchor: offset, + consumeTapEvents: marker['consumeTapEvents'], + draggable: marker['draggable'], + flat: marker['flat'], + icon: BitmapDescriptor.defaultMarker, + infoWindow: InfoWindow( + title: marker['infoWindow']['title'] ?? '', + snippet: marker['snippet'], + anchor : offset, + ), + position: LatLng.fromJson(marker['position']), + rotation: marker['rotation'], + visible: marker['visible'], + zIndex: marker['zIndex'], + ) + ); + }); + return MarkerUpdates.from(null, current); + } + return null; +} + +GoogleMap.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { + return GoogleMap.InfoWindowOptions() + ..content = marker.infoWindow.snippet + ..zIndex = marker.zIndex + ..position = GoogleMap.LatLng( + marker.position.latitude, + marker.position.longitude) + ; +} + +GoogleMap.MarkerOptions _markerOptionsFromMarker(GoogleMap.GMap googleMap, + Marker marker) { + + dynamic iconConfig = marker.icon.toJson(); + dynamic icon; + + if(iconConfig[0] == 'defaultMarker') icon = ''; + else if(iconConfig[0] == 'fromAssetImage') { + print('TODO:' + iconConfig); + Image mImage = Image.asset(iconConfig[1] ); +// ui.Image image = mImage.; + } + return GoogleMap.MarkerOptions() + ..position = GoogleMap.LatLng(marker.position.latitude, + marker.position.longitude) + ..title = marker.infoWindow.title + ..zIndex = marker.zIndex + ..visible = marker.visible + ..opacity = marker.alpha + ..draggable = marker.draggable + ..icon = icon// this.icon = BitmapDescriptor.defaultMarker, + ..anchorPoint = GoogleMap.Point(marker.anchor.dx, marker.anchor.dy) + //marker.rotation + //https://stackoverflow.com/questions/6800613/rotating-image-marker-image-on-google-map-v3/28819037 +// this.flat = false, + ; +} + + + +GoogleMap.CircleOptions _circleOptionsFromCircle(Circle circle) { + final populationOptions = GoogleMap.CircleOptions() + ..strokeColor = '#'+circle.strokeColor.value.toRadixString(16) + ..strokeOpacity = 0.8 + ..strokeWeight = circle.strokeWidth + ..fillColor = '#'+circle.fillColor.value.toRadixString(16) + ..fillOpacity = 0.6 + ..center = GoogleMap.LatLng(circle.center.latitude,circle.center.longitude) + ..radius = circle.radius + ..visible = circle.visible + ; + return populationOptions; +} + +CircleUpdates _circleFromParams(value) { + if (value != null) { + List> list = value; + Set current = Set(); + list.forEach((circle) { + CircleId circleId = CircleId(circle['circleId']); + current.add( + Circle( + circleId: circleId, + consumeTapEvents: circle['consumeTapEvents'], + fillColor: Color(circle['fillColor']), + center: LatLng.fromJson(circle['center']), + radius: circle['radius'], + strokeColor: Color(circle['strokeColor']), + strokeWidth: circle['strokeWidth'], + visible: circle['visible'], + zIndex: circle['zIndex'], + ) + ); + }); + return CircleUpdates.from(null, current); + } + return null; +} \ No newline at end of file diff --git a/lib/src/google_map_web.dart b/lib/src/google_maps_controller.dart similarity index 92% rename from lib/src/google_map_web.dart rename to lib/src/google_maps_controller.dart index 55ca07badacb..114455a4447e 100644 --- a/lib/src/google_map_web.dart +++ b/lib/src/google_maps_controller.dart @@ -55,6 +55,19 @@ class GoogleMapController { googleMap = GoogleMap.GMap(div, options); onMapReady(googleMap); + googleMap.onClick.listen((event) { + streamController.add( + MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng))); + }); + + googleMap.onDblclick.listen(_rightClick); + googleMap.onRightclick.listen(_rightClick); + + } + + void _rightClick(event) { + streamController.add( + MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng))); } void onMapReady(GoogleMap.GMap googleMap) { diff --git a/lib/src/google_maps_flutter_web.dart b/lib/src/google_maps_flutter_web.dart index f6a543288133..0f0592a80564 100644 --- a/lib/src/google_maps_flutter_web.dart +++ b/lib/src/google_maps_flutter_web.dart @@ -24,9 +24,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @override Future init(int mapId) { - mapId = _id; - print('init $mapId'); -// throw Exception('>>'); + print('init mapId:$mapId'); } @override @@ -34,7 +32,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map optionsUpdate, { @required int mapId, }) { - print('mapId:$mapId'); + print('updateMapOptions mapId:$mapId' +'\n '+ optionsUpdate.toString()); +// try {throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } + /* + Convert.interpretGoogleMapOptions(call.argument("options"), this); + result.success(Convert.cameraPositionToJson(getCameraPosition())); + */ // _mapById[mapId].googleMap.options(options); // throw UnimplementedError('updateMapOptions() has not been implemented.'); } @@ -96,15 +99,34 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { CameraUpdate cameraUpdate, { @required int mapId, }) { - throw UnimplementedError('animateCamera() has not been implemented.'); + moveCamera(cameraUpdate, mapId: mapId); } + @override Future moveCamera( CameraUpdate cameraUpdate, { @required int mapId, }) { - throw UnimplementedError('moveCamera() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + if(googleMapController != null) { + GoogleMap.GMap map = googleMapController.googleMap; + dynamic json = cameraUpdate.toJson(); + print(json); + if('newLatLng' == json[0] + || 'newLatLngZoom' == json[0]) { + map.panTo(GoogleMap.LatLng(json[1][0],json[1][1])); + } else if('newLatLngBounds' == json[0]) { + map.panToBounds(GoogleMap.LatLngBounds( + GoogleMap.LatLng(json[1][0][0],json[1][0][1]), + GoogleMap.LatLng(json[1][1][0],json[1][1][1]) + )); + } else { + throw UnimplementedError(json[0]+' has not been implemented.'); + } + } +// try { throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } +// throw UnimplementedError('moveCamera() has not been implemented.'); } @override @@ -112,6 +134,26 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { String mapStyle, { @required int mapId, }) { + GoogleMapController googleMapController = _mapById[mapId]; + if(googleMapController != null) { + print(mapStyle); + //https://github.com/cylyl/dart-google-maps/blob/master/example/04-styles/maptype-styled-simple/page.dart +// googleMapController.googleMap.mapTypes.set('custom_style', value); + + /* String mapStyle = (String) call.arguments; + boolean mapStyleSet; + if (mapStyle == null) { + mapStyleSet = googleMap.setMapStyle(null); + } else { + mapStyleSet = googleMap.setMapStyle(new MapStyleOptions(mapStyle)); + } + ArrayList mapStyleResult = new ArrayList<>(2); + mapStyleResult.add(mapStyleSet); + if (!mapStyleSet) { + mapStyleResult.add( + "Unable to set the map style. Please check console logs for errors."); + }*/ + } throw UnimplementedError('setMapStyle() has not been implemented.'); } @@ -119,7 +161,19 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future getVisibleRegion({ @required int mapId, }) { - throw UnimplementedError('getVisibleRegion() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + if(googleMapController != null) { + GoogleMap.LatLngBounds latLngBounds = googleMapController.googleMap + .bounds; + if(latLngBounds != null) { + return Future.value(_gmLatLngBoundsTolatLngBounds(latLngBounds)); + } + } +// try { throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } +// return Future.error( +// StateError("getVisibleRegion called prior to map initialization") +// ); + return Future.value(LatLngBounds(southwest: LatLng(0,0),northeast:LatLng(0,0) )); } @override @@ -127,7 +181,15 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { LatLng latLng, { @required int mapId, }) { - throw UnimplementedError('getScreenCoordinate() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + if (googleMapController != null) { + GoogleMap.Point point = googleMapController.googleMap.projection + .fromLatLngToPoint(_latlngToGmLatlng(latLng)); + return Future.value(ScreenCoordinate(x: point.x, y: point.y)); + } + return Future.error( + StateError("getScreenCoordinate called prior to map initialization") + ); } @override @@ -135,7 +197,16 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { ScreenCoordinate screenCoordinate, { @required int mapId, }) { - throw UnimplementedError('getLatLng() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + if(googleMapController != null) { + GoogleMap.LatLng latLng = googleMapController.googleMap.projection.fromPointToLatLng( + GoogleMap.Point(screenCoordinate.x, screenCoordinate.y) + ); + return Future.value(_gmLatlngToLatlng(latLng)); + } + return Future.error( + StateError("getLatLng called prior to map initialization") + ); } @override @@ -143,7 +214,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerId markerId, { @required int mapId, }) { - throw UnimplementedError( 'showMarkerInfoWindow() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + googleMapController.markersController.showMarkerInfoWindow( + markerId.value.toString() + ); } @override @@ -151,7 +225,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerId markerId, { @required int mapId, }) { - throw UnimplementedError( 'hideMarkerInfoWindow() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + googleMapController.markersController.hideMarkerInfoWindow( + markerId.value.toString() + ); } @override @@ -159,14 +236,45 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerId markerId, { @required int mapId, }) { - throw UnimplementedError('updateMapOptions() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + return Future.value( + googleMapController.markersController.isInfoWindowShown( + markerId.value.toString() + ) + ); } @override Future getZoomLevel({ @required int mapId, }) { - throw UnimplementedError('getZoomLevel() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + return Future.value(googleMapController.googleMap.zoom.toDouble()); + } + + @override + Future takeSnapshot({ + @required int mapId, + }) { + throw UnimplementedError('takeSnapshot() has not been implemented.'); + /**takeSnapshot + * if (googleMap != null) { + final MethodChannel.Result _result = result; + googleMap.snapshot( + new SnapshotReadyCallback() { + @Override + public void onSnapshotReady(Bitmap bitmap) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + byte[] byteArray = stream.toByteArray(); + bitmap.recycle(); + _result.success(byteArray); + } + }); + } else { + result.error("GoogleMap uninitialized", "takeSnapshot", null); + } + */ } // The following are the 11 possible streams of data from the native side @@ -263,29 +371,27 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } }); - - - _mapById[_id] = - GoogleMapController.build( - mapId: _id, - streamController: _controller, - onPlatformViewCreated: onPlatformViewCreated, - options: options, - position: position, - initialCircles: initialCircles != null - ? initialCircles.circlesToAdd - : null, - initialPolygons: initialPolygons != null ? initialPolygons - .polygonsToAdd : null, - initialPolylines: initialPolylines != null ? initialPolylines - .polylinesToAdd : null, - initialMarkers: initialMarkers != null ? initialMarkers - .markersToAdd : null, - ) - ; + _mapById[_id] = + GoogleMapController.build( + mapId: _id, + streamController: _controller, + onPlatformViewCreated: onPlatformViewCreated, + options: options, + position: position, + initialCircles: initialCircles != null + ? initialCircles.circlesToAdd + : null, + initialPolygons: initialPolygons != null ? initialPolygons + .polygonsToAdd : null, + initialPolylines: initialPolylines != null ? initialPolylines + .polylinesToAdd : null, + initialMarkers: initialMarkers != null ? initialMarkers + .markersToAdd : null, + ) + ; +// try {throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } onPlatformViewCreated.call(_id); - ///TODO not create redundent view. + ///TODO not create redundent view. return _mapById[_id++].html; } } - diff --git a/lib/src/marker.dart b/lib/src/marker.dart index 944cbd860c6d..81fd5bac57db 100644 --- a/lib/src/marker.dart +++ b/lib/src/marker.dart @@ -1,78 +1,5 @@ part of google_maps_flutter_web; -MarkerUpdates _markerFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((marker) { - MarkerId markerId = MarkerId(marker['markerId']); - Offset offset = Offset( - (marker['anchor'][0]), - (marker['anchor'][1])); - current.add( - Marker( - markerId: markerId, - alpha: marker['alpha'], - anchor: offset, - consumeTapEvents: marker['consumeTapEvents'], - draggable: marker['draggable'], - flat: marker['flat'], - icon: BitmapDescriptor.defaultMarker, - infoWindow: InfoWindow( - title: marker['infoWindow']['title'] ?? '', - snippet: marker['snippet'], - anchor : offset, - ), - position: LatLng.fromJson(marker['position']), - rotation: marker['rotation'], - visible: marker['visible'], - zIndex: marker['zIndex'], - ) - ); - }); - return MarkerUpdates.from(null, current); - } - return null; -} - -GoogleMap.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { - return GoogleMap.InfoWindowOptions() - ..content = marker.infoWindow.snippet - ..zIndex = marker.zIndex - ..position = GoogleMap.LatLng( - marker.position.latitude, - marker.position.longitude) - ; -} - -GoogleMap.MarkerOptions _markerOptionsFromMarker(GoogleMap.GMap googleMap, - Marker marker) { - - dynamic iconConfig = marker.icon.toJson(); - dynamic icon; - - if(iconConfig[0] == 'defaultMarker') icon = ''; - else if(iconConfig[0] == 'fromAssetImage') { - print('TODO:' + iconConfig); - Image mImage = Image.asset(iconConfig[1] ); -// ui.Image image = mImage.; - } - return GoogleMap.MarkerOptions() - ..position = GoogleMap.LatLng(marker.position.latitude, - marker.position.longitude) - ..title = marker.infoWindow.title - ..zIndex = marker.zIndex - ..visible = marker.visible - ..opacity = marker.alpha - ..draggable = marker.draggable - ..icon = icon// this.icon = BitmapDescriptor.defaultMarker, - ..anchorPoint = GoogleMap.Point(marker.anchor.dx, marker.anchor.dy) - //marker.rotation - //https://stackoverflow.com/questions/6800613/rotating-image-marker-image-on-google-map-v3/28819037 -// this.flat = false, - ; -} - class MarkerController { GoogleMap.Marker _marker; diff --git a/lib/src/markers.dart b/lib/src/markers.dart index 22067a0a782d..ffc4f3c6e3e2 100644 --- a/lib/src/markers.dart +++ b/lib/src/markers.dart @@ -78,7 +78,7 @@ class MarkersController extends AbstractController { return false; } - void showMarkerInfoWindow(String markerId, dynamic result) { + void showMarkerInfoWindow(String markerId) { MarkerController markerController = _markerIdToController[markerId]; if (markerController != null) { markerController.showMarkerInfoWindow(); @@ -93,7 +93,7 @@ class MarkersController extends AbstractController { return false; } - void hideMarkerInfoWindow(String markerId, dynamic result) { + void hideMarkerInfoWindow(String markerId) { MarkerController markerController = _markerIdToController[markerId]; if (markerController != null) { markerController.hideInfoWindow(); diff --git a/lib/src/options.dart b/lib/src/options.dart deleted file mode 100644 index aa94f641c97b..000000000000 --- a/lib/src/options.dart +++ /dev/null @@ -1,28 +0,0 @@ -part of google_maps_flutter_web; - -void _optionsFromParams(GoogleMap.MapOptions options, - Map optionsUpdate) { - print('>'+ optionsUpdate.toString()); -// compassEnabled: true -// mapToolbarEnabled: true -// cameraTargetBounds: [null] -// mapType: 1 -// ..mapTypeId(optionsUpdate['mapType']) -// minMaxZoomPreference: [null -// null] -// rotateGesturesEnabled: true -// scrollGesturesEnabled: true -// tiltGesturesEnabled: true -// zoomGesturesEnabled: true -// trackCameraPosition: true -// myLocationEnabled: true -// myLocationButtonEnabled: true -// padding: [0 -// 0 -// 0 -// 0] -// indoorEnabled: true -// trafficEnabled: false -// buildingsEnabled: true - ; -} \ No newline at end of file diff --git a/lib/src/polygon.dart b/lib/src/polygon.dart index ffe34bd6c26f..713bce43d82e 100644 --- a/lib/src/polygon.dart +++ b/lib/src/polygon.dart @@ -1,54 +1,5 @@ part of google_maps_flutter_web; -PolygonUpdates _polygonFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((polygon) { - PolygonId polygonId = PolygonId(polygon['polygonId']); - List points = []; - List jsonPoints = polygon['points']; - jsonPoints.forEach((p) { - points.add(LatLng.fromJson(p)); - }); - current.add( - Polygon( - polygonId: polygonId, - consumeTapEvents: polygon['consumeTapEvents'], - fillColor: Color(polygon['fillColor']), - geodesic: polygon['geodesic'], - strokeColor: Color(polygon['strokeColor']), - strokeWidth: polygon['strokeWidth'], - visible: polygon['visible'], - zIndex: polygon['zIndex'], - points: points, - ) - ); - }); - return PolygonUpdates.from(null, current); - } - return null; -} - -GoogleMap.PolygonOptions _polygonOptionsFromPolygon(GoogleMap.GMap googleMap, - Polygon polygon) { - List paths = []; - polygon.points.forEach((point) { - paths.add(GoogleMap.LatLng(point.latitude, point.longitude)); - }); - return GoogleMap.PolygonOptions() - ..paths = paths - ..strokeColor = '#'+polygon.strokeColor.value.toRadixString(16) - ..strokeOpacity = 0.8 - ..strokeWeight = polygon.strokeWidth - ..fillColor = '#'+polygon.fillColor.value.toRadixString(16) - ..fillOpacity = 0.35 - ..visible = polygon.visible - ..zIndex = polygon.zIndex - ..geodesic = polygon.geodesic - ; -} - /// class PolygonController { diff --git a/lib/src/polyline.dart b/lib/src/polyline.dart index 03d37d5e905c..5c34afeeb1c1 100644 --- a/lib/src/polyline.dart +++ b/lib/src/polyline.dart @@ -1,60 +1,5 @@ part of google_maps_flutter_web; -PolylineUpdates _polylineFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((polyline) { - PolylineId polylineId = PolylineId(polyline['polylineId']); - List points = []; - List jsonPoints = polyline['points']; - jsonPoints.forEach((p) { - points.add(LatLng.fromJson(p)); - }); - current.add( - Polyline( - polylineId: polylineId, - consumeTapEvents: polyline['consumeTapEvents'], - color: Color(polyline['color']), - geodesic: polyline['geodesic'], - visible: polyline['visible'], - zIndex: polyline['zIndex'], - width: polyline['width'], - points: points, -// endCap = Cap.buttCap, -// jointType = JointType.mitered, -// patterns = const [], -// startCap = Cap.buttCap, - ) - ); - }); - return PolylineUpdates.from(null, current); - } - return null; -} - -GoogleMap.PolylineOptions _polylineOptionsFromPolyline(GoogleMap.GMap googleMap, - Polyline polyline) { - List paths = []; - polyline.points.forEach((point) { - paths.add(GoogleMap.LatLng(point.latitude, point.longitude)); - }); - - return GoogleMap.PolylineOptions() - ..path = paths - ..strokeOpacity = 1.0 - ..strokeWeight = polyline.width - ..strokeColor = '#'+polyline.color.value.toRadixString(16).substring(0,6) - ..visible = polyline.visible - ..zIndex = polyline.zIndex - ..geodesic = polyline.geodesic - ; -// this.endCap = Cap.buttCap, -// this.jointType = JointType.mitered, -// this.patterns = const [], -// this.startCap = Cap.buttCap, -// this.width = 10, -} class PolylineController { From e95425c95320ead07bd093a310da00422a5c6876 Mon Sep 17 00:00:00 2001 From: chung2012 Date: Fri, 17 Apr 2020 22:45:23 +0800 Subject: [PATCH 08/58] updateMapOptions --- lib/src/convert.dart | 85 ++++++++++++++++++++-------- lib/src/google_maps_flutter_web.dart | 26 +++++---- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/lib/src/convert.dart b/lib/src/convert.dart index 36bfc1cd07e8..1afbee1e5498 100644 --- a/lib/src/convert.dart +++ b/lib/src/convert.dart @@ -2,29 +2,68 @@ part of google_maps_flutter_web; void _optionsFromParams(GoogleMap.MapOptions options, Map optionsUpdate) { - print('>'+ optionsUpdate.toString()); -// compassEnabled: true -// mapToolbarEnabled: true -// cameraTargetBounds: [null] -// mapType: 1 -// ..mapTypeId(optionsUpdate['mapType']) -// minMaxZoomPreference: [null -// null] -// rotateGesturesEnabled: true -// scrollGesturesEnabled: true -// tiltGesturesEnabled: true -// zoomGesturesEnabled: true -// trackCameraPosition: true -// myLocationEnabled: true -// myLocationButtonEnabled: true -// padding: [0 -// 0 -// 0 -// 0] -// indoorEnabled: true -// trafficEnabled: false -// buildingsEnabled: true - ; + if(optionsUpdate['mapType'] != null) { + options + ..mapTypeId = GoogleMap.MapTypeId.values[optionsUpdate['mapType']] + ;} + options + ..minZoom = optionsUpdate['minMaxZoomPreference'][0] + ..maxZoom = optionsUpdate['minMaxZoomPreference'][1] + ; +// compassEnabled, +// mapToolbarEnabled, +// cameraTargetBounds, +// mapType, +// minMaxZoomPreference, +// rotateGesturesEnabled, +// scrollGesturesEnabled, +// tiltGesturesEnabled, +// trackCameraPosition, +// zoomGesturesEnabled, +// myLocationEnabled, +// myLocationButtonEnabled, +// padding, +// indoorViewEnabled, +// trafficEnabled, +// buildingsEnabled, + +// backgroundColor(String _backgroundColor) +//center(LatLng _center) +//clickableIcons(bool _clickableIcons) +//disableDefaultUI(bool _disableDefaultUI) +//disableDoubleClickZoom(bool _disableDoubleClickZoom) +//draggable(bool _draggable) +//draggableCursor(String _draggableCursor) +//draggingCursor(String _draggingCursor) +//fullscreenControl(bool _fullscreenControl) +//fullscreenControlOptions( +//gestureHandling(String _gestureHandling) +//heading(num _heading) +//keyboardShortcuts(bool _keyboardShortcuts) +//mapTypeControl(bool _mapTypeControl) +//mapTypeControlOptions(MapTypeControlOptions _mapTypeControlOptions) +//_mapTypeId(dynamic __mapTypeId) +//mapTypeId(dynamic /*MapTypeId|String*/ mapTypeId) +//maxZoom(num _maxZoom) +//minZoom(num _minZoom) +//noClear(bool _noClear) +//overviewMapControl(bool _overviewMapControl) +//overviewMapControlOptions( +//panControl(bool _panControl) +//panControlOptions(PanControlOptions _panControlOptions) +//rotateControl(bool _rotateControl) +//rotateControlOptions(RotateControlOptions _rotateControlOptions) +//scaleControl(bool _scaleControl) +//scaleControlOptions(ScaleControlOptions _scaleControlOptions) +//scrollwheel(bool _scrollwheel) +//streetView(StreetViewPanorama _streetView) +//streetViewControl(bool _streetViewControl) +//streetViewControlOptions( +//styles(List _styles) +//tilt(num _tilt) +//zoom(num _zoom) +//zoomControl(bool _zoomControl) +//zoomControlOptions(ZoomControlOptions _zoomControlOptions) } diff --git a/lib/src/google_maps_flutter_web.dart b/lib/src/google_maps_flutter_web.dart index 0f0592a80564..1a93032db0b2 100644 --- a/lib/src/google_maps_flutter_web.dart +++ b/lib/src/google_maps_flutter_web.dart @@ -23,23 +23,19 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { _controller.stream.where((event) => event.mapId == mapId); @override - Future init(int mapId) { - print('init mapId:$mapId'); - } + Future init(int mapId) { } @override Future updateMapOptions( Map optionsUpdate, { @required int mapId, }) { - print('updateMapOptions mapId:$mapId' +'\n '+ optionsUpdate.toString()); -// try {throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } - /* - Convert.interpretGoogleMapOptions(call.argument("options"), this); - result.success(Convert.cameraPositionToJson(getCameraPosition())); - */ -// _mapById[mapId].googleMap.options(options); -// throw UnimplementedError('updateMapOptions() has not been implemented.'); + GoogleMapController googleMapController = _mapById[mapId]; + if(googleMapController != null) { + _optionsFromParams(googleMapController.options, optionsUpdate); + } else { + throw StateError("updateMapOptions called prior to map initialization"); + } } @override @@ -389,6 +385,14 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { .markersToAdd : null, ) ; + + + /** trafficEnabled + * var trafficLayer = new google.maps.TrafficLayer(); + trafficLayer.setMap(map); + */ + + // try {throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } onPlatformViewCreated.call(_id); ///TODO not create redundent view. From 9ce8771ecd2f853b3354f69ebae1fa2bfcb4568a Mon Sep 17 00:00:00 2001 From: chung2012 Date: Sat, 18 Apr 2020 01:32:56 +0800 Subject: [PATCH 09/58] setMapStyle --- lib/src/convert.dart | 156 +++++++++++++++++++++++++++ lib/src/google_maps_flutter_web.dart | 19 +--- 2 files changed, 157 insertions(+), 18 deletions(-) diff --git a/lib/src/convert.dart b/lib/src/convert.dart index 1afbee1e5498..b6bb801819c5 100644 --- a/lib/src/convert.dart +++ b/lib/src/convert.dart @@ -66,6 +66,162 @@ void _optionsFromParams(GoogleMap.MapOptions options, //zoomControlOptions(ZoomControlOptions _zoomControlOptions) } +List _mapStyles(String mapStyle) { + List styles = []; + if(mapStyle != null) { + List list = json.decode(mapStyle); + list.forEach((style) { + List list2 = style['stylers']; + List stylers = []; + list2.forEach((style) { + stylers.add( + GoogleMap.MapTypeStyler() + ..color = style['color'] + ..gamma = style['gamma'] + ..hue = style['hue'] + ..invertLightness = style['invertLightness'] + ..lightness = style['lightness'] + ..saturation = style['saturation'] + ..visibility = style['visibility'] + ..weight = style['weight'] + ); + }); + + GoogleMap.MapTypeStyleElementType elementType; + if (style['elementType'] == 'geometry') { + elementType = GoogleMap.MapTypeStyleElementType.GEOMETRY; + } else if (style['elementType'] == 'geometry.fill') { + elementType = GoogleMap.MapTypeStyleElementType.GEOMETRY_FILL; + } else if (style['elementType'] == 'geometry.stroke') { + elementType = GoogleMap.MapTypeStyleElementType.GEOMETRY_STROKE; + } else if (style['elementType'] == 'labels') { + elementType = GoogleMap.MapTypeStyleElementType.LABELS; + } else if (style['elementType'] == 'labels.icon') { + elementType = GoogleMap.MapTypeStyleElementType.LABELS_ICON; + } else if (style['elementType'] == 'labels.text') { + elementType = GoogleMap.MapTypeStyleElementType.LABELS_TEXT; + } else if (style['elementType'] == 'labels.text.fill') { + elementType = GoogleMap.MapTypeStyleElementType.LABELS_TEXT_FILL; + } else if (style['elementType'] == 'labels.text.stroke') { + elementType = GoogleMap.MapTypeStyleElementType.LABELS_TEXT_STROKE; + } + + GoogleMap.MapTypeStyleFeatureType featureType; + if (style[featureType] == 'administrative') { + featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE; + } + else if (style[featureType] == 'administrative.country') { + featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY; + } + else if (style[featureType] == 'administrative.land_parcel') { + featureType = + GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL; + } + else if (style[featureType] == 'administrative.locality') { + featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY; + } + else if (style[featureType] == 'administrative.neighborhood') { + featureType = + GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD; + } + else if (style[featureType] == 'administrative.province') { + featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE; + } + else if (style[featureType] == 'all') { + featureType = GoogleMap.MapTypeStyleFeatureType.ALL; + } + else if (style[featureType] == 'landscape') { + featureType = GoogleMap.MapTypeStyleFeatureType.LANDSCAPE; + } + else if (style[featureType] == 'landscape.man_made') { + featureType = GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE; + } + else if (style[featureType] == 'landscape.natural') { + featureType = GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_NATURAL; + } + else if (style[featureType] == 'landscape.natural.landcover') { + featureType = + GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER; + } + else if (style[featureType] == 'landscape.natural.terrain') { + featureType = + GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN; + } + else if (style[featureType] == 'poi') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI; + } + else if (style[featureType] == 'poi.attraction') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_ATTRACTION; + } + else if (style[featureType] == 'poi.business') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_BUSINESS; + } + else if (style[featureType] == 'poi.government') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_GOVERNMENT; + } + else if (style[featureType] == 'poi.medical') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_MEDICAL; + } + else if (style[featureType] == 'poi.park') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_PARK; + } + else if (style[featureType] == 'poi.place_of_worship') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_PLACE_OF_WORSHIP; + } + else if (style[featureType] == 'poi.school') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_SCHOOL; + } + else if (style[featureType] == 'poi.sports_complex') { + featureType = GoogleMap.MapTypeStyleFeatureType.POI_SPORTS_COMPLEX; + } + else if (style[featureType] == 'road') { + featureType = GoogleMap.MapTypeStyleFeatureType.ROAD; + } + else if (style[featureType] == 'road.arterial') { + featureType = GoogleMap.MapTypeStyleFeatureType.ROAD_ARTERIAL; + } + else if (style[featureType] == 'road.highway') { + featureType = GoogleMap.MapTypeStyleFeatureType.ROAD_HIGHWAY; + } + else if (style[featureType] == 'road.highway.controlled_access') { + featureType = + GoogleMap.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS; + } + else if (style[featureType] == 'road.local') { + featureType = GoogleMap.MapTypeStyleFeatureType.ROAD_LOCAL; + } + else if (style[featureType] == 'transit') { + featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT; + } + else if (style[featureType] == 'transit.line') { + featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_LINE; + } + else if (style[featureType] == 'transit.station') { + featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION; + } + else if (style[featureType] == 'transit.station.airport') { + featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT; + } + else if (style[featureType] == 'transit.station.bus') { + featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION_BUS; + } + else if (style[featureType] == 'transit.station.rail') { + featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION_RAIL; + } + else if (style[featureType] == 'water') { + featureType = GoogleMap.MapTypeStyleFeatureType.WATER; + } + + styles.add( + GoogleMap.MapTypeStyle() + ..elementType = elementType + ..featureType = featureType + ..stylers = stylers + ); + }); + } + return styles; +} PolylineUpdates _polylineFromParams(value) { if (value != null) { diff --git a/lib/src/google_maps_flutter_web.dart b/lib/src/google_maps_flutter_web.dart index 1a93032db0b2..5555b673fd3e 100644 --- a/lib/src/google_maps_flutter_web.dart +++ b/lib/src/google_maps_flutter_web.dart @@ -132,25 +132,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { GoogleMapController googleMapController = _mapById[mapId]; if(googleMapController != null) { - print(mapStyle); - //https://github.com/cylyl/dart-google-maps/blob/master/example/04-styles/maptype-styled-simple/page.dart -// googleMapController.googleMap.mapTypes.set('custom_style', value); - - /* String mapStyle = (String) call.arguments; - boolean mapStyleSet; - if (mapStyle == null) { - mapStyleSet = googleMap.setMapStyle(null); - } else { - mapStyleSet = googleMap.setMapStyle(new MapStyleOptions(mapStyle)); - } - ArrayList mapStyleResult = new ArrayList<>(2); - mapStyleResult.add(mapStyleSet); - if (!mapStyleSet) { - mapStyleResult.add( - "Unable to set the map style. Please check console logs for errors."); - }*/ + googleMapController.options.styles = _mapStyles(mapStyle); } - throw UnimplementedError('setMapStyle() has not been implemented.'); } @override From d03b83fe09ea705cc2711e52129114d32efaae55 Mon Sep 17 00:00:00 2001 From: chung2012 Date: Sun, 26 Apr 2020 11:14:44 +0800 Subject: [PATCH 10/58] update pubspec --- pubspec.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index a0360c618f4f..b646ad5038cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,8 +16,7 @@ dependencies: flutter_web_plugins: sdk: flutter meta: ^1.1.7 - google_maps_flutter_platform_interface: - path: ../google_maps_flutter_platform_interface + google_maps_flutter_platform_interface: ^1.0.1 google_maps: ">=3.0.0 <4.0.0" stream_transform: ^1.2.0 From f990aaebdb29a682df4dfce4e8e6bfdb5117ce82 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 30 Apr 2020 13:45:36 -0700 Subject: [PATCH 11/58] Repo surgery --- .../google_maps_flutter_web/lib}/google_maps_flutter_web.dart | 0 .../google_maps_flutter_web/lib}/src/circle.dart | 0 .../google_maps_flutter_web/lib}/src/circles.dart | 0 .../google_maps_flutter_web/lib}/src/convert.dart | 0 .../google_maps_flutter_web/lib}/src/google_maps_controller.dart | 0 .../google_maps_flutter_web/lib}/src/google_maps_flutter_web.dart | 0 .../google_maps_flutter_web/lib}/src/marker.dart | 0 .../google_maps_flutter_web/lib}/src/markers.dart | 0 .../google_maps_flutter_web/lib}/src/polygon.dart | 0 .../google_maps_flutter_web/lib}/src/polygons.dart | 0 .../google_maps_flutter_web/lib}/src/polyline.dart | 0 .../google_maps_flutter_web/lib}/src/polylines.dart | 0 .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/google_maps_flutter_web.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/circle.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/circles.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/convert.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/google_maps_controller.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/google_maps_flutter_web.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/marker.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/markers.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/polygon.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/polygons.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/polyline.dart (100%) rename {lib => packages/google_maps_flutter/google_maps_flutter_web/lib}/src/polylines.dart (100%) rename pubspec.yaml => packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml (100%) diff --git a/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart similarity index 100% rename from lib/google_maps_flutter_web.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart diff --git a/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart similarity index 100% rename from lib/src/circle.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart diff --git a/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart similarity index 100% rename from lib/src/circles.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart diff --git a/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart similarity index 100% rename from lib/src/convert.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart diff --git a/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart similarity index 100% rename from lib/src/google_maps_controller.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart diff --git a/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart similarity index 100% rename from lib/src/google_maps_flutter_web.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart diff --git a/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart similarity index 100% rename from lib/src/marker.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart diff --git a/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart similarity index 100% rename from lib/src/markers.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart diff --git a/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart similarity index 100% rename from lib/src/polygon.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart diff --git a/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart similarity index 100% rename from lib/src/polygons.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart diff --git a/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart similarity index 100% rename from lib/src/polyline.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart diff --git a/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart similarity index 100% rename from lib/src/polylines.dart rename to packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart diff --git a/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml similarity index 100% rename from pubspec.yaml rename to packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml From c16b730bd96391346be0f3f367c717d52e4c4e8f Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 19 Jun 2020 16:25:52 -0700 Subject: [PATCH 12/58] Use creationMapId as a cache key in the buildView method. --- .../lib/src/google_maps_controller.dart | 2 ++ .../lib/src/google_maps_flutter_web.dart | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 114455a4447e..995485c208d9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -48,6 +48,8 @@ class GoogleMapController { div = DivElement() ..id = 'plugins.flutter.io/google_maps_$mapId' ; + // TODO: Move the comment below to analysis-options.yaml + // ignore:undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( 'plugins.flutter.io/google_maps_$mapId', (int viewId) => div, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 5555b673fd3e..7f9b28f67939 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -14,8 +14,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return "1.0"; } - int _id = 0 ; + // This is a cache of rendered maps <-> GoogleMapControllers HashMap _mapById = HashMap(); + final StreamController _controller = StreamController.broadcast(); @@ -23,7 +24,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { _controller.stream.where((event) => event.mapId == mapId); @override - Future init(int mapId) { } + Future init(int mapId) async { + /* Noop */ + } @override Future updateMapOptions( @@ -314,11 +317,28 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + // TODO: Add a new dispose(int mapId) method to clear the cache of Controllers + // that the `buildView` method is creating! + @override Widget buildView( Map creationParams, Set> gestureRecognizers, PlatformViewCreatedCallback onPlatformViewCreated) { + + int mapId = creationParams['creationMapId']; + + if (mapId == null) { + throw PlatformException(code: 'maps_web_missing_creation_map_id', message: 'Pass a `creationMapId` in creationParams to prevent reloads in web.',); + } + + if (_mapById[mapId]?.html != null) { + print('Map ID $mapId already exists, returning cached...'); + return _mapById[mapId].html; + } + + creationParams.remove('creationMapId'); + GoogleMap.MapOptions options = GoogleMap.MapOptions(); CameraPosition position; @@ -350,9 +370,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } }); - _mapById[_id] = + _mapById[mapId] = GoogleMapController.build( - mapId: _id, + mapId: mapId, streamController: _controller, onPlatformViewCreated: onPlatformViewCreated, options: options, @@ -377,8 +397,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { // try {throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } - onPlatformViewCreated.call(_id); - ///TODO not create redundent view. - return _mapById[_id++].html; + onPlatformViewCreated.call(mapId); + + return _mapById[mapId].html; } } From ba3113d218267bf00db4cea07487a8ce489255d5 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 23 Jun 2020 17:22:20 -0700 Subject: [PATCH 13/58] Broadcast Camera Move event. --- .../lib/src/convert.dart | 8 +++++++ .../lib/src/google_maps_controller.dart | 23 +++++++++++-------- .../lib/src/google_maps_flutter_web.dart | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index b6bb801819c5..4d3d5fc9a8b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -344,6 +344,14 @@ LatLngBounds _gmLatLngBoundsTolatLngBounds(GoogleMap.LatLngBounds latLngBounds){ ); } +CameraPosition _gmViewportToCameraPosition(GoogleMap.GMap map) { + return CameraPosition( + target: _gmLatlngToLatlng(map.center), + bearing: map.heading ?? 0, + tilt: map.tilt, + zoom: map.zoom.toDouble(), + ); +} MarkerUpdates _markerFromParams(value) { if (value != null) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 995485c208d9..42d32ff1464f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -56,20 +56,25 @@ class GoogleMapController { ); googleMap = GoogleMap.GMap(div, options); onMapReady(googleMap); + _attachMapEvents(googleMap); + } + + void _onRightClick(event) { + streamController.add( + MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng))); + } - googleMap.onClick.listen((event) { + void _attachMapEvents(GoogleMap.GMap map) { + map.onClick.listen((event) { streamController.add( MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng))); }); - googleMap.onDblclick.listen(_rightClick); - googleMap.onRightclick.listen(_rightClick); - - } - - void _rightClick(event) { - streamController.add( - MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng))); + map.onDblclick.listen(_onRightClick); + map.onRightclick.listen(_onRightClick); + map.onBoundsChanged.listen((event) { + streamController.add(CameraMoveEvent(mapId, _gmViewportToCameraPosition(map))); + }); } void onMapReady(GoogleMap.GMap googleMap) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 7f9b28f67939..790f1d4476a3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -333,7 +333,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } if (_mapById[mapId]?.html != null) { - print('Map ID $mapId already exists, returning cached...'); + // print('Map ID $mapId already exists, returning cached...'); return _mapById[mapId].html; } From 687687cc52fbd904e9bfbc6e5d7b6c7c080570c7 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 23 Jun 2020 17:33:21 -0700 Subject: [PATCH 14/58] Add CameraMoveStartedEvent, CameraIdleEvent. Remove LongPress on double click. --- .../lib/src/google_maps_controller.dart | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 42d32ff1464f..71aec3451fdf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -25,6 +25,8 @@ class GoogleMapController { Set initialPolylines; Set initialMarkers; + bool _mapIsMoving = false; + ///TODO GoogleMapController.build({ @required this.mapId, @@ -59,21 +61,25 @@ class GoogleMapController { _attachMapEvents(googleMap); } - void _onRightClick(event) { - streamController.add( - MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng))); - } - void _attachMapEvents(GoogleMap.GMap map) { map.onClick.listen((event) { streamController.add( - MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng))); + MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng)),); + }); + map.onRightclick.listen((event) { + streamController.add( + MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng)),); }); - - map.onDblclick.listen(_onRightClick); - map.onRightclick.listen(_onRightClick); map.onBoundsChanged.listen((event) { - streamController.add(CameraMoveEvent(mapId, _gmViewportToCameraPosition(map))); + if (!_mapIsMoving) { + _mapIsMoving = true; + streamController.add(CameraMoveStartedEvent(mapId)); + } + streamController.add(CameraMoveEvent(mapId, _gmViewportToCameraPosition(map)),); + }); + map.onIdle.listen((event) { + _mapIsMoving = false; + streamController.add(CameraIdleEvent(mapId)); }); } From dd7be6fb5dc7b347cfaa37e5315185b6de6bb53a Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 24 Jun 2020 18:05:00 -0700 Subject: [PATCH 15/58] Make controllers emit their own events instead of calling maps controller methods. --- .../lib/src/circles.dart | 36 +++++------- .../lib/src/google_maps_controller.dart | 48 ++++------------ .../lib/src/google_maps_flutter_web.dart | 12 +--- .../lib/src/markers.dart | 56 +++++++++---------- .../lib/src/polygons.dart | 34 +++++------ .../lib/src/polylines.dart | 36 +++++------- 6 files changed, 86 insertions(+), 136 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index 8c789dce58e0..f4085db3d282 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -2,14 +2,13 @@ part of google_maps_flutter_web; class CirclesController extends AbstractController { - final Map _circleIdToController; + final Map _circleIdToController; - GoogleMapController googleMapController; + StreamController _streamController; CirclesController({ - @required this.googleMapController - }) - : _circleIdToController = Map(); + @required StreamController stream, + }): _streamController = stream, _circleIdToController = Map(); void addCircles(Set circlesToAdd) { if(circlesToAdd != null) { @@ -28,8 +27,8 @@ class CirclesController extends AbstractController { CircleController controller = CircleController( circle: gmCircle, consumeTapEvents:circle.consumeTapEvents, - ontab:(){ onCircleTap(circle.circleId);}); - _circleIdToController[circle.circleId.value] = controller; + ontab:(){ _onCircleTap(circle.circleId);}); + _circleIdToController[circle.circleId] = controller; } @@ -43,7 +42,7 @@ class CirclesController extends AbstractController { void changeCircle(Circle circle) { if (circle == null) { return;} - CircleController circleController = _circleIdToController[circle.circleId.value]; + CircleController circleController = _circleIdToController[circle.circleId]; if (circleController != null) { circleController.update( _circleOptionsFromCircle(circle)); @@ -54,25 +53,20 @@ class CirclesController extends AbstractController { if (circleIdsToRemove == null) {return;} circleIdsToRemove.forEach((circleId) { if(circleId != null) { - final CircleController circleController = _circleIdToController[circleId - .value]; + final CircleController circleController = _circleIdToController[circleId]; if(circleController != null) { circleController.remove(); - _circleIdToController.remove(circleId.value); + _circleIdToController.remove(circleId); } } }); } - bool onCircleTap(CircleId circleId) { - googleMapController.onCircleTap(circleId); - final CircleController circleController = _circleIdToController[circleId - .value]; - if(circleController != null) { - return circleController.consumeTapEvents; - } - return false; - } + + bool _onCircleTap(CircleId circleId) { + _streamController.add(CircleTapEvent(mapId, circleId)); + // Stop propagation? + return _circleIdToController[circleId]?.consumeTapEvents ?? false; + } } - diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 71aec3451fdf..3956f0a285aa 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -39,10 +39,10 @@ class GoogleMapController { @required this.initialPolylines, @required this.initialMarkers, }) { - circlesController = CirclesController(googleMapController: this); - polygonsController = PolygonsController(googleMapController: this); - polylinesController = PolylinesController(googleMapController: this); - markersController = MarkersController(googleMapController: this); + circlesController = CirclesController(stream: this.streamController); + polygonsController = PolygonsController(stream: this.streamController); + polylinesController = PolylinesController(stream: this.streamController); + markersController = MarkersController(stream: this.streamController); html = HtmlElementView( viewType: 'plugins.flutter.io/google_maps_$mapId' ); @@ -85,11 +85,11 @@ class GoogleMapController { void onMapReady(GoogleMap.GMap googleMap) { this.googleMap = googleMap; - //set googlemap listener - circlesController.setGoogleMap(googleMap); - polygonsController.setGoogleMap(googleMap); - polylinesController.setGoogleMap(googleMap); - markersController.setGoogleMap(googleMap); + // Bind map instance to the other geometry controllers. + circlesController.bindToMap(mapId, googleMap); + polygonsController.bindToMap(mapId, googleMap); + polylinesController.bindToMap(mapId, googleMap); + markersController.bindToMap(mapId, googleMap); updateInitialCircles(); updateInitialPolygons(); updateInitialPolylines(); @@ -143,37 +143,13 @@ class GoogleMapController { if(initialMarkers == null) return; markersController.addMarkers(initialMarkers); } - - void onCircleTap(CircleId circleId) { - streamController.add(CircleTapEvent(mapId, circleId)); - } - - void onPolygonTap(PolygonId polygonId) { - streamController.add(PolygonTapEvent(mapId, polygonId)); - } - - void onPolylineTap(PolylineId polylineId) { - streamController.add(PolylineTapEvent(mapId, polylineId)); - } - - void onMarkerTap(MarkerId markerId) { - streamController.add(MarkerTapEvent(mapId, markerId)); - } - - void onMarkerDragEnd(MarkerId markerId, GoogleMap.LatLng latLng) { - streamController.add(MarkerDragEndEvent(mapId, - LatLng(latLng.lat, latLng.lng), markerId)); - } - - void onInfoWindowTap(MarkerId markerId) { - streamController.add(InfoWindowTapEvent(mapId, markerId)); - } - } abstract class AbstractController { GoogleMap.GMap googleMap; - void setGoogleMap(GoogleMap.GMap googleMap) { + int mapId; + void bindToMap(int mapId, GoogleMap.GMap googleMap) { + this.mapId = mapId; this.googleMap = googleMap; } } \ No newline at end of file diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 790f1d4476a3..116c87158a0d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -197,9 +197,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @required int mapId, }) { GoogleMapController googleMapController = _mapById[mapId]; - googleMapController.markersController.showMarkerInfoWindow( - markerId.value.toString() - ); + googleMapController.markersController.showMarkerInfoWindow(markerId); } @override @@ -208,9 +206,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @required int mapId, }) { GoogleMapController googleMapController = _mapById[mapId]; - googleMapController.markersController.hideMarkerInfoWindow( - markerId.value.toString() - ); + googleMapController.markersController.hideMarkerInfoWindow(markerId); } @override @@ -220,9 +216,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { GoogleMapController googleMapController = _mapById[mapId]; return Future.value( - googleMapController.markersController.isInfoWindowShown( - markerId.value.toString() - ) + googleMapController.markersController.isInfoWindowShown(markerId) ); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index ffc4f3c6e3e2..ff3ec9991b1d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -2,13 +2,13 @@ part of google_maps_flutter_web; class MarkersController extends AbstractController { - final Map _markerIdToController; + final Map _markerIdToController; - GoogleMapController googleMapController; + StreamController _streamController; MarkersController({ - @required this.googleMapController - }): _markerIdToController = Map(); + @required StreamController stream, + }): _streamController = stream, _markerIdToController = Map(); void addMarkers(Set markersToAdd) { if(markersToAdd != null) { @@ -29,12 +29,12 @@ class MarkersController extends AbstractController { marker: gmMarker, infoWindow : gmInfoWindow, consumeTapEvents:marker.consumeTapEvents, - ontab:(){onMarkerTap(marker.markerId);}, - onDragEnd :(GoogleMap.LatLng latLng){ - onMarkerDragEnd(marker.markerId, latLng);}, - onInfoWindowTap : (){onInfoWindowTap(marker.markerId);} + ontab:(){ _onMarkerTap(marker.markerId);}, + onDragEnd : (GoogleMap.LatLng latLng){ + _onMarkerDragEnd(marker.markerId, latLng);}, + onInfoWindowTap : (){ _onInfoWindowTap(marker.markerId);} ); - _markerIdToController[marker.markerId.value] = controller; + _markerIdToController[marker.markerId] = controller; } void changeMarkers(Set markersToChange) { @@ -47,7 +47,7 @@ class MarkersController extends AbstractController { void changeMarker(Marker marker) { if (marker == null) { return;} - MarkerController markerController = _markerIdToController[marker.markerId.value]; + MarkerController markerController = _markerIdToController[marker.markerId]; if (markerController != null) { markerController.update( _markerOptionsFromMarker(googleMap, marker)); @@ -58,8 +58,7 @@ class MarkersController extends AbstractController { if (markerIdsToRemove == null) {return;} markerIdsToRemove.forEach((markerId) { if(markerId != null) { - final MarkerController markerController = _markerIdToController[markerId - .value]; + final MarkerController markerController = _markerIdToController[markerId]; if(markerController != null) { markerController.remove(); _markerIdToController.remove(markerId.value); @@ -68,24 +67,14 @@ class MarkersController extends AbstractController { }); } - bool onMarkerTap(MarkerId markerId) { - googleMapController.onMarkerTap(markerId); - final MarkerController markerController = _markerIdToController[markerId - .value]; - if(markerController != null) { - return markerController.consumeTapEvents; - } - return false; - } - - void showMarkerInfoWindow(String markerId) { + void showMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; if (markerController != null) { markerController.showMarkerInfoWindow(); } } - bool isInfoWindowShown(String markerId) { + bool isInfoWindowShown(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; if (markerController != null) { return markerController.isInfoWindowShown(); @@ -93,19 +82,28 @@ class MarkersController extends AbstractController { return false; } - void hideMarkerInfoWindow(String markerId) { + void hideMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; if (markerController != null) { markerController.hideInfoWindow(); } } - void onInfoWindowTap(MarkerId markerId) { - googleMapController.onInfoWindowTap(markerId); + // Handle internal events + + bool _onMarkerTap(MarkerId markerId) { + _streamController.add(MarkerTapEvent(mapId, markerId)); + // Stop propagation? + return _markerIdToController[markerId]?.consumeTapEvents ?? false; + } + + void _onInfoWindowTap(MarkerId markerId) { + _streamController.add(InfoWindowTapEvent(mapId, markerId)); } - void onMarkerDragEnd(MarkerId markerId, GoogleMap.LatLng latLng) { - googleMapController.onMarkerDragEnd(markerId, latLng); + void _onMarkerDragEnd(MarkerId markerId, GoogleMap.LatLng latLng) { + _streamController.add(MarkerDragEndEvent(mapId, + _gmLatlngToLatlng(latLng), markerId,)); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index 7fa89bae343f..56f7c42c6ded 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -2,13 +2,13 @@ part of google_maps_flutter_web; class PolygonsController extends AbstractController { - final Map _polygonIdToController; + final Map _polygonIdToController; - GoogleMapController googleMapController; + StreamController _streamController; PolygonsController({ - @required this.googleMapController - }): _polygonIdToController = Map(); + @required StreamController stream, + }): _streamController = stream, _polygonIdToController = Map(); void addPolygons(Set polygonsToAdd) { if(polygonsToAdd != null) { @@ -26,8 +26,8 @@ class PolygonsController extends AbstractController { PolygonController controller = PolygonController( polygon: gmPolygon, consumeTapEvents:polygon.consumeTapEvents, - ontab:(){ onPolygonTap(polygon.polygonId);}); - _polygonIdToController[polygon.polygonId.value] = controller; + ontab:(){ _onPolygonTap(polygon.polygonId);}); + _polygonIdToController[polygon.polygonId] = controller; } void changePolygons(Set polygonsToChange) { @@ -41,7 +41,7 @@ class PolygonsController extends AbstractController { void changePolygon(Polygon polygon) { if (polygon == null) { return;} - PolygonController polygonController = _polygonIdToController[polygon.polygonId.value]; + PolygonController polygonController = _polygonIdToController[polygon.polygonId]; if (polygonController != null) { polygonController.update( _polygonOptionsFromPolygon(googleMap, polygon)); @@ -52,8 +52,7 @@ class PolygonsController extends AbstractController { if (polygonIdsToRemove == null) {return;} polygonIdsToRemove.forEach((polygonId) { if(polygonId != null) { - final PolygonController polygonController = _polygonIdToController[polygonId - .value]; + final PolygonController polygonController = _polygonIdToController[polygonId]; if(polygonController != null) { polygonController.remove(); _polygonIdToController.remove(polygonId.value); @@ -62,16 +61,11 @@ class PolygonsController extends AbstractController { }); } - bool onPolygonTap(PolygonId polygonId) { - googleMapController.onPolygonTap(polygonId); - final PolygonController polygonController = _polygonIdToController[polygonId - .value]; - if(polygonController != null) { - return polygonController.consumeTapEvents; - } - return false; - } - + // Handle internal events + bool _onPolygonTap(PolygonId polygonId) { + _streamController.add(PolygonTapEvent(mapId, polygonId)); + // Stop propagation? + return _polygonIdToController[polygonId]?.consumeTapEvents ?? false; + } } - diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index 0d23a1a559f3..2f8e08c115ba 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -2,13 +2,13 @@ part of google_maps_flutter_web; class PolylinesController extends AbstractController { - final Map _polylineIdToController; + final Map _polylineIdToController; - GoogleMapController googleMapController; + StreamController _streamController; PolylinesController({ - @required this.googleMapController - }): _polylineIdToController = Map(); + @required StreamController stream, + }): _streamController = stream, _polylineIdToController = Map(); void addPolylines(Set polylinesToAdd) { if(polylinesToAdd != null) { @@ -26,8 +26,8 @@ class PolylinesController extends AbstractController { PolylineController controller = PolylineController( polyline: gmPolyline, consumeTapEvents:polyline.consumeTapEvents, - ontab:(){ onPolylineTap(polyline.polylineId);}); - _polylineIdToController[polyline.polylineId.value] = controller; + ontab:(){ _onPolylineTap(polyline.polylineId);}); + _polylineIdToController[polyline.polylineId] = controller; } void changePolylines(Set polylinesToChange) { @@ -40,7 +40,7 @@ class PolylinesController extends AbstractController { void changePolyline(Polyline polyline) { if (polyline == null) { return;} - PolylineController polylineController = _polylineIdToController[polyline.polylineId.value]; + PolylineController polylineController = _polylineIdToController[polyline.polylineId]; if (polylineController != null) { polylineController.update( _polylineOptionsFromPolyline(googleMap, polyline)); @@ -51,26 +51,20 @@ class PolylinesController extends AbstractController { if (polylineIdsToRemove == null) {return;} polylineIdsToRemove.forEach((polylineId) { if(polylineId != null) { - final PolylineController polylineController = _polylineIdToController[polylineId - .value]; + final PolylineController polylineController = _polylineIdToController[polylineId]; if(polylineController != null) { polylineController.remove(); - _polylineIdToController.remove(polylineId.value); + _polylineIdToController.remove(polylineId); } } }); } - bool onPolylineTap(PolylineId polylineId) { - googleMapController.onPolylineTap(polylineId); - final PolylineController polylineController = _polylineIdToController[polylineId - .value]; - if(polylineController != null) { - return polylineController.consumeTapEvents; - } - return false; - } - + // Handle internal events + bool _onPolylineTap(PolylineId polylineId) { + _streamController.add(PolylineTapEvent(mapId, polylineId)); + // Stop propagation? + return _polylineIdToController[polylineId]?.consumeTapEvents ?? false; + } } - From c52a3014cdcd6def3979214cb1d651a6ff5ca0a7 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 24 Jun 2020 18:08:40 -0700 Subject: [PATCH 16/58] ontab -> onTap --- .../google_maps_flutter_web/lib/src/circle.dart | 6 +++--- .../google_maps_flutter_web/lib/src/circles.dart | 2 +- .../google_maps_flutter_web/lib/src/marker.dart | 8 ++++---- .../google_maps_flutter_web/lib/src/markers.dart | 2 +- .../google_maps_flutter_web/lib/src/polygon.dart | 6 +++--- .../google_maps_flutter_web/lib/src/polygons.dart | 2 +- .../google_maps_flutter_web/lib/src/polyline.dart | 6 +++--- .../google_maps_flutter_web/lib/src/polylines.dart | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index e17906dd345c..ad45a3e4ac2d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -7,14 +7,14 @@ class CircleController { bool consumeTapEvents = false; - ui.VoidCallback ontab; + ui.VoidCallback onTap; /// - CircleController({@required GoogleMap.Circle circle, this.consumeTapEvents, this.ontab}){ + CircleController({@required GoogleMap.Circle circle, this.consumeTapEvents, this.onTap}){ _circle = circle; if(consumeTapEvents) { circle.onClick.listen((event) { - if(ontab !=null) ontab.call(); + if(onTap !=null) onTap.call(); }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index f4085db3d282..23e115f7a6bb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -27,7 +27,7 @@ class CirclesController extends AbstractController { CircleController controller = CircleController( circle: gmCircle, consumeTapEvents:circle.consumeTapEvents, - ontab:(){ _onCircleTap(circle.circleId);}); + onTap:(){ _onCircleTap(circle.circleId);}); _circleIdToController[circle.circleId] = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 81fd5bac57db..f6cefcf1c631 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -5,7 +5,7 @@ class MarkerController { GoogleMap.Marker _marker; GoogleMap.InfoWindow infoWindow; bool consumeTapEvents = false; - ui.VoidCallback ontab; + ui.VoidCallback onTap; ui.VoidCallback onInfoWindowTap; LatLngCallback onDragEnd; bool infoWindowShown = false; @@ -14,15 +14,15 @@ class MarkerController { @required GoogleMap.Marker marker, this.infoWindow, this.consumeTapEvents, - this.ontab, + this.onTap, this.onDragEnd, this.onInfoWindowTap, }){ _marker = marker; if(consumeTapEvents) { } - if(ontab !=null){ - marker.onClick.listen((event) {ontab.call(); }); + if(onTap !=null){ + marker.onClick.listen((event) {onTap.call(); }); } if(_marker.draggable) { marker.onDragend.listen((event) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index ff3ec9991b1d..897f83b07c3c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -29,7 +29,7 @@ class MarkersController extends AbstractController { marker: gmMarker, infoWindow : gmInfoWindow, consumeTapEvents:marker.consumeTapEvents, - ontab:(){ _onMarkerTap(marker.markerId);}, + onTap:(){ _onMarkerTap(marker.markerId);}, onDragEnd : (GoogleMap.LatLng latLng){ _onMarkerDragEnd(marker.markerId, latLng);}, onInfoWindowTap : (){ _onInfoWindowTap(marker.markerId);} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index 713bce43d82e..5e848b7c5c8d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -6,18 +6,18 @@ class PolygonController { GoogleMap.Polygon _polygon; bool consumeTapEvents = false; - ui.VoidCallback ontab; + ui.VoidCallback onTap; /// PolygonController({ @required GoogleMap.Polygon polygon, bool consumeTapEvents, - this.ontab + this.onTap }){ _polygon = polygon; if(consumeTapEvents) { polygon.onClick.listen((event) { - if(ontab !=null) ontab.call(); + if(onTap !=null) onTap.call(); }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index 56f7c42c6ded..0312f9ed27ea 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -26,7 +26,7 @@ class PolygonsController extends AbstractController { PolygonController controller = PolygonController( polygon: gmPolygon, consumeTapEvents:polygon.consumeTapEvents, - ontab:(){ _onPolygonTap(polygon.polygonId);}); + onTap:(){ _onPolygonTap(polygon.polygonId);}); _polygonIdToController[polygon.polygonId] = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index 5c34afeeb1c1..8bd3fe47251e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -5,18 +5,18 @@ class PolylineController { GoogleMap.Polyline _polyline; bool consumeTapEvents = false; - ui.VoidCallback ontab; + ui.VoidCallback onTap; /// PolylineController({ @required GoogleMap.Polyline polyline, bool consumeTapEvents, - this.ontab + this.onTap }){ _polyline = polyline; if(consumeTapEvents) { polyline.onClick.listen((event) { - if(ontab !=null) ontab.call(); + if(onTap !=null) onTap.call(); }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index 2f8e08c115ba..dace424842c8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -26,7 +26,7 @@ class PolylinesController extends AbstractController { PolylineController controller = PolylineController( polyline: gmPolyline, consumeTapEvents:polyline.consumeTapEvents, - ontab:(){ _onPolylineTap(polyline.polylineId);}); + onTap:(){ _onPolylineTap(polyline.polylineId);}); _polylineIdToController[polyline.polylineId] = controller; } From b78baface3011e9c81eb759f3fc8437116a30a01 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 24 Jun 2020 18:41:40 -0700 Subject: [PATCH 17/58] Support custom marker images from Assets. --- .../lib/src/convert.dart | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 4d3d5fc9a8b8..49e785252ce9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -401,29 +401,24 @@ GoogleMap.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { GoogleMap.MarkerOptions _markerOptionsFromMarker(GoogleMap.GMap googleMap, Marker marker) { - dynamic iconConfig = marker.icon.toJson(); - dynamic icon; + final iconConfig = marker.icon.toJson(); + GoogleMap.Icon icon; - if(iconConfig[0] == 'defaultMarker') icon = ''; - else if(iconConfig[0] == 'fromAssetImage') { - print('TODO:' + iconConfig); - Image mImage = Image.asset(iconConfig[1] ); -// ui.Image image = mImage.; + if(iconConfig[0] == 'fromAssetImage') { + icon = GoogleMap.Icon() + ..url = iconConfig[1]; } return GoogleMap.MarkerOptions() ..position = GoogleMap.LatLng(marker.position.latitude, - marker.position.longitude) + marker.position.longitude,) ..title = marker.infoWindow.title ..zIndex = marker.zIndex ..visible = marker.visible ..opacity = marker.alpha ..draggable = marker.draggable - ..icon = icon// this.icon = BitmapDescriptor.defaultMarker, - ..anchorPoint = GoogleMap.Point(marker.anchor.dx, marker.anchor.dy) - //marker.rotation - //https://stackoverflow.com/questions/6800613/rotating-image-marker-image-on-google-map-v3/28819037 -// this.flat = false, - ; + ..icon = icon + ..anchorPoint = GoogleMap.Point(marker.anchor.dx, marker.anchor.dy,); + // Flat and Rotation are not supported directly on the web. } From 74f9b5a463c6fce213078e1d39a3c15142559ae7 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 25 Jun 2020 19:45:27 -0700 Subject: [PATCH 18/58] Implement moveCamera --- .../lib/src/google_maps_flutter_web.dart | 71 ++++++++++++++----- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 116c87158a0d..1a02217a3cd5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -101,31 +101,68 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { moveCamera(cameraUpdate, mapId: mapId); } - @override Future moveCamera( CameraUpdate cameraUpdate, { @required int mapId, }) { + GoogleMapController googleMapController = _mapById[mapId]; - if(googleMapController != null) { - GoogleMap.GMap map = googleMapController.googleMap; - dynamic json = cameraUpdate.toJson(); - print(json); - if('newLatLng' == json[0] - || 'newLatLngZoom' == json[0]) { - map.panTo(GoogleMap.LatLng(json[1][0],json[1][1])); - } else if('newLatLngBounds' == json[0]) { - map.panToBounds(GoogleMap.LatLngBounds( - GoogleMap.LatLng(json[1][0][0],json[1][0][1]), - GoogleMap.LatLng(json[1][1][0],json[1][1][1]) + if (googleMapController == null) { + return null; + } + + GoogleMap.GMap map = googleMapController.googleMap; + // TODO: Subclass CameraUpdate so the below code is not stringly-typed. + dynamic json = cameraUpdate.toJson(); + + switch (json[0]) { + case 'newCameraPosition': + map.heading = json[1]['bearing']; + map.zoom = json[1]['zoom']; + map.panTo(GoogleMap.LatLng(json[1]['target'][0], json[1]['target'][1])); + map.tilt = json[1]['tilt']; + break; + case 'newLatLng': + map.panTo(GoogleMap.LatLng(json[1][0], json[1][1])); + break; + case 'newLatLngZoom': + map.zoom = json[2]; + map.panTo(GoogleMap.LatLng(json[1][0], json[1][1])); + break; + case 'newLatLngBounds': + map.fitBounds(GoogleMap.LatLngBounds( + GoogleMap.LatLng(json[1][0][0],json[1][0][1]), + GoogleMap.LatLng(json[1][1][0],json[1][1][1]) )); - } else { - throw UnimplementedError(json[0]+' has not been implemented.'); - } + // padding = json[2]; + // Needs package:google_maps ^4.0.0 to adjust the padding in fitBounds + break; + case 'scrollBy': + map.panBy(json[1], json[2]); + break; + case 'zoomBy': + double zoomDelta = json[1] ?? 0; + // Web only supports integer changes... + int newZoomDelta = zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); + map.zoom = map.zoom + newZoomDelta; + if (json.length == 3) { + // With focus + map.panTo(GoogleMap.LatLng(json[2][0], json[2][1])); + } + break; + case 'zoomIn': + map.zoom++; + break; + case 'zoomOut': + map.zoom--; + break; + case 'zoomTo': + map.zoom = json[1]; + break; + default: + throw UnimplementedError('moveCamera() does not implement: ${json[0]}.'); } -// try { throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } -// throw UnimplementedError('moveCamera() has not been implemented.'); } @override From eadc5cd4d55612859ee71e75bc85893eafec77e4 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 8 Jul 2020 14:54:38 -0700 Subject: [PATCH 19/58] Fix some analyzer issues. --- .../lib/google_maps_flutter_web.dart | 3 +- .../lib/src/circle.dart | 8 +- .../lib/src/circles.dart | 4 +- .../lib/src/convert.dart | 146 +++++++++--------- .../lib/src/google_maps_controller.dart | 14 +- .../lib/src/google_maps_flutter_web.dart | 30 ++-- .../lib/src/marker.dart | 10 +- .../lib/src/markers.dart | 10 +- .../lib/src/polygon.dart | 8 +- .../lib/src/polygons.dart | 2 +- .../lib/src/polyline.dart | 8 +- .../lib/src/polylines.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- 13 files changed, 123 insertions(+), 124 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index eabd1e1c346c..7fa183c994f8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -3,7 +3,6 @@ library google_maps_flutter_web; import 'dart:async'; import 'dart:html'; import 'dart:ui' as ui; -import 'dart:math' show sqrt; import 'dart:collection'; import 'dart:convert'; import 'dart:typed_data'; @@ -19,7 +18,7 @@ import 'package:stream_transform/stream_transform.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:google_maps/google_maps.dart' as GoogleMap; +import 'package:google_maps/google_maps.dart' as gmaps; //import 'package:js/js.dart'; part 'src/google_maps_flutter_web.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index ad45a3e4ac2d..59d57cc908a8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -3,14 +3,14 @@ part of google_maps_flutter_web; /// class CircleController { - GoogleMap.Circle _circle; + gmaps.Circle _circle; bool consumeTapEvents = false; ui.VoidCallback onTap; /// - CircleController({@required GoogleMap.Circle circle, this.consumeTapEvents, this.onTap}){ + CircleController({@required gmaps.Circle circle, this.consumeTapEvents, this.onTap}){ _circle = circle; if(consumeTapEvents) { circle.onClick.listen((event) { @@ -19,10 +19,10 @@ class CircleController { } } - set circle (GoogleMap.Circle circle) => {_circle = circle}; + set circle (gmaps.Circle circle) { _circle = circle; } ///TODO - void update(GoogleMap.CircleOptions options) { + void update(gmaps.CircleOptions options) { _circle.options = options; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index 23e115f7a6bb..32229825e415 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -18,11 +18,11 @@ class CirclesController extends AbstractController { } } - /// add [GoogleMap.Circle] to [GoogleMap.GMap]. + /// add [gmaps.Circle] to [gmaps.GMap]. void addCircle(Circle circle) { if(circle == null) return; final populationOptions = _circleOptionsFromCircle(circle); - GoogleMap.Circle gmCircle = GoogleMap.Circle(populationOptions); + gmaps.Circle gmCircle = gmaps.Circle(populationOptions); gmCircle.map = googleMap; CircleController controller = CircleController( circle: gmCircle, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 49e785252ce9..e96a4f94f3e2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -1,10 +1,10 @@ part of google_maps_flutter_web; -void _optionsFromParams(GoogleMap.MapOptions options, +void _optionsFromParams(gmaps.MapOptions options, Map optionsUpdate) { if(optionsUpdate['mapType'] != null) { options - ..mapTypeId = GoogleMap.MapTypeId.values[optionsUpdate['mapType']] + ..mapTypeId = gmaps.MapTypeId.values[optionsUpdate['mapType']] ;} options ..minZoom = optionsUpdate['minMaxZoomPreference'][0] @@ -66,16 +66,16 @@ void _optionsFromParams(GoogleMap.MapOptions options, //zoomControlOptions(ZoomControlOptions _zoomControlOptions) } -List _mapStyles(String mapStyle) { - List styles = []; +List _mapStyles(String mapStyle) { + List styles = []; if(mapStyle != null) { List list = json.decode(mapStyle); list.forEach((style) { List list2 = style['stylers']; - List stylers = []; + List stylers = []; list2.forEach((style) { stylers.add( - GoogleMap.MapTypeStyler() + gmaps.MapTypeStyler() ..color = style['color'] ..gamma = style['gamma'] ..hue = style['hue'] @@ -87,133 +87,133 @@ List _mapStyles(String mapStyle) { ); }); - GoogleMap.MapTypeStyleElementType elementType; + gmaps.MapTypeStyleElementType elementType; if (style['elementType'] == 'geometry') { - elementType = GoogleMap.MapTypeStyleElementType.GEOMETRY; + elementType = gmaps.MapTypeStyleElementType.GEOMETRY; } else if (style['elementType'] == 'geometry.fill') { - elementType = GoogleMap.MapTypeStyleElementType.GEOMETRY_FILL; + elementType = gmaps.MapTypeStyleElementType.GEOMETRY_FILL; } else if (style['elementType'] == 'geometry.stroke') { - elementType = GoogleMap.MapTypeStyleElementType.GEOMETRY_STROKE; + elementType = gmaps.MapTypeStyleElementType.GEOMETRY_STROKE; } else if (style['elementType'] == 'labels') { - elementType = GoogleMap.MapTypeStyleElementType.LABELS; + elementType = gmaps.MapTypeStyleElementType.LABELS; } else if (style['elementType'] == 'labels.icon') { - elementType = GoogleMap.MapTypeStyleElementType.LABELS_ICON; + elementType = gmaps.MapTypeStyleElementType.LABELS_ICON; } else if (style['elementType'] == 'labels.text') { - elementType = GoogleMap.MapTypeStyleElementType.LABELS_TEXT; + elementType = gmaps.MapTypeStyleElementType.LABELS_TEXT; } else if (style['elementType'] == 'labels.text.fill') { - elementType = GoogleMap.MapTypeStyleElementType.LABELS_TEXT_FILL; + elementType = gmaps.MapTypeStyleElementType.LABELS_TEXT_FILL; } else if (style['elementType'] == 'labels.text.stroke') { - elementType = GoogleMap.MapTypeStyleElementType.LABELS_TEXT_STROKE; + elementType = gmaps.MapTypeStyleElementType.LABELS_TEXT_STROKE; } - GoogleMap.MapTypeStyleFeatureType featureType; + gmaps.MapTypeStyleFeatureType featureType; if (style[featureType] == 'administrative') { - featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE; + featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE; } else if (style[featureType] == 'administrative.country') { - featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY; + featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY; } else if (style[featureType] == 'administrative.land_parcel') { featureType = - GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL; + gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL; } else if (style[featureType] == 'administrative.locality') { - featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY; + featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY; } else if (style[featureType] == 'administrative.neighborhood') { featureType = - GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD; + gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD; } else if (style[featureType] == 'administrative.province') { - featureType = GoogleMap.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE; + featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE; } else if (style[featureType] == 'all') { - featureType = GoogleMap.MapTypeStyleFeatureType.ALL; + featureType = gmaps.MapTypeStyleFeatureType.ALL; } else if (style[featureType] == 'landscape') { - featureType = GoogleMap.MapTypeStyleFeatureType.LANDSCAPE; + featureType = gmaps.MapTypeStyleFeatureType.LANDSCAPE; } else if (style[featureType] == 'landscape.man_made') { - featureType = GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE; + featureType = gmaps.MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE; } else if (style[featureType] == 'landscape.natural') { - featureType = GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_NATURAL; + featureType = gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL; } else if (style[featureType] == 'landscape.natural.landcover') { featureType = - GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER; + gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER; } else if (style[featureType] == 'landscape.natural.terrain') { featureType = - GoogleMap.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN; + gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN; } else if (style[featureType] == 'poi') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI; + featureType = gmaps.MapTypeStyleFeatureType.POI; } else if (style[featureType] == 'poi.attraction') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_ATTRACTION; + featureType = gmaps.MapTypeStyleFeatureType.POI_ATTRACTION; } else if (style[featureType] == 'poi.business') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_BUSINESS; + featureType = gmaps.MapTypeStyleFeatureType.POI_BUSINESS; } else if (style[featureType] == 'poi.government') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_GOVERNMENT; + featureType = gmaps.MapTypeStyleFeatureType.POI_GOVERNMENT; } else if (style[featureType] == 'poi.medical') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_MEDICAL; + featureType = gmaps.MapTypeStyleFeatureType.POI_MEDICAL; } else if (style[featureType] == 'poi.park') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_PARK; + featureType = gmaps.MapTypeStyleFeatureType.POI_PARK; } else if (style[featureType] == 'poi.place_of_worship') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_PLACE_OF_WORSHIP; + featureType = gmaps.MapTypeStyleFeatureType.POI_PLACE_OF_WORSHIP; } else if (style[featureType] == 'poi.school') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_SCHOOL; + featureType = gmaps.MapTypeStyleFeatureType.POI_SCHOOL; } else if (style[featureType] == 'poi.sports_complex') { - featureType = GoogleMap.MapTypeStyleFeatureType.POI_SPORTS_COMPLEX; + featureType = gmaps.MapTypeStyleFeatureType.POI_SPORTS_COMPLEX; } else if (style[featureType] == 'road') { - featureType = GoogleMap.MapTypeStyleFeatureType.ROAD; + featureType = gmaps.MapTypeStyleFeatureType.ROAD; } else if (style[featureType] == 'road.arterial') { - featureType = GoogleMap.MapTypeStyleFeatureType.ROAD_ARTERIAL; + featureType = gmaps.MapTypeStyleFeatureType.ROAD_ARTERIAL; } else if (style[featureType] == 'road.highway') { - featureType = GoogleMap.MapTypeStyleFeatureType.ROAD_HIGHWAY; + featureType = gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY; } else if (style[featureType] == 'road.highway.controlled_access') { featureType = - GoogleMap.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS; + gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS; } else if (style[featureType] == 'road.local') { - featureType = GoogleMap.MapTypeStyleFeatureType.ROAD_LOCAL; + featureType = gmaps.MapTypeStyleFeatureType.ROAD_LOCAL; } else if (style[featureType] == 'transit') { - featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT; + featureType = gmaps.MapTypeStyleFeatureType.TRANSIT; } else if (style[featureType] == 'transit.line') { - featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_LINE; + featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_LINE; } else if (style[featureType] == 'transit.station') { - featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION; + featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION; } else if (style[featureType] == 'transit.station.airport') { - featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT; + featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT; } else if (style[featureType] == 'transit.station.bus') { - featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION_BUS; + featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_BUS; } else if (style[featureType] == 'transit.station.rail') { - featureType = GoogleMap.MapTypeStyleFeatureType.TRANSIT_STATION_RAIL; + featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_RAIL; } else if (style[featureType] == 'water') { - featureType = GoogleMap.MapTypeStyleFeatureType.WATER; + featureType = gmaps.MapTypeStyleFeatureType.WATER; } styles.add( - GoogleMap.MapTypeStyle() + gmaps.MapTypeStyle() ..elementType = elementType ..featureType = featureType ..stylers = stylers @@ -256,14 +256,14 @@ PolylineUpdates _polylineFromParams(value) { return null; } -GoogleMap.PolylineOptions _polylineOptionsFromPolyline(GoogleMap.GMap googleMap, +gmaps.PolylineOptions _polylineOptionsFromPolyline(gmaps.GMap googleMap, Polyline polyline) { - List paths = []; + List paths = []; polyline.points.forEach((point) { paths.add(_latlngToGmLatlng(point)); }); - return GoogleMap.PolylineOptions() + return gmaps.PolylineOptions() ..path = paths ..strokeOpacity = 1.0 ..strokeWeight = polyline.width @@ -310,13 +310,13 @@ PolygonUpdates _polygonFromParams(value) { return null; } -GoogleMap.PolygonOptions _polygonOptionsFromPolygon(GoogleMap.GMap googleMap, +gmaps.PolygonOptions _polygonOptionsFromPolygon(gmaps.GMap googleMap, Polygon polygon) { - List paths = []; + List paths = []; polygon.points.forEach((point) { paths.add(_latlngToGmLatlng(point)); }); - return GoogleMap.PolygonOptions() + return gmaps.PolygonOptions() ..paths = paths ..strokeColor = '#'+polygon.strokeColor.value.toRadixString(16) ..strokeOpacity = 0.8 @@ -329,22 +329,22 @@ GoogleMap.PolygonOptions _polygonOptionsFromPolygon(GoogleMap.GMap googleMap, ; } -GoogleMap.LatLng _latlngToGmLatlng(LatLng latLng){ - return GoogleMap.LatLng(latLng.latitude, latLng.longitude); +gmaps.LatLng _latlngToGmLatlng(LatLng latLng){ + return gmaps.LatLng(latLng.latitude, latLng.longitude); } -LatLng _gmLatlngToLatlng(GoogleMap.LatLng latLng){ +LatLng _gmLatlngToLatlng(gmaps.LatLng latLng){ return LatLng(latLng.lat, latLng.lng); } -LatLngBounds _gmLatLngBoundsTolatLngBounds(GoogleMap.LatLngBounds latLngBounds){ +LatLngBounds _gmLatLngBoundsTolatLngBounds(gmaps.LatLngBounds latLngBounds){ return LatLngBounds( southwest: _gmLatlngToLatlng(latLngBounds.southWest), northeast: _gmLatlngToLatlng(latLngBounds.northEast), ); } -CameraPosition _gmViewportToCameraPosition(GoogleMap.GMap map) { +CameraPosition _gmViewportToCameraPosition(gmaps.GMap map) { return CameraPosition( target: _gmLatlngToLatlng(map.center), bearing: map.heading ?? 0, @@ -388,28 +388,28 @@ MarkerUpdates _markerFromParams(value) { return null; } -GoogleMap.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { - return GoogleMap.InfoWindowOptions() +gmaps.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { + return gmaps.InfoWindowOptions() ..content = marker.infoWindow.snippet ..zIndex = marker.zIndex - ..position = GoogleMap.LatLng( + ..position = gmaps.LatLng( marker.position.latitude, marker.position.longitude) ; } -GoogleMap.MarkerOptions _markerOptionsFromMarker(GoogleMap.GMap googleMap, +gmaps.MarkerOptions _markerOptionsFromMarker(gmaps.GMap googleMap, Marker marker) { final iconConfig = marker.icon.toJson(); - GoogleMap.Icon icon; + gmaps.Icon icon; if(iconConfig[0] == 'fromAssetImage') { - icon = GoogleMap.Icon() + icon = gmaps.Icon() ..url = iconConfig[1]; } - return GoogleMap.MarkerOptions() - ..position = GoogleMap.LatLng(marker.position.latitude, + return gmaps.MarkerOptions() + ..position = gmaps.LatLng(marker.position.latitude, marker.position.longitude,) ..title = marker.infoWindow.title ..zIndex = marker.zIndex @@ -417,20 +417,20 @@ GoogleMap.MarkerOptions _markerOptionsFromMarker(GoogleMap.GMap googleMap, ..opacity = marker.alpha ..draggable = marker.draggable ..icon = icon - ..anchorPoint = GoogleMap.Point(marker.anchor.dx, marker.anchor.dy,); + ..anchorPoint = gmaps.Point(marker.anchor.dx, marker.anchor.dy,); // Flat and Rotation are not supported directly on the web. } -GoogleMap.CircleOptions _circleOptionsFromCircle(Circle circle) { - final populationOptions = GoogleMap.CircleOptions() +gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { + final populationOptions = gmaps.CircleOptions() ..strokeColor = '#'+circle.strokeColor.value.toRadixString(16) ..strokeOpacity = 0.8 ..strokeWeight = circle.strokeWidth ..fillColor = '#'+circle.fillColor.value.toRadixString(16) ..fillOpacity = 0.6 - ..center = GoogleMap.LatLng(circle.center.latitude,circle.center.longitude) + ..center = gmaps.LatLng(circle.center.latitude,circle.center.longitude) ..radius = circle.radius ..visible = circle.visible ; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 3956f0a285aa..e7553ffe4fe9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -7,11 +7,11 @@ class GoogleMapController { ///TODO HtmlElementView html; ///TODO - GoogleMap.GMap googleMap; + gmaps.GMap googleMap; ///TODO DivElement div; ///TODO - final GoogleMap.MapOptions options; + final gmaps.MapOptions options; final StreamController streamController; CameraPosition position; CirclesController circlesController; @@ -56,12 +56,12 @@ class GoogleMapController { 'plugins.flutter.io/google_maps_$mapId', (int viewId) => div, ); - googleMap = GoogleMap.GMap(div, options); + googleMap = gmaps.GMap(div, options); onMapReady(googleMap); _attachMapEvents(googleMap); } - void _attachMapEvents(GoogleMap.GMap map) { + void _attachMapEvents(gmaps.GMap map) { map.onClick.listen((event) { streamController.add( MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng)),); @@ -83,7 +83,7 @@ class GoogleMapController { }); } - void onMapReady(GoogleMap.GMap googleMap) { + void onMapReady(gmaps.GMap googleMap) { this.googleMap = googleMap; // Bind map instance to the other geometry controllers. circlesController.bindToMap(mapId, googleMap); @@ -146,9 +146,9 @@ class GoogleMapController { } abstract class AbstractController { - GoogleMap.GMap googleMap; + gmaps.GMap googleMap; int mapId; - void bindToMap(int mapId, GoogleMap.GMap googleMap) { + void bindToMap(int mapId, gmaps.GMap googleMap) { this.mapId = mapId; this.googleMap = googleMap; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 1a02217a3cd5..41279517ceff 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -112,7 +112,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return null; } - GoogleMap.GMap map = googleMapController.googleMap; + gmaps.GMap map = googleMapController.googleMap; // TODO: Subclass CameraUpdate so the below code is not stringly-typed. dynamic json = cameraUpdate.toJson(); @@ -120,20 +120,20 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { case 'newCameraPosition': map.heading = json[1]['bearing']; map.zoom = json[1]['zoom']; - map.panTo(GoogleMap.LatLng(json[1]['target'][0], json[1]['target'][1])); + map.panTo(gmaps.LatLng(json[1]['target'][0], json[1]['target'][1])); map.tilt = json[1]['tilt']; break; case 'newLatLng': - map.panTo(GoogleMap.LatLng(json[1][0], json[1][1])); + map.panTo(gmaps.LatLng(json[1][0], json[1][1])); break; case 'newLatLngZoom': map.zoom = json[2]; - map.panTo(GoogleMap.LatLng(json[1][0], json[1][1])); + map.panTo(gmaps.LatLng(json[1][0], json[1][1])); break; case 'newLatLngBounds': - map.fitBounds(GoogleMap.LatLngBounds( - GoogleMap.LatLng(json[1][0][0],json[1][0][1]), - GoogleMap.LatLng(json[1][1][0],json[1][1][1]) + map.fitBounds(gmaps.LatLngBounds( + gmaps.LatLng(json[1][0][0],json[1][0][1]), + gmaps.LatLng(json[1][1][0],json[1][1][1]) )); // padding = json[2]; // Needs package:google_maps ^4.0.0 to adjust the padding in fitBounds @@ -148,7 +148,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { map.zoom = map.zoom + newZoomDelta; if (json.length == 3) { // With focus - map.panTo(GoogleMap.LatLng(json[2][0], json[2][1])); + map.panTo(gmaps.LatLng(json[2][0], json[2][1])); } break; case 'zoomIn': @@ -182,7 +182,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { GoogleMapController googleMapController = _mapById[mapId]; if(googleMapController != null) { - GoogleMap.LatLngBounds latLngBounds = googleMapController.googleMap + gmaps.LatLngBounds latLngBounds = googleMapController.googleMap .bounds; if(latLngBounds != null) { return Future.value(_gmLatLngBoundsTolatLngBounds(latLngBounds)); @@ -202,7 +202,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { GoogleMapController googleMapController = _mapById[mapId]; if (googleMapController != null) { - GoogleMap.Point point = googleMapController.googleMap.projection + gmaps.Point point = googleMapController.googleMap.projection .fromLatLngToPoint(_latlngToGmLatlng(latLng)); return Future.value(ScreenCoordinate(x: point.x, y: point.y)); } @@ -218,8 +218,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { GoogleMapController googleMapController = _mapById[mapId]; if(googleMapController != null) { - GoogleMap.LatLng latLng = googleMapController.googleMap.projection.fromPointToLatLng( - GoogleMap.Point(screenCoordinate.x, screenCoordinate.y) + gmaps.LatLng latLng = googleMapController.googleMap.projection.fromPointToLatLng( + gmaps.Point(screenCoordinate.x, screenCoordinate.y) ); return Future.value(_gmLatlngToLatlng(latLng)); } @@ -273,7 +273,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { /**takeSnapshot * if (googleMap != null) { final MethodChannel.Result _result = result; - googleMap.snapshot( + gmaps.snapshot( new SnapshotReadyCallback() { @Override public void onSnapshotReady(Bitmap bitmap) { @@ -370,7 +370,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { creationParams.remove('creationMapId'); - GoogleMap.MapOptions options = GoogleMap.MapOptions(); + gmaps.MapOptions options = gmaps.MapOptions(); CameraPosition position; CircleUpdates initialCircles = null; @@ -392,7 +392,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } else if(key == 'initialCameraPosition') { position = CameraPosition.fromMap(value); options.zoom = position.zoom; - options.center = GoogleMap.LatLng( + options.center = gmaps.LatLng( position.target.latitude, position.target.longitude ); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index f6cefcf1c631..9e11e6dacdcd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -2,8 +2,8 @@ part of google_maps_flutter_web; class MarkerController { - GoogleMap.Marker _marker; - GoogleMap.InfoWindow infoWindow; + gmaps.Marker _marker; + gmaps.InfoWindow infoWindow; bool consumeTapEvents = false; ui.VoidCallback onTap; ui.VoidCallback onInfoWindowTap; @@ -11,7 +11,7 @@ class MarkerController { bool infoWindowShown = false; /// MarkerController({ - @required GoogleMap.Marker marker, + @required gmaps.Marker marker, this.infoWindow, this.consumeTapEvents, this.onTap, @@ -35,9 +35,9 @@ class MarkerController { } - set marker (GoogleMap.Marker marker) => {_marker = marker}; + set marker (gmaps.Marker marker) { _marker = marker; } - void update(GoogleMap.MarkerOptions options) { + void update(gmaps.MarkerOptions options) { _marker.options = options; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 897f83b07c3c..0eafd775d357 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -21,16 +21,16 @@ class MarkersController extends AbstractController { void _addMarker(Marker marker){ if(marker == null) return; final infoWindoOptions = _infoWindowOPtionsFromMarker(marker); - GoogleMap.InfoWindow gmInfoWindow = GoogleMap.InfoWindow(infoWindoOptions); + gmaps.InfoWindow gmInfoWindow = gmaps.InfoWindow(infoWindoOptions); final populationOptions = _markerOptionsFromMarker(googleMap, marker); - GoogleMap.Marker gmMarker = GoogleMap.Marker(populationOptions); + gmaps.Marker gmMarker = gmaps.Marker(populationOptions); gmMarker.map = googleMap; MarkerController controller = MarkerController( marker: gmMarker, infoWindow : gmInfoWindow, consumeTapEvents:marker.consumeTapEvents, onTap:(){ _onMarkerTap(marker.markerId);}, - onDragEnd : (GoogleMap.LatLng latLng){ + onDragEnd : (gmaps.LatLng latLng){ _onMarkerDragEnd(marker.markerId, latLng);}, onInfoWindowTap : (){ _onInfoWindowTap(marker.markerId);} ); @@ -101,11 +101,11 @@ class MarkersController extends AbstractController { _streamController.add(InfoWindowTapEvent(mapId, markerId)); } - void _onMarkerDragEnd(MarkerId markerId, GoogleMap.LatLng latLng) { + void _onMarkerDragEnd(MarkerId markerId, gmaps.LatLng latLng) { _streamController.add(MarkerDragEndEvent(mapId, _gmLatlngToLatlng(latLng), markerId,)); } } -typedef LatLngCallback = void Function(GoogleMap.LatLng latLng); \ No newline at end of file +typedef LatLngCallback = void Function(gmaps.LatLng latLng); \ No newline at end of file diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index 5e848b7c5c8d..88d369053281 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -4,13 +4,13 @@ part of google_maps_flutter_web; /// class PolygonController { - GoogleMap.Polygon _polygon; + gmaps.Polygon _polygon; bool consumeTapEvents = false; ui.VoidCallback onTap; /// PolygonController({ - @required GoogleMap.Polygon polygon, + @required gmaps.Polygon polygon, bool consumeTapEvents, this.onTap }){ @@ -23,9 +23,9 @@ class PolygonController { } - set polygon (GoogleMap.Polygon polygon) => {_polygon = polygon}; + set polygon (gmaps.Polygon polygon) { _polygon = polygon; } - void update(GoogleMap.PolygonOptions options) { + void update(gmaps.PolygonOptions options) { _polygon.options = options; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index 0312f9ed27ea..d0067524387b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -21,7 +21,7 @@ class PolygonsController extends AbstractController { void _addPolygon(Polygon polygon){ if(polygon == null) return; final populationOptions = _polygonOptionsFromPolygon(googleMap, polygon); - GoogleMap.Polygon gmPolygon = GoogleMap.Polygon(populationOptions); + gmaps.Polygon gmPolygon = gmaps.Polygon(populationOptions); gmPolygon.map = googleMap; PolygonController controller = PolygonController( polygon: gmPolygon, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index 8bd3fe47251e..653d4a1bdb35 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -3,13 +3,13 @@ part of google_maps_flutter_web; class PolylineController { - GoogleMap.Polyline _polyline; + gmaps.Polyline _polyline; bool consumeTapEvents = false; ui.VoidCallback onTap; /// PolylineController({ - @required GoogleMap.Polyline polyline, + @required gmaps.Polyline polyline, bool consumeTapEvents, this.onTap }){ @@ -22,9 +22,9 @@ class PolylineController { } - set polyline (GoogleMap.Polyline polyline) => {_polyline = polyline}; + set polyline (gmaps.Polyline polyline) {_polyline = polyline; } - void update(GoogleMap.PolylineOptions options) { + void update(gmaps.PolylineOptions options) { _polyline.options = options; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index dace424842c8..70af17af78b3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -21,7 +21,7 @@ class PolylinesController extends AbstractController { void _addPolyline(Polyline polyline){ if(polyline == null) return; final populationOptions = _polylineOptionsFromPolyline(googleMap, polyline); - GoogleMap.Polyline gmPolyline = GoogleMap.Polyline(populationOptions); + gmaps.Polyline gmPolyline = gmaps.Polyline(populationOptions); gmPolyline.map = googleMap; PolylineController controller = PolylineController( polyline: gmPolyline, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index b646ad5038cb..b986c7583f88 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -1,7 +1,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter -version: 0.0.1 +version: 0.1.0 flutter: plugin: From 4c57a93eff5b0ee6066555b0a527dfc0665b5299 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 16 Jul 2020 12:52:27 -0700 Subject: [PATCH 20/58] Refactor options to always use the map setter. * Does not mutate underlying options object anymore (maps SDK was not aware of changes there) * Keeps an internal cache of current options for every mapId. * Implement all initialization options possible with the current SDK + gmaps lib. * Document initialization gaps. * Clean up the interface of the maps controller, to not expose more than what we need (ongoing). * Prepare for a dispose(mapId) method. * Prepare for Traffic Layer, but leave it disabled. Re-rendering issue prevents it from toggling properly. --- .../lib/src/convert.dart | 129 +++++++++--------- .../lib/src/google_maps_controller.dart | 51 +++++-- .../lib/src/google_maps_flutter_web.dart | 93 +++++++------ .../google_maps_flutter_web/pubspec.yaml | 2 +- 4 files changed, 160 insertions(+), 115 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index e96a4f94f3e2..bda224ad617d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -1,69 +1,72 @@ part of google_maps_flutter_web; -void _optionsFromParams(gmaps.MapOptions options, - Map optionsUpdate) { - if(optionsUpdate['mapType'] != null) { +// Indices in the plugin side don't match with the ones +// in the gmaps lib. This translates from plugin -> gmaps. +final _mapTypeToMapTypeId = { + 0: gmaps.MapTypeId.ROADMAP, // "none" in the plugin + 1: gmaps.MapTypeId.ROADMAP, + 2: gmaps.MapTypeId.SATELLITE, + 3: gmaps.MapTypeId.TERRAIN, + 4: gmaps.MapTypeId.HYBRID, +}; + +/// Converts options from the plugin into gmaps.MapOptions that can be used by the JS SDK. +// The following options are not handled here, for various reasons: +// The following are not available in web, because the map doesn't rotate there: +// compassEnabled +// rotateGesturesEnabled +// tiltGesturesEnabled +// mapToolbarEnabled is unused in web, there's no "map toolbar" +// myLocationButtonEnabled Widget not available in web yet, it needs to be built on top of the maps widget +// See: https://developers.google.com/maps/documentation/javascript/examples/control-custom +// myLocationEnabled needs to be built through dart:html navigator.geolocation +// See: https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html +// trafficEnabled is handled when creating the GMap object, since it needs to be added as a layer. +// trackCameraPosition is just a boolan value that indicates if the map has an onCameraMove handler. +// indoorViewEnabled seems to not have an equivalent in web +// buildingsEnabled seems to not have an equivalent in web +// padding seems to behave differently in web than mobile. You can't move UI elements in web. +gmaps.MapOptions _optionsFromParams(Map optionsUpdate, { + gmaps.MapOptions existingOptions, + }) { + + gmaps.MapOptions options = existingOptions ?? gmaps.MapOptions(); + + if(_mapTypeToMapTypeId.containsKey(optionsUpdate['mapType'])) { + options.mapTypeId = _mapTypeToMapTypeId[optionsUpdate['mapType']]; + } + + if (optionsUpdate['minMaxZoomPreference'] != null) { options - ..mapTypeId = gmaps.MapTypeId.values[optionsUpdate['mapType']] - ;} - options - ..minZoom = optionsUpdate['minMaxZoomPreference'][0] - ..maxZoom = optionsUpdate['minMaxZoomPreference'][1] - ; -// compassEnabled, -// mapToolbarEnabled, -// cameraTargetBounds, -// mapType, -// minMaxZoomPreference, -// rotateGesturesEnabled, -// scrollGesturesEnabled, -// tiltGesturesEnabled, -// trackCameraPosition, -// zoomGesturesEnabled, -// myLocationEnabled, -// myLocationButtonEnabled, -// padding, -// indoorViewEnabled, -// trafficEnabled, -// buildingsEnabled, - -// backgroundColor(String _backgroundColor) -//center(LatLng _center) -//clickableIcons(bool _clickableIcons) -//disableDefaultUI(bool _disableDefaultUI) -//disableDoubleClickZoom(bool _disableDoubleClickZoom) -//draggable(bool _draggable) -//draggableCursor(String _draggableCursor) -//draggingCursor(String _draggingCursor) -//fullscreenControl(bool _fullscreenControl) -//fullscreenControlOptions( -//gestureHandling(String _gestureHandling) -//heading(num _heading) -//keyboardShortcuts(bool _keyboardShortcuts) -//mapTypeControl(bool _mapTypeControl) -//mapTypeControlOptions(MapTypeControlOptions _mapTypeControlOptions) -//_mapTypeId(dynamic __mapTypeId) -//mapTypeId(dynamic /*MapTypeId|String*/ mapTypeId) -//maxZoom(num _maxZoom) -//minZoom(num _minZoom) -//noClear(bool _noClear) -//overviewMapControl(bool _overviewMapControl) -//overviewMapControlOptions( -//panControl(bool _panControl) -//panControlOptions(PanControlOptions _panControlOptions) -//rotateControl(bool _rotateControl) -//rotateControlOptions(RotateControlOptions _rotateControlOptions) -//scaleControl(bool _scaleControl) -//scaleControlOptions(ScaleControlOptions _scaleControlOptions) -//scrollwheel(bool _scrollwheel) -//streetView(StreetViewPanorama _streetView) -//streetViewControl(bool _streetViewControl) -//streetViewControlOptions( -//styles(List _styles) -//tilt(num _tilt) -//zoom(num _zoom) -//zoomControl(bool _zoomControl) -//zoomControlOptions(ZoomControlOptions _zoomControlOptions) + ..minZoom = optionsUpdate['minMaxZoomPreference'][0] + ..maxZoom = optionsUpdate['minMaxZoomPreference'][1]; + } + + if (optionsUpdate['cameraTargetBounds'] != null) { + // Needs gmaps.MapOptions.restriction and gmaps.MapRestriction + // see: https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.restriction + } + + if (optionsUpdate['zoomControlsEnabled'] != null) { + options.zoomControl = optionsUpdate['zoomControlsEnabled']; + } + + if (optionsUpdate['styles'] != null) { + options.styles = optionsUpdate['styles']; + } + + if (optionsUpdate['scrollGesturesEnabled'] == false || optionsUpdate['zoomGesturesEnabled'] == false) { + options.gestureHandling = 'none'; + } else { + options.gestureHandling = 'auto'; + } + + // These don't have any optionUpdate entry, but they seem to be off in the native maps. + options.mapTypeControl = optionsUpdate['mapToolbarEnabled'] ?? false; + options.fullscreenControl = optionsUpdate['mapToolbarEnabled'] ?? false; + options.streetViewControl = false; + + return options; } List _mapStyles(String mapStyle) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index e7553ffe4fe9..289486dd8c91 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -6,12 +6,12 @@ class GoogleMapController { final int mapId; ///TODO HtmlElementView html; - ///TODO + + gmaps.TrafficLayer _trafficLayer; + + /// TODO gmaps.GMap googleMap; - ///TODO - DivElement div; - ///TODO - final gmaps.MapOptions options; + final StreamController streamController; CameraPosition position; CirclesController circlesController; @@ -19,7 +19,6 @@ class GoogleMapController { PolylinesController polylinesController; MarkersController markersController; - Set initialCircles; Set initialPolygons; Set initialPolylines; @@ -31,7 +30,7 @@ class GoogleMapController { GoogleMapController.build({ @required this.mapId, @required this.streamController, - @required this.options, + @required gmaps.MapOptions options, @required this.position, @required onPlatformViewCreated, @required this.initialCircles, @@ -46,21 +45,47 @@ class GoogleMapController { html = HtmlElementView( viewType: 'plugins.flutter.io/google_maps_$mapId' ); -// onPlatformViewCreated.call(mapId); - div = DivElement() - ..id = 'plugins.flutter.io/google_maps_$mapId' - ; + DivElement div = DivElement() + ..id = 'plugins.flutter.io/google_maps_$mapId'; + // TODO: Move the comment below to analysis-options.yaml // ignore:undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( 'plugins.flutter.io/google_maps_$mapId', (int viewId) => div, ); + googleMap = gmaps.GMap(div, options); onMapReady(googleMap); _attachMapEvents(googleMap); } + void dispose() { + html = null; + googleMap = null; + circlesController = null; + polygonsController = null; + polylinesController = null; + markersController = null; + streamController.close(); + } + + /// Attaches/detaches a Traffic Layer on the current googleMap. + void setTrafficLayer(bool attach) { + if (attach && _trafficLayer == null) { + _trafficLayer = gmaps.TrafficLayer(); + _trafficLayer.set('map', googleMap); + googleMap.panBy(1, 0); + googleMap.panBy(-1, 0); + } + if (!attach && _trafficLayer != null) { + _trafficLayer.set('map', null); + _trafficLayer = null; + googleMap.panBy(1, 0); + googleMap.panBy(-1, 0); + } + } + void _attachMapEvents(gmaps.GMap map) { map.onClick.listen((event) { streamController.add( @@ -96,6 +121,10 @@ class GoogleMapController { updateInitialMarkers(); } + void setOptions(gmaps.MapOptions options) { + googleMap?.options = options; + } + void setInitialCircles(Set initialCircles) { this.initialCircles = initialCircles; if (googleMap != null) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 41279517ceff..51bb1d8e6c07 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -10,12 +10,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { GoogleMapsFlutterPlatform.instance = GoogleMapsPlugin(); } - static Future get platformVersion async { - return "1.0"; - } - // This is a cache of rendered maps <-> GoogleMapControllers - HashMap _mapById = HashMap(); + Map _mapById = Map(); + Map _optionsById = Map>(); final StreamController _controller = StreamController.broadcast(); @@ -28,6 +25,16 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { /* Noop */ } + // Updates the cache of map options for a given mapId, so we can + // recrate the gmaps.MapOptions object from scratch. + Map _mergeRawMapOptions(dynamic newOptions, int mapId) { + _optionsById[mapId] = { + ..._optionsById[mapId] ?? {}, + ...newOptions, + }; + return _optionsById[mapId]; + } + @override Future updateMapOptions( Map optionsUpdate, { @@ -35,7 +42,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { GoogleMapController googleMapController = _mapById[mapId]; if(googleMapController != null) { - _optionsFromParams(googleMapController.options, optionsUpdate); + googleMapController.setOptions( + _optionsFromParams( + _mergeRawMapOptions(optionsUpdate, mapId), + ), + ); } else { throw StateError("updateMapOptions called prior to map initialization"); } @@ -113,7 +124,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } gmaps.GMap map = googleMapController.googleMap; - // TODO: Subclass CameraUpdate so the below code is not stringly-typed. + // TODO: Subclass CameraUpdate so the below code is not so stringly-typed? dynamic json = cameraUpdate.toJson(); switch (json[0]) { @@ -172,7 +183,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { GoogleMapController googleMapController = _mapById[mapId]; if(googleMapController != null) { - googleMapController.options.styles = _mapStyles(mapStyle); + googleMapController.setOptions( + _optionsFromParams(_mergeRawMapOptions({ + 'styles': _mapStyles(mapStyle), + }, mapId)), + ); } } @@ -188,10 +203,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return Future.value(_gmLatLngBoundsTolatLngBounds(latLngBounds)); } } -// try { throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } -// return Future.error( -// StateError("getVisibleRegion called prior to map initialization") -// ); return Future.value(LatLngBounds(southwest: LatLng(0,0),northeast:LatLng(0,0) )); } @@ -348,8 +359,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } - // TODO: Add a new dispose(int mapId) method to clear the cache of Controllers - // that the `buildView` method is creating! + // TODO: Make this method part of the interface! + void dispose({@required int mapId}) { + _mapById[mapId]?.dispose(); + _mapById.remove(mapId); + _optionsById.remove(mapId); + } @override Widget buildView( @@ -358,29 +373,38 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { PlatformViewCreatedCallback onPlatformViewCreated) { int mapId = creationParams['creationMapId']; + creationParams.remove('creationMapId'); if (mapId == null) { throw PlatformException(code: 'maps_web_missing_creation_map_id', message: 'Pass a `creationMapId` in creationParams to prevent reloads in web.',); } + Map mergedRawOptions; + + // Merge the raw options now, so we can adjust the traffic layer on the cached controller, if needed. + if (creationParams['options'] != null) { + mergedRawOptions = _mergeRawMapOptions(creationParams['options'], mapId); + } + if (_mapById[mapId]?.html != null) { - // print('Map ID $mapId already exists, returning cached...'); + // TODO: Toggling the traffic layer here needs a repaint that isn't happening. + // How to achieve that? + // _mapById[mapId].setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); return _mapById[mapId].html; } - creationParams.remove('creationMapId'); gmaps.MapOptions options = gmaps.MapOptions(); CameraPosition position; - CircleUpdates initialCircles = null; - PolygonUpdates initialPolygons = null; - PolylineUpdates initialPolylines = null; - MarkerUpdates initialMarkers = null; + CircleUpdates initialCircles; + PolygonUpdates initialPolygons; + PolylineUpdates initialPolylines; + MarkerUpdates initialMarkers; creationParams.forEach((key, value) { if(key == 'options') { - _optionsFromParams(options, value); + _optionsFromParams(mergedRawOptions, existingOptions: options); } else if(key == 'markersToAdd') { initialMarkers = _markerFromParams(value); } else if(key == 'polygonsToAdd') { @@ -408,28 +432,17 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { onPlatformViewCreated: onPlatformViewCreated, options: options, position: position, - initialCircles: initialCircles != null - ? initialCircles.circlesToAdd - : null, - initialPolygons: initialPolygons != null ? initialPolygons - .polygonsToAdd : null, - initialPolylines: initialPolylines != null ? initialPolylines - .polylinesToAdd : null, - initialMarkers: initialMarkers != null ? initialMarkers - .markersToAdd : null, - ) - ; - - - /** trafficEnabled - * var trafficLayer = new google.maps.TrafficLayer(); - trafficLayer.setMap(map); - */ - + initialCircles: initialCircles?.circlesToAdd, + initialPolygons: initialPolygons?.polygonsToAdd, + initialPolylines: initialPolylines?.polylinesToAdd, + initialMarkers: initialMarkers?.markersToAdd, + ); -// try {throw Error(); } catch (error, stacktrace) { print(stacktrace.toString()); } onPlatformViewCreated.call(mapId); + // TODO: Enable layer support, once toggling works. + // _mapById[mapId].setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); + return _mapById[mapId].html; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index b986c7583f88..8d766c81c0dc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -29,5 +29,5 @@ dev_dependencies: path: ../google_maps_flutter environment: - sdk: ">=2.0.0-dev.28.0 <3.0.0" + sdk: ">=2.3.0 <3.0.0" flutter: ">=1.10.0 <2.0.0" From ca74def1c0ce898f64e41c5a5da5f95441b82cbb Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Mon, 20 Jul 2020 16:28:10 -0700 Subject: [PATCH 21/58] Split _mapStyles in smaller chunks. Fix typos with featureType conversion. --- .../lib/src/convert.dart | 235 +++++++----------- 1 file changed, 85 insertions(+), 150 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index bda224ad617d..7bc30916019a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -69,159 +69,94 @@ gmaps.MapOptions _optionsFromParams(Map optionsUpdate, { return options; } -List _mapStyles(String mapStyle) { - List styles = []; - if(mapStyle != null) { - List list = json.decode(mapStyle); - list.forEach((style) { - List list2 = style['stylers']; - List stylers = []; - list2.forEach((style) { - stylers.add( - gmaps.MapTypeStyler() - ..color = style['color'] - ..gamma = style['gamma'] - ..hue = style['hue'] - ..invertLightness = style['invertLightness'] - ..lightness = style['lightness'] - ..saturation = style['saturation'] - ..visibility = style['visibility'] - ..weight = style['weight'] - ); - }); +// Coverts the incoming JSON object into a List of MapTypeStyler objects. +List _parseStylers(List stylerJsons) { + return stylerJsons?.map((styler) { + return gmaps.MapTypeStyler() + ..color = styler['color'] + ..gamma = styler['gamma'] + ..hue = styler['hue'] + ..invertLightness = styler['invertLightness'] + ..lightness = styler['lightness'] + ..saturation = styler['saturation'] + ..visibility = styler['visibility'] + ..weight = styler['weight']; + })?.toList(); +} - gmaps.MapTypeStyleElementType elementType; - if (style['elementType'] == 'geometry') { - elementType = gmaps.MapTypeStyleElementType.GEOMETRY; - } else if (style['elementType'] == 'geometry.fill') { - elementType = gmaps.MapTypeStyleElementType.GEOMETRY_FILL; - } else if (style['elementType'] == 'geometry.stroke') { - elementType = gmaps.MapTypeStyleElementType.GEOMETRY_STROKE; - } else if (style['elementType'] == 'labels') { - elementType = gmaps.MapTypeStyleElementType.LABELS; - } else if (style['elementType'] == 'labels.icon') { - elementType = gmaps.MapTypeStyleElementType.LABELS_ICON; - } else if (style['elementType'] == 'labels.text') { - elementType = gmaps.MapTypeStyleElementType.LABELS_TEXT; - } else if (style['elementType'] == 'labels.text.fill') { - elementType = gmaps.MapTypeStyleElementType.LABELS_TEXT_FILL; - } else if (style['elementType'] == 'labels.text.stroke') { - elementType = gmaps.MapTypeStyleElementType.LABELS_TEXT_STROKE; - } +// Converts a String to its corresponding MapTypeStyleElementType enum value. +final _elementTypeToEnum = { + 'all': gmaps.MapTypeStyleElementType.ALL, + 'geometry': gmaps.MapTypeStyleElementType.GEOMETRY, + 'geometry.fill': gmaps.MapTypeStyleElementType.GEOMETRY_FILL, + 'geometry.stroke': gmaps.MapTypeStyleElementType.GEOMETRY_STROKE, + 'labels': gmaps.MapTypeStyleElementType.LABELS, + 'labels.icon': gmaps.MapTypeStyleElementType.LABELS_ICON, + 'labels.text': gmaps.MapTypeStyleElementType.LABELS_TEXT, + 'labels.text.fill': gmaps.MapTypeStyleElementType.LABELS_TEXT_FILL, + 'labels.text.stroke': gmaps.MapTypeStyleElementType.LABELS_TEXT_STROKE, +}; - gmaps.MapTypeStyleFeatureType featureType; - if (style[featureType] == 'administrative') { - featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE; - } - else if (style[featureType] == 'administrative.country') { - featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY; - } - else if (style[featureType] == 'administrative.land_parcel') { - featureType = - gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL; - } - else if (style[featureType] == 'administrative.locality') { - featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY; - } - else if (style[featureType] == 'administrative.neighborhood') { - featureType = - gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD; - } - else if (style[featureType] == 'administrative.province') { - featureType = gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE; - } - else if (style[featureType] == 'all') { - featureType = gmaps.MapTypeStyleFeatureType.ALL; - } - else if (style[featureType] == 'landscape') { - featureType = gmaps.MapTypeStyleFeatureType.LANDSCAPE; - } - else if (style[featureType] == 'landscape.man_made') { - featureType = gmaps.MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE; - } - else if (style[featureType] == 'landscape.natural') { - featureType = gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL; - } - else if (style[featureType] == 'landscape.natural.landcover') { - featureType = - gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER; - } - else if (style[featureType] == 'landscape.natural.terrain') { - featureType = - gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN; - } - else if (style[featureType] == 'poi') { - featureType = gmaps.MapTypeStyleFeatureType.POI; - } - else if (style[featureType] == 'poi.attraction') { - featureType = gmaps.MapTypeStyleFeatureType.POI_ATTRACTION; - } - else if (style[featureType] == 'poi.business') { - featureType = gmaps.MapTypeStyleFeatureType.POI_BUSINESS; - } - else if (style[featureType] == 'poi.government') { - featureType = gmaps.MapTypeStyleFeatureType.POI_GOVERNMENT; - } - else if (style[featureType] == 'poi.medical') { - featureType = gmaps.MapTypeStyleFeatureType.POI_MEDICAL; - } - else if (style[featureType] == 'poi.park') { - featureType = gmaps.MapTypeStyleFeatureType.POI_PARK; - } - else if (style[featureType] == 'poi.place_of_worship') { - featureType = gmaps.MapTypeStyleFeatureType.POI_PLACE_OF_WORSHIP; - } - else if (style[featureType] == 'poi.school') { - featureType = gmaps.MapTypeStyleFeatureType.POI_SCHOOL; - } - else if (style[featureType] == 'poi.sports_complex') { - featureType = gmaps.MapTypeStyleFeatureType.POI_SPORTS_COMPLEX; - } - else if (style[featureType] == 'road') { - featureType = gmaps.MapTypeStyleFeatureType.ROAD; - } - else if (style[featureType] == 'road.arterial') { - featureType = gmaps.MapTypeStyleFeatureType.ROAD_ARTERIAL; - } - else if (style[featureType] == 'road.highway') { - featureType = gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY; - } - else if (style[featureType] == 'road.highway.controlled_access') { - featureType = - gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS; - } - else if (style[featureType] == 'road.local') { - featureType = gmaps.MapTypeStyleFeatureType.ROAD_LOCAL; - } - else if (style[featureType] == 'transit') { - featureType = gmaps.MapTypeStyleFeatureType.TRANSIT; - } - else if (style[featureType] == 'transit.line') { - featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_LINE; - } - else if (style[featureType] == 'transit.station') { - featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION; - } - else if (style[featureType] == 'transit.station.airport') { - featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT; - } - else if (style[featureType] == 'transit.station.bus') { - featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_BUS; - } - else if (style[featureType] == 'transit.station.rail') { - featureType = gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_RAIL; - } - else if (style[featureType] == 'water') { - featureType = gmaps.MapTypeStyleFeatureType.WATER; - } +// Converts a String to its corresponding MapTypeStyleFeatureType enum value. +final _featureTypeToEnum = { + 'administrative': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE, + 'administrative.country': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY, + 'administrative.land_parcel': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL, + 'administrative.locality': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY, + 'administrative.neighborhood': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD, + 'administrative.province': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE, + 'all': gmaps.MapTypeStyleFeatureType.ALL, + 'landscape': gmaps.MapTypeStyleFeatureType.LANDSCAPE, + 'landscape.man_made': gmaps.MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE, + 'landscape.natural': gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL, + 'landscape.natural.landcover': gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER, + 'landscape.natural.terrain': gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN, + 'poi': gmaps.MapTypeStyleFeatureType.POI, + 'poi.attraction': gmaps.MapTypeStyleFeatureType.POI_ATTRACTION, + 'poi.business': gmaps.MapTypeStyleFeatureType.POI_BUSINESS, + 'poi.government': gmaps.MapTypeStyleFeatureType.POI_GOVERNMENT, + 'poi.medical': gmaps.MapTypeStyleFeatureType.POI_MEDICAL, + 'poi.park': gmaps.MapTypeStyleFeatureType.POI_PARK, + 'poi.place_of_worship': gmaps.MapTypeStyleFeatureType.POI_PLACE_OF_WORSHIP, + 'poi.school': gmaps.MapTypeStyleFeatureType.POI_SCHOOL, + 'poi.sports_complex': gmaps.MapTypeStyleFeatureType.POI_SPORTS_COMPLEX, + 'road': gmaps.MapTypeStyleFeatureType.ROAD, + 'road.arterial': gmaps.MapTypeStyleFeatureType.ROAD_ARTERIAL, + 'road.highway': gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY, + 'road.highway.controlled_access': gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS, + 'road.local': gmaps.MapTypeStyleFeatureType.ROAD_LOCAL, + 'transit': gmaps.MapTypeStyleFeatureType.TRANSIT, + 'transit.line': gmaps.MapTypeStyleFeatureType.TRANSIT_LINE, + 'transit.station': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION, + 'transit.station.airport': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT, + 'transit.station.bus': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_BUS, + 'transit.station.rail': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_RAIL, + 'water': gmaps.MapTypeStyleFeatureType.WATER, +}; - styles.add( - gmaps.MapTypeStyle() - ..elementType = elementType - ..featureType = featureType - ..stylers = stylers - ); - }); +// The keys we'd expect to see in a serialized MapTypeStyle JSON object. +final _mapStyleKeys = { + 'elementType', 'featureType', 'stylers', +}; + +// Checks if the passed in Map contains some of the _mapStyleKeys. +bool _isJsonMapStyle(Map value) { + return _mapStyleKeys.intersection(value.keys.toSet()).isNotEmpty; +} + +// Converts an incoming JSON-encoded Style info, into the correct gmaps array. +List _mapStyles(String mapStyleJson) { + List styles = []; + if(mapStyleJson != null) { + styles = json.decode(mapStyleJson, reviver: (key, value) { + if (value is Map && _isJsonMapStyle(value)) { + return gmaps.MapTypeStyle() + ..elementType = _elementTypeToEnum[value['elementType']] + ..featureType = _featureTypeToEnum[value['featureType']] + ..stylers = _parseStylers(value['stylers']); + } + return value; + }).cast(); } return styles; } From e89870347270109615b2bb31fec7e9d649189639 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 24 Jul 2020 15:52:15 -0700 Subject: [PATCH 22/58] Add fake podspec --- .../ios/google_maps_flutter_web.podspec | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/ios/google_maps_flutter_web.podspec diff --git a/packages/google_maps_flutter/google_maps_flutter_web/ios/google_maps_flutter_web.podspec b/packages/google_maps_flutter/google_maps_flutter_web/ios/google_maps_flutter_web.podspec new file mode 100644 index 000000000000..18db6ced01b6 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/ios/google_maps_flutter_web.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint google_maps_flutter_web.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'google_maps_flutter_web' + s.version = '0.1.0' + s.summary = 'No-op implementation of google maps flutter web plugin to avoid build issues on iOS' + s.description = <<-DESC +temp fake google_maps_flutter_web plugin + DESC + s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_web' + s.license = { :file => '../LICENSE' } + s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '8.0' + + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.swift_version = '5.0' +end From a8a7c693204ca698e4b43ec5a11f1894e200f84e Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 24 Jul 2020 15:55:46 -0700 Subject: [PATCH 23/58] Solve flutter analyze (except for missing docs) --- .../lib/google_maps_flutter_web.dart | 1 - .../lib/src/google_maps_flutter_web.dart | 22 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index 7fa183c994f8..732205438bb7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -3,7 +3,6 @@ library google_maps_flutter_web; import 'dart:async'; import 'dart:html'; import 'dart:ui' as ui; -import 'dart:collection'; import 'dart:convert'; import 'dart:typed_data'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 51bb1d8e6c07..cd7de53efdf3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -39,7 +39,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future updateMapOptions( Map optionsUpdate, { @required int mapId, - }) { + }) async { GoogleMapController googleMapController = _mapById[mapId]; if(googleMapController != null) { googleMapController.setOptions( @@ -56,7 +56,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future updateMarkers( MarkerUpdates markerUpdates, { @required int mapId, - }) { + }) async { _mapById[mapId].markersController .addMarkers(markerUpdates.markersToAdd); _mapById[mapId].markersController @@ -69,7 +69,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future updatePolygons( PolygonUpdates polygonUpdates, { @required int mapId, - }) { + }) async { _mapById[mapId].polygonsController .addPolygons(polygonUpdates.polygonsToAdd); _mapById[mapId].polygonsController @@ -82,7 +82,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future updatePolylines( PolylineUpdates polylineUpdates, { @required int mapId, - }) { + }) async { _mapById[mapId].polylinesController .addPolylines(polylineUpdates.polylinesToAdd); _mapById[mapId].polylinesController @@ -95,7 +95,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future updateCircles( CircleUpdates circleUpdates, { @required int mapId, - }) { + }) async { _mapById[mapId].circlesController .addCircles(circleUpdates.circlesToAdd); _mapById[mapId].circlesController @@ -108,15 +108,15 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future animateCamera( CameraUpdate cameraUpdate, { @required int mapId, - }) { - moveCamera(cameraUpdate, mapId: mapId); + }) async { + return moveCamera(cameraUpdate, mapId: mapId); } @override Future moveCamera( CameraUpdate cameraUpdate, { @required int mapId, - }) { + }) async { GoogleMapController googleMapController = _mapById[mapId]; if (googleMapController == null) { @@ -180,7 +180,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future setMapStyle( String mapStyle, { @required int mapId, - }) { + }) async { GoogleMapController googleMapController = _mapById[mapId]; if(googleMapController != null) { googleMapController.setOptions( @@ -243,7 +243,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future showMarkerInfoWindow( MarkerId markerId, { @required int mapId, - }) { + }) async { GoogleMapController googleMapController = _mapById[mapId]; googleMapController.markersController.showMarkerInfoWindow(markerId); } @@ -252,7 +252,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future hideMarkerInfoWindow( MarkerId markerId, { @required int mapId, - }) { + }) async { GoogleMapController googleMapController = _mapById[mapId]; googleMapController.markersController.hideMarkerInfoWindow(markerId); } From c5579a2e2cf0ea4b0ec1f814e195d2d831928c64 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 24 Jul 2020 16:11:14 -0700 Subject: [PATCH 24/58] Add documentation --- .../google_maps_flutter_web/CHANGELOG.md | 3 ++ .../google_maps_flutter_web/LICENSE | 0 .../google_maps_flutter_web/README.md | 49 +++++++++++++++++++ .../lib/src/convert.dart | 4 +- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/LICENSE create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/README.md diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md new file mode 100644 index 000000000000..d989279aac42 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* First open-source version diff --git a/packages/google_maps_flutter/google_maps_flutter_web/LICENSE b/packages/google_maps_flutter/google_maps_flutter_web/LICENSE new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md new file mode 100644 index 000000000000..71fb22b6aaa3 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -0,0 +1,49 @@ +# google_maps_flutter_web + +This is an implementation of the Google Maps Flutter plugin for web. + +## Usage + +### Depend on the package + +This package is not an endorsed implementation of the google_maps_flutter plugin, so you'll need to modify the `pubspec.yaml` file of your app to depend on this package: + +```yaml +dependencies: + google_maps_flutter: ^0.5.28 + google_maps_flutter_web: ^0.1.0 +``` + +### Modify web/index.html + +Get an API Key for Google Maps JavaScript API. Get started [here](https://developers.google.com/maps/documentation/javascript/get-api-key). + +Modify the `` tag of your `web/index.html` to load the Google Maps JavaScript API, like so: + +```html + + + + + + +``` + +Now you should be able to use the Google Maps plugin normally. + +## Limitations of the web version + +The following map options are not available in web, because the map doesn't rotate there: + +* `compassEnabled` +* `rotateGesturesEnabled` +* `tiltGesturesEnabled` + +There's no "Map Toolbar" in web, so the `mapToolbarEnabled` option is unused. + +There's no "My Location" widget in web (for now), so the following options are ignored: + +* `myLocationButtonEnabled` ([doable](https://developers.google.com/maps/documentation/javascript/examples/control-custom)) +* `myLocationEnabled` ([doable](https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html)) + +Traffic, indoor and building layers are still not available on the web. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 7bc30916019a..4943f709711d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -62,8 +62,8 @@ gmaps.MapOptions _optionsFromParams(Map optionsUpdate, { } // These don't have any optionUpdate entry, but they seem to be off in the native maps. - options.mapTypeControl = optionsUpdate['mapToolbarEnabled'] ?? false; - options.fullscreenControl = optionsUpdate['mapToolbarEnabled'] ?? false; + options.mapTypeControl = false; + options.fullscreenControl = false; options.streetViewControl = false; return options; From 3f49c888357f49197989528bf193595a33fe13ad Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 24 Jul 2020 16:12:14 -0700 Subject: [PATCH 25/58] add license --- .../google_maps_flutter_web/LICENSE | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/LICENSE b/packages/google_maps_flutter/google_maps_flutter_web/LICENSE index e69de29bb2d1..282a0f51aa4a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/LICENSE +++ b/packages/google_maps_flutter/google_maps_flutter_web/LICENSE @@ -0,0 +1,26 @@ +Copyright 2017, the Flutter project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 6482d1361b7bd12e28952b6e70047fff7df98df5 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 24 Jul 2020 16:13:17 -0700 Subject: [PATCH 26/58] Automatic pass of dartfmt -w . --- .../lib/google_maps_flutter_web.dart | 2 +- .../lib/src/circle.dart | 22 +- .../lib/src/circles.dart | 43 ++-- .../lib/src/convert.dart | 243 +++++++++--------- .../lib/src/google_maps_controller.dart | 52 ++-- .../lib/src/google_maps_flutter_web.dart | 216 ++++++++-------- .../lib/src/marker.dart | 28 +- .../lib/src/markers.dart | 62 +++-- .../lib/src/polygon.dart | 20 +- .../lib/src/polygons.dart | 41 +-- .../lib/src/polyline.dart | 20 +- .../lib/src/polylines.dart | 42 +-- 12 files changed, 408 insertions(+), 383 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index 732205438bb7..d4730df67fe2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -30,4 +30,4 @@ part 'src/polyline.dart'; part 'src/polylines.dart'; part 'src/marker.dart'; part 'src/markers.dart'; -part 'src/convert.dart'; \ No newline at end of file +part 'src/convert.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 59d57cc908a8..abd7adab5602 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -2,24 +2,26 @@ part of google_maps_flutter_web; /// class CircleController { - - gmaps.Circle _circle; + gmaps.Circle _circle; bool consumeTapEvents = false; ui.VoidCallback onTap; /// - CircleController({@required gmaps.Circle circle, this.consumeTapEvents, this.onTap}){ + CircleController( + {@required gmaps.Circle circle, this.consumeTapEvents, this.onTap}) { _circle = circle; - if(consumeTapEvents) { - circle.onClick.listen((event) { - if(onTap !=null) onTap.call(); - }); - } + if (consumeTapEvents) { + circle.onClick.listen((event) { + if (onTap != null) onTap.call(); + }); + } } - set circle (gmaps.Circle circle) { _circle = circle; } + set circle(gmaps.Circle circle) { + _circle = circle; + } ///TODO void update(gmaps.CircleOptions options) { @@ -33,4 +35,4 @@ class CircleController { _circle = null; //_circle.remove(); } -} \ No newline at end of file +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index 32229825e415..a1da31d76b79 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -1,17 +1,17 @@ part of google_maps_flutter_web; class CirclesController extends AbstractController { - final Map _circleIdToController; StreamController _streamController; CirclesController({ @required StreamController stream, - }): _streamController = stream, _circleIdToController = Map(); + }) : _streamController = stream, + _circleIdToController = Map(); - void addCircles(Set circlesToAdd) { - if(circlesToAdd != null) { + void addCircles(Set circlesToAdd) { + if (circlesToAdd != null) { circlesToAdd.forEach((circle) { addCircle(circle); }); @@ -20,18 +20,19 @@ class CirclesController extends AbstractController { /// add [gmaps.Circle] to [gmaps.GMap]. void addCircle(Circle circle) { - if(circle == null) return; - final populationOptions = _circleOptionsFromCircle(circle); + if (circle == null) return; + final populationOptions = _circleOptionsFromCircle(circle); gmaps.Circle gmCircle = gmaps.Circle(populationOptions); gmCircle.map = googleMap; CircleController controller = CircleController( circle: gmCircle, - consumeTapEvents:circle.consumeTapEvents, - onTap:(){ _onCircleTap(circle.circleId);}); + consumeTapEvents: circle.consumeTapEvents, + onTap: () { + _onCircleTap(circle.circleId); + }); _circleIdToController[circle.circleId] = controller; } - void changeCircles(Set circlesToChange) { if (circlesToChange != null) { circlesToChange.forEach((circleToChange) { @@ -41,20 +42,24 @@ class CirclesController extends AbstractController { } void changeCircle(Circle circle) { - if (circle == null) { return;} + if (circle == null) { + return; + } CircleController circleController = _circleIdToController[circle.circleId]; - if (circleController != null) { - circleController.update( - _circleOptionsFromCircle(circle)); - } + if (circleController != null) { + circleController.update(_circleOptionsFromCircle(circle)); + } } void removeCircles(Set circleIdsToRemove) { - if (circleIdsToRemove == null) {return;} + if (circleIdsToRemove == null) { + return; + } circleIdsToRemove.forEach((circleId) { - if(circleId != null) { - final CircleController circleController = _circleIdToController[circleId]; - if(circleController != null) { + if (circleId != null) { + final CircleController circleController = + _circleIdToController[circleId]; + if (circleController != null) { circleController.remove(); _circleIdToController.remove(circleId); } @@ -62,8 +67,6 @@ class CirclesController extends AbstractController { }); } - - bool _onCircleTap(CircleId circleId) { _streamController.add(CircleTapEvent(mapId, circleId)); // Stop propagation? diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 4943f709711d..14940bf71d10 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -26,20 +26,20 @@ final _mapTypeToMapTypeId = { // indoorViewEnabled seems to not have an equivalent in web // buildingsEnabled seems to not have an equivalent in web // padding seems to behave differently in web than mobile. You can't move UI elements in web. -gmaps.MapOptions _optionsFromParams(Map optionsUpdate, { - gmaps.MapOptions existingOptions, - }) { - +gmaps.MapOptions _optionsFromParams( + Map optionsUpdate, { + gmaps.MapOptions existingOptions, +}) { gmaps.MapOptions options = existingOptions ?? gmaps.MapOptions(); - if(_mapTypeToMapTypeId.containsKey(optionsUpdate['mapType'])) { + if (_mapTypeToMapTypeId.containsKey(optionsUpdate['mapType'])) { options.mapTypeId = _mapTypeToMapTypeId[optionsUpdate['mapType']]; } if (optionsUpdate['minMaxZoomPreference'] != null) { options - ..minZoom = optionsUpdate['minMaxZoomPreference'][0] - ..maxZoom = optionsUpdate['minMaxZoomPreference'][1]; + ..minZoom = optionsUpdate['minMaxZoomPreference'][0] + ..maxZoom = optionsUpdate['minMaxZoomPreference'][1]; } if (optionsUpdate['cameraTargetBounds'] != null) { @@ -55,7 +55,8 @@ gmaps.MapOptions _optionsFromParams(Map optionsUpdate, { options.styles = optionsUpdate['styles']; } - if (optionsUpdate['scrollGesturesEnabled'] == false || optionsUpdate['zoomGesturesEnabled'] == false) { + if (optionsUpdate['scrollGesturesEnabled'] == false || + optionsUpdate['zoomGesturesEnabled'] == false) { options.gestureHandling = 'none'; } else { options.gestureHandling = 'auto'; @@ -100,17 +101,24 @@ final _elementTypeToEnum = { // Converts a String to its corresponding MapTypeStyleFeatureType enum value. final _featureTypeToEnum = { 'administrative': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE, - 'administrative.country': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY, - 'administrative.land_parcel': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL, - 'administrative.locality': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY, - 'administrative.neighborhood': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD, - 'administrative.province': gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE, + 'administrative.country': + gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY, + 'administrative.land_parcel': + gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL, + 'administrative.locality': + gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY, + 'administrative.neighborhood': + gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD, + 'administrative.province': + gmaps.MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE, 'all': gmaps.MapTypeStyleFeatureType.ALL, 'landscape': gmaps.MapTypeStyleFeatureType.LANDSCAPE, 'landscape.man_made': gmaps.MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE, 'landscape.natural': gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL, - 'landscape.natural.landcover': gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER, - 'landscape.natural.terrain': gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN, + 'landscape.natural.landcover': + gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER, + 'landscape.natural.terrain': + gmaps.MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN, 'poi': gmaps.MapTypeStyleFeatureType.POI, 'poi.attraction': gmaps.MapTypeStyleFeatureType.POI_ATTRACTION, 'poi.business': gmaps.MapTypeStyleFeatureType.POI_BUSINESS, @@ -123,12 +131,14 @@ final _featureTypeToEnum = { 'road': gmaps.MapTypeStyleFeatureType.ROAD, 'road.arterial': gmaps.MapTypeStyleFeatureType.ROAD_ARTERIAL, 'road.highway': gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY, - 'road.highway.controlled_access': gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS, + 'road.highway.controlled_access': + gmaps.MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS, 'road.local': gmaps.MapTypeStyleFeatureType.ROAD_LOCAL, 'transit': gmaps.MapTypeStyleFeatureType.TRANSIT, 'transit.line': gmaps.MapTypeStyleFeatureType.TRANSIT_LINE, 'transit.station': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION, - 'transit.station.airport': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT, + 'transit.station.airport': + gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT, 'transit.station.bus': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_BUS, 'transit.station.rail': gmaps.MapTypeStyleFeatureType.TRANSIT_STATION_RAIL, 'water': gmaps.MapTypeStyleFeatureType.WATER, @@ -136,7 +146,9 @@ final _featureTypeToEnum = { // The keys we'd expect to see in a serialized MapTypeStyle JSON object. final _mapStyleKeys = { - 'elementType', 'featureType', 'stylers', + 'elementType', + 'featureType', + 'stylers', }; // Checks if the passed in Map contains some of the _mapStyleKeys. @@ -147,7 +159,7 @@ bool _isJsonMapStyle(Map value) { // Converts an incoming JSON-encoded Style info, into the correct gmaps array. List _mapStyles(String mapStyleJson) { List styles = []; - if(mapStyleJson != null) { + if (mapStyleJson != null) { styles = json.decode(mapStyleJson, reviver: (key, value) { if (value is Map && _isJsonMapStyle(value)) { return gmaps.MapTypeStyle() @@ -172,30 +184,28 @@ PolylineUpdates _polylineFromParams(value) { jsonPoints.forEach((p) { points.add(LatLng.fromJson(p)); }); - current.add( - Polyline( - polylineId: polylineId, - consumeTapEvents: polyline['consumeTapEvents'], - color: Color(polyline['color']), - geodesic: polyline['geodesic'], - visible: polyline['visible'], - zIndex: polyline['zIndex'], - width: polyline['width'], - points: points, + current.add(Polyline( + polylineId: polylineId, + consumeTapEvents: polyline['consumeTapEvents'], + color: Color(polyline['color']), + geodesic: polyline['geodesic'], + visible: polyline['visible'], + zIndex: polyline['zIndex'], + width: polyline['width'], + points: points, // endCap = Cap.buttCap, // jointType = JointType.mitered, // patterns = const [], // startCap = Cap.buttCap, - ) - ); + )); }); return PolylineUpdates.from(null, current); } return null; } -gmaps.PolylineOptions _polylineOptionsFromPolyline(gmaps.GMap googleMap, - Polyline polyline) { +gmaps.PolylineOptions _polylineOptionsFromPolyline( + gmaps.GMap googleMap, Polyline polyline) { List paths = []; polyline.points.forEach((point) { paths.add(_latlngToGmLatlng(point)); @@ -205,11 +215,10 @@ gmaps.PolylineOptions _polylineOptionsFromPolyline(gmaps.GMap googleMap, ..path = paths ..strokeOpacity = 1.0 ..strokeWeight = polyline.width - ..strokeColor = '#'+polyline.color.value.toRadixString(16).substring(0,6) + ..strokeColor = '#' + polyline.color.value.toRadixString(16).substring(0, 6) ..visible = polyline.visible ..zIndex = polyline.zIndex - ..geodesic = polyline.geodesic - ; + ..geodesic = polyline.geodesic; // this.endCap = Cap.buttCap, // this.jointType = JointType.mitered, // this.patterns = const [], @@ -217,7 +226,6 @@ gmaps.PolylineOptions _polylineOptionsFromPolyline(gmaps.GMap googleMap, // this.width = 10, } - PolygonUpdates _polygonFromParams(value) { if (value != null) { List> list = value; @@ -229,53 +237,50 @@ PolygonUpdates _polygonFromParams(value) { jsonPoints.forEach((p) { points.add(LatLng.fromJson(p)); }); - current.add( - Polygon( - polygonId: polygonId, - consumeTapEvents: polygon['consumeTapEvents'], - fillColor: Color(polygon['fillColor']), - geodesic: polygon['geodesic'], - strokeColor: Color(polygon['strokeColor']), - strokeWidth: polygon['strokeWidth'], - visible: polygon['visible'], - zIndex: polygon['zIndex'], - points: points, - ) - ); + current.add(Polygon( + polygonId: polygonId, + consumeTapEvents: polygon['consumeTapEvents'], + fillColor: Color(polygon['fillColor']), + geodesic: polygon['geodesic'], + strokeColor: Color(polygon['strokeColor']), + strokeWidth: polygon['strokeWidth'], + visible: polygon['visible'], + zIndex: polygon['zIndex'], + points: points, + )); }); return PolygonUpdates.from(null, current); } return null; } -gmaps.PolygonOptions _polygonOptionsFromPolygon(gmaps.GMap googleMap, - Polygon polygon) { +gmaps.PolygonOptions _polygonOptionsFromPolygon( + gmaps.GMap googleMap, Polygon polygon) { List paths = []; polygon.points.forEach((point) { paths.add(_latlngToGmLatlng(point)); }); return gmaps.PolygonOptions() ..paths = paths - ..strokeColor = '#'+polygon.strokeColor.value.toRadixString(16) + ..strokeColor = '#' + polygon.strokeColor.value.toRadixString(16) ..strokeOpacity = 0.8 ..strokeWeight = polygon.strokeWidth - ..fillColor = '#'+polygon.fillColor.value.toRadixString(16) + ..fillColor = '#' + polygon.fillColor.value.toRadixString(16) ..fillOpacity = 0.35 ..visible = polygon.visible ..zIndex = polygon.zIndex - ..geodesic = polygon.geodesic - ; + ..geodesic = polygon.geodesic; } -gmaps.LatLng _latlngToGmLatlng(LatLng latLng){ +gmaps.LatLng _latlngToGmLatlng(LatLng latLng) { return gmaps.LatLng(latLng.latitude, latLng.longitude); } -LatLng _gmLatlngToLatlng(gmaps.LatLng latLng){ +LatLng _gmLatlngToLatlng(gmaps.LatLng latLng) { return LatLng(latLng.lat, latLng.lng); } -LatLngBounds _gmLatLngBoundsTolatLngBounds(gmaps.LatLngBounds latLngBounds){ +LatLngBounds _gmLatLngBoundsTolatLngBounds(gmaps.LatLngBounds latLngBounds) { return LatLngBounds( southwest: _gmLatlngToLatlng(latLngBounds.southWest), northeast: _gmLatlngToLatlng(latLngBounds.northEast), @@ -297,29 +302,25 @@ MarkerUpdates _markerFromParams(value) { Set current = Set(); list.forEach((marker) { MarkerId markerId = MarkerId(marker['markerId']); - Offset offset = Offset( - (marker['anchor'][0]), - (marker['anchor'][1])); - current.add( - Marker( - markerId: markerId, - alpha: marker['alpha'], - anchor: offset, - consumeTapEvents: marker['consumeTapEvents'], - draggable: marker['draggable'], - flat: marker['flat'], - icon: BitmapDescriptor.defaultMarker, - infoWindow: InfoWindow( - title: marker['infoWindow']['title'] ?? '', - snippet: marker['snippet'], - anchor : offset, - ), - position: LatLng.fromJson(marker['position']), - rotation: marker['rotation'], - visible: marker['visible'], - zIndex: marker['zIndex'], - ) - ); + Offset offset = Offset((marker['anchor'][0]), (marker['anchor'][1])); + current.add(Marker( + markerId: markerId, + alpha: marker['alpha'], + anchor: offset, + consumeTapEvents: marker['consumeTapEvents'], + draggable: marker['draggable'], + flat: marker['flat'], + icon: BitmapDescriptor.defaultMarker, + infoWindow: InfoWindow( + title: marker['infoWindow']['title'] ?? '', + snippet: marker['snippet'], + anchor: offset, + ), + position: LatLng.fromJson(marker['position']), + rotation: marker['rotation'], + visible: marker['visible'], + zIndex: marker['zIndex'], + )); }); return MarkerUpdates.from(null, current); } @@ -329,49 +330,47 @@ MarkerUpdates _markerFromParams(value) { gmaps.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { return gmaps.InfoWindowOptions() ..content = marker.infoWindow.snippet - ..zIndex = marker.zIndex - ..position = gmaps.LatLng( - marker.position.latitude, - marker.position.longitude) - ; + ..zIndex = marker.zIndex + ..position = + gmaps.LatLng(marker.position.latitude, marker.position.longitude); } -gmaps.MarkerOptions _markerOptionsFromMarker(gmaps.GMap googleMap, - Marker marker) { - +gmaps.MarkerOptions _markerOptionsFromMarker( + gmaps.GMap googleMap, Marker marker) { final iconConfig = marker.icon.toJson(); gmaps.Icon icon; - if(iconConfig[0] == 'fromAssetImage') { - icon = gmaps.Icon() - ..url = iconConfig[1]; + if (iconConfig[0] == 'fromAssetImage') { + icon = gmaps.Icon()..url = iconConfig[1]; } return gmaps.MarkerOptions() - ..position = gmaps.LatLng(marker.position.latitude, - marker.position.longitude,) - ..title = marker.infoWindow.title - ..zIndex = marker.zIndex - ..visible = marker.visible - ..opacity = marker.alpha + ..position = gmaps.LatLng( + marker.position.latitude, + marker.position.longitude, + ) + ..title = marker.infoWindow.title + ..zIndex = marker.zIndex + ..visible = marker.visible + ..opacity = marker.alpha ..draggable = marker.draggable - ..icon = icon - ..anchorPoint = gmaps.Point(marker.anchor.dx, marker.anchor.dy,); - // Flat and Rotation are not supported directly on the web. + ..icon = icon + ..anchorPoint = gmaps.Point( + marker.anchor.dx, + marker.anchor.dy, + ); + // Flat and Rotation are not supported directly on the web. } - - gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { final populationOptions = gmaps.CircleOptions() - ..strokeColor = '#'+circle.strokeColor.value.toRadixString(16) + ..strokeColor = '#' + circle.strokeColor.value.toRadixString(16) ..strokeOpacity = 0.8 ..strokeWeight = circle.strokeWidth - ..fillColor = '#'+circle.fillColor.value.toRadixString(16) + ..fillColor = '#' + circle.fillColor.value.toRadixString(16) ..fillOpacity = 0.6 - ..center = gmaps.LatLng(circle.center.latitude,circle.center.longitude) + ..center = gmaps.LatLng(circle.center.latitude, circle.center.longitude) ..radius = circle.radius - ..visible = circle.visible - ; + ..visible = circle.visible; return populationOptions; } @@ -381,21 +380,19 @@ CircleUpdates _circleFromParams(value) { Set current = Set(); list.forEach((circle) { CircleId circleId = CircleId(circle['circleId']); - current.add( - Circle( - circleId: circleId, - consumeTapEvents: circle['consumeTapEvents'], - fillColor: Color(circle['fillColor']), - center: LatLng.fromJson(circle['center']), - radius: circle['radius'], - strokeColor: Color(circle['strokeColor']), - strokeWidth: circle['strokeWidth'], - visible: circle['visible'], - zIndex: circle['zIndex'], - ) - ); + current.add(Circle( + circleId: circleId, + consumeTapEvents: circle['consumeTapEvents'], + fillColor: Color(circle['fillColor']), + center: LatLng.fromJson(circle['center']), + radius: circle['radius'], + strokeColor: Color(circle['strokeColor']), + strokeWidth: circle['strokeWidth'], + visible: circle['visible'], + zIndex: circle['zIndex'], + )); }); return CircleUpdates.from(null, current); } return null; -} \ No newline at end of file +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 289486dd8c91..2c326b18aa34 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -4,25 +4,26 @@ part of google_maps_flutter_web; class GoogleMapController { ///TODO final int mapId; + ///TODO HtmlElementView html; gmaps.TrafficLayer _trafficLayer; - + /// TODO gmaps.GMap googleMap; - final StreamController streamController; - CameraPosition position; - CirclesController circlesController; - PolygonsController polygonsController; - PolylinesController polylinesController; - MarkersController markersController; + final StreamController streamController; + CameraPosition position; + CirclesController circlesController; + PolygonsController polygonsController; + PolylinesController polylinesController; + MarkersController markersController; - Set initialCircles; - Set initialPolygons; - Set initialPolylines; - Set initialMarkers; + Set initialCircles; + Set initialPolygons; + Set initialPolylines; + Set initialMarkers; bool _mapIsMoving = false; @@ -42,17 +43,14 @@ class GoogleMapController { polygonsController = PolygonsController(stream: this.streamController); polylinesController = PolylinesController(stream: this.streamController); markersController = MarkersController(stream: this.streamController); - html = HtmlElementView( - viewType: 'plugins.flutter.io/google_maps_$mapId' - ); - DivElement div = DivElement() - ..id = 'plugins.flutter.io/google_maps_$mapId'; + html = HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$mapId'); + DivElement div = DivElement()..id = 'plugins.flutter.io/google_maps_$mapId'; // TODO: Move the comment below to analysis-options.yaml // ignore:undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( 'plugins.flutter.io/google_maps_$mapId', - (int viewId) => div, + (int viewId) => div, ); googleMap = gmaps.GMap(div, options); @@ -89,18 +87,22 @@ class GoogleMapController { void _attachMapEvents(gmaps.GMap map) { map.onClick.listen((event) { streamController.add( - MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng)),); + MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng)), + ); }); map.onRightclick.listen((event) { streamController.add( - MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng)),); + MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng)), + ); }); map.onBoundsChanged.listen((event) { if (!_mapIsMoving) { _mapIsMoving = true; streamController.add(CameraMoveStartedEvent(mapId)); } - streamController.add(CameraMoveEvent(mapId, _gmViewportToCameraPosition(map)),); + streamController.add( + CameraMoveEvent(mapId, _gmViewportToCameraPosition(map)), + ); }); map.onIdle.listen((event) { _mapIsMoving = false; @@ -133,7 +135,7 @@ class GoogleMapController { } void updateInitialCircles() { - if(initialCircles == null) return; + if (initialCircles == null) return; circlesController.addCircles(initialCircles); } @@ -159,17 +161,17 @@ class GoogleMapController { } void updateInitialPolygons() { - if(initialPolygons == null) return; + if (initialPolygons == null) return; polygonsController.addPolygons(initialPolygons); } void updateInitialPolylines() { - if(initialPolylines == null) return; + if (initialPolylines == null) return; polylinesController.addPolylines(initialPolylines); } void updateInitialMarkers() { - if(initialMarkers == null) return; + if (initialMarkers == null) return; markersController.addMarkers(initialMarkers); } } @@ -181,4 +183,4 @@ abstract class AbstractController { this.mapId = mapId; this.googleMap = googleMap; } -} \ No newline at end of file +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index cd7de53efdf3..8ac8062b9bc9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -4,7 +4,6 @@ part of google_maps_flutter_web; /// /// This class implements the `package:google_maps_flutter` functionality for the web. class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { - /// Registers this class as the default instance of [GoogleMapsFlutterPlatform]. static void registerWith(Registrar registrar) { GoogleMapsFlutterPlatform.instance = GoogleMapsPlugin(); @@ -15,7 +14,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map _optionsById = Map>(); final StreamController _controller = - StreamController.broadcast(); + StreamController.broadcast(); Stream _events(int mapId) => _controller.stream.where((event) => event.mapId == mapId); @@ -25,7 +24,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { /* Noop */ } - // Updates the cache of map options for a given mapId, so we can + // Updates the cache of map options for a given mapId, so we can // recrate the gmaps.MapOptions object from scratch. Map _mergeRawMapOptions(dynamic newOptions, int mapId) { _optionsById[mapId] = { @@ -37,11 +36,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @override Future updateMapOptions( - Map optionsUpdate, { - @required int mapId, - }) async { + Map optionsUpdate, { + @required int mapId, + }) async { GoogleMapController googleMapController = _mapById[mapId]; - if(googleMapController != null) { + if (googleMapController != null) { googleMapController.setOptions( _optionsFromParams( _mergeRawMapOptions(optionsUpdate, mapId), @@ -54,70 +53,77 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @override Future updateMarkers( - MarkerUpdates markerUpdates, { - @required int mapId, - }) async { - _mapById[mapId].markersController - .addMarkers(markerUpdates.markersToAdd); - _mapById[mapId].markersController + MarkerUpdates markerUpdates, { + @required int mapId, + }) async { + _mapById[mapId].markersController.addMarkers(markerUpdates.markersToAdd); + _mapById[mapId] + .markersController .changeMarkers(markerUpdates.markersToChange); - _mapById[mapId].markersController + _mapById[mapId] + .markersController .removeMarkers(markerUpdates.markerIdsToRemove); } @override Future updatePolygons( - PolygonUpdates polygonUpdates, { - @required int mapId, - }) async { - _mapById[mapId].polygonsController + PolygonUpdates polygonUpdates, { + @required int mapId, + }) async { + _mapById[mapId] + .polygonsController .addPolygons(polygonUpdates.polygonsToAdd); - _mapById[mapId].polygonsController + _mapById[mapId] + .polygonsController .changePolygons(polygonUpdates.polygonsToChange); - _mapById[mapId].polygonsController + _mapById[mapId] + .polygonsController .removePolygons(polygonUpdates.polygonIdsToRemove); } @override Future updatePolylines( - PolylineUpdates polylineUpdates, { - @required int mapId, - }) async { - _mapById[mapId].polylinesController + PolylineUpdates polylineUpdates, { + @required int mapId, + }) async { + _mapById[mapId] + .polylinesController .addPolylines(polylineUpdates.polylinesToAdd); - _mapById[mapId].polylinesController + _mapById[mapId] + .polylinesController .changePolylines(polylineUpdates.polylinesToChange); - _mapById[mapId].polylinesController + _mapById[mapId] + .polylinesController .removePolylines(polylineUpdates.polylineIdsToRemove); } @override Future updateCircles( - CircleUpdates circleUpdates, { - @required int mapId, - }) async { - _mapById[mapId].circlesController - .addCircles(circleUpdates.circlesToAdd); - _mapById[mapId].circlesController + CircleUpdates circleUpdates, { + @required int mapId, + }) async { + _mapById[mapId].circlesController.addCircles(circleUpdates.circlesToAdd); + _mapById[mapId] + .circlesController .changeCircles(circleUpdates.circlesToChange); - _mapById[mapId].circlesController + _mapById[mapId] + .circlesController .removeCircles(circleUpdates.circleIdsToRemove); } @override Future animateCamera( - CameraUpdate cameraUpdate, { - @required int mapId, - }) async { + CameraUpdate cameraUpdate, { + @required int mapId, + }) async { return moveCamera(cameraUpdate, mapId: mapId); } @override Future moveCamera( - CameraUpdate cameraUpdate, { - @required int mapId, - }) async { - + CameraUpdate cameraUpdate, { + @required int mapId, + }) async { GoogleMapController googleMapController = _mapById[mapId]; if (googleMapController == null) { return null; @@ -143,9 +149,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { break; case 'newLatLngBounds': map.fitBounds(gmaps.LatLngBounds( - gmaps.LatLng(json[1][0][0],json[1][0][1]), - gmaps.LatLng(json[1][1][0],json[1][1][1]) - )); + gmaps.LatLng(json[1][0][0], json[1][0][1]), + gmaps.LatLng(json[1][1][0], json[1][1][1]))); // padding = json[2]; // Needs package:google_maps ^4.0.0 to adjust the padding in fitBounds break; @@ -172,17 +177,18 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { map.zoom = json[1]; break; default: - throw UnimplementedError('moveCamera() does not implement: ${json[0]}.'); + throw UnimplementedError( + 'moveCamera() does not implement: ${json[0]}.'); } } @override Future setMapStyle( - String mapStyle, { - @required int mapId, - }) async { + String mapStyle, { + @required int mapId, + }) async { GoogleMapController googleMapController = _mapById[mapId]; - if(googleMapController != null) { + if (googleMapController != null) { googleMapController.setOptions( _optionsFromParams(_mergeRawMapOptions({ 'styles': _mapStyles(mapStyle), @@ -196,21 +202,21 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { @required int mapId, }) { GoogleMapController googleMapController = _mapById[mapId]; - if(googleMapController != null) { - gmaps.LatLngBounds latLngBounds = googleMapController.googleMap - .bounds; - if(latLngBounds != null) { + if (googleMapController != null) { + gmaps.LatLngBounds latLngBounds = googleMapController.googleMap.bounds; + if (latLngBounds != null) { return Future.value(_gmLatLngBoundsTolatLngBounds(latLngBounds)); } } - return Future.value(LatLngBounds(southwest: LatLng(0,0),northeast:LatLng(0,0) )); + return Future.value( + LatLngBounds(southwest: LatLng(0, 0), northeast: LatLng(0, 0))); } @override Future getScreenCoordinate( - LatLng latLng, { - @required int mapId, - }) { + LatLng latLng, { + @required int mapId, + }) { GoogleMapController googleMapController = _mapById[mapId]; if (googleMapController != null) { gmaps.Point point = googleMapController.googleMap.projection @@ -218,54 +224,51 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return Future.value(ScreenCoordinate(x: point.x, y: point.y)); } return Future.error( - StateError("getScreenCoordinate called prior to map initialization") - ); + StateError("getScreenCoordinate called prior to map initialization")); } @override Future getLatLng( - ScreenCoordinate screenCoordinate, { - @required int mapId, - }) { + ScreenCoordinate screenCoordinate, { + @required int mapId, + }) { GoogleMapController googleMapController = _mapById[mapId]; - if(googleMapController != null) { - gmaps.LatLng latLng = googleMapController.googleMap.projection.fromPointToLatLng( - gmaps.Point(screenCoordinate.x, screenCoordinate.y) - ); + if (googleMapController != null) { + gmaps.LatLng latLng = googleMapController.googleMap.projection + .fromPointToLatLng( + gmaps.Point(screenCoordinate.x, screenCoordinate.y)); return Future.value(_gmLatlngToLatlng(latLng)); } return Future.error( - StateError("getLatLng called prior to map initialization") - ); + StateError("getLatLng called prior to map initialization")); } @override Future showMarkerInfoWindow( - MarkerId markerId, { - @required int mapId, - }) async { + MarkerId markerId, { + @required int mapId, + }) async { GoogleMapController googleMapController = _mapById[mapId]; googleMapController.markersController.showMarkerInfoWindow(markerId); } @override Future hideMarkerInfoWindow( - MarkerId markerId, { - @required int mapId, - }) async { + MarkerId markerId, { + @required int mapId, + }) async { GoogleMapController googleMapController = _mapById[mapId]; googleMapController.markersController.hideMarkerInfoWindow(markerId); } @override Future isMarkerInfoWindowShown( - MarkerId markerId, { - @required int mapId, - }) { + MarkerId markerId, { + @required int mapId, + }) { GoogleMapController googleMapController = _mapById[mapId]; return Future.value( - googleMapController.markersController.isInfoWindowShown(markerId) - ); + googleMapController.markersController.isInfoWindowShown(markerId)); } @override @@ -371,12 +374,15 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map creationParams, Set> gestureRecognizers, PlatformViewCreatedCallback onPlatformViewCreated) { - int mapId = creationParams['creationMapId']; creationParams.remove('creationMapId'); if (mapId == null) { - throw PlatformException(code: 'maps_web_missing_creation_map_id', message: 'Pass a `creationMapId` in creationParams to prevent reloads in web.',); + throw PlatformException( + code: 'maps_web_missing_creation_map_id', + message: + 'Pass a `creationMapId` in creationParams to prevent reloads in web.', + ); } Map mergedRawOptions; @@ -393,50 +399,46 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _mapById[mapId].html; } - gmaps.MapOptions options = gmaps.MapOptions(); CameraPosition position; - CircleUpdates initialCircles; - PolygonUpdates initialPolygons; - PolylineUpdates initialPolylines; - MarkerUpdates initialMarkers; + CircleUpdates initialCircles; + PolygonUpdates initialPolygons; + PolylineUpdates initialPolylines; + MarkerUpdates initialMarkers; creationParams.forEach((key, value) { - if(key == 'options') { + if (key == 'options') { _optionsFromParams(mergedRawOptions, existingOptions: options); - } else if(key == 'markersToAdd') { + } else if (key == 'markersToAdd') { initialMarkers = _markerFromParams(value); - } else if(key == 'polygonsToAdd') { + } else if (key == 'polygonsToAdd') { initialPolygons = _polygonFromParams(value); - } else if(key == 'polylinesToAdd') { + } else if (key == 'polylinesToAdd') { initialPolylines = _polylineFromParams(value); - } else if(key == 'circlesToAdd') { + } else if (key == 'circlesToAdd') { initialCircles = _circleFromParams(value); - } else if(key == 'initialCameraPosition') { + } else if (key == 'initialCameraPosition') { position = CameraPosition.fromMap(value); options.zoom = position.zoom; - options.center = gmaps.LatLng( - position.target.latitude, - position.target.longitude - ); + options.center = + gmaps.LatLng(position.target.latitude, position.target.longitude); } else { print('un-handle >>$key'); } }); - _mapById[mapId] = - GoogleMapController.build( - mapId: mapId, - streamController: _controller, - onPlatformViewCreated: onPlatformViewCreated, - options: options, - position: position, - initialCircles: initialCircles?.circlesToAdd, - initialPolygons: initialPolygons?.polygonsToAdd, - initialPolylines: initialPolylines?.polylinesToAdd, - initialMarkers: initialMarkers?.markersToAdd, - ); + _mapById[mapId] = GoogleMapController.build( + mapId: mapId, + streamController: _controller, + onPlatformViewCreated: onPlatformViewCreated, + options: options, + position: position, + initialCircles: initialCircles?.circlesToAdd, + initialPolygons: initialPolygons?.polygonsToAdd, + initialPolylines: initialPolylines?.polylinesToAdd, + initialMarkers: initialMarkers?.markersToAdd, + ); onPlatformViewCreated.call(mapId); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 9e11e6dacdcd..4473592cb315 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -1,7 +1,6 @@ part of google_maps_flutter_web; class MarkerController { - gmaps.Marker _marker; gmaps.InfoWindow infoWindow; bool consumeTapEvents = false; @@ -9,6 +8,7 @@ class MarkerController { ui.VoidCallback onInfoWindowTap; LatLngCallback onDragEnd; bool infoWindowShown = false; + /// MarkerController({ @required gmaps.Marker marker, @@ -17,25 +17,27 @@ class MarkerController { this.onTap, this.onDragEnd, this.onInfoWindowTap, - }){ + }) { _marker = marker; - if(consumeTapEvents) { - } - if(onTap !=null){ - marker.onClick.listen((event) {onTap.call(); }); + if (consumeTapEvents) {} + if (onTap != null) { + marker.onClick.listen((event) { + onTap.call(); + }); } - if(_marker.draggable) { + if (_marker.draggable) { marker.onDragend.listen((event) { - if(onDragEnd !=null) onDragEnd.call(event.latLng); + if (onDragEnd != null) onDragEnd.call(event.latLng); }); } - if(onInfoWindowTap !=null) { + if (onInfoWindowTap != null) { infoWindow.addListener('click', onInfoWindowTap); } } - - set marker (gmaps.Marker marker) { _marker = marker; } + set marker(gmaps.Marker marker) { + _marker = marker; + } void update(gmaps.MarkerOptions options) { _marker.options = options; @@ -49,7 +51,7 @@ class MarkerController { } void hideInfoWindow() { - if(infoWindow != null) { + if (infoWindow != null) { infoWindow.close(); infoWindowShown = false; } @@ -63,4 +65,4 @@ class MarkerController { bool isInfoWindowShown() { return infoWindowShown; } -} \ No newline at end of file +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 0eafd775d357..ff32b767eb14 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -1,39 +1,43 @@ part of google_maps_flutter_web; class MarkersController extends AbstractController { - final Map _markerIdToController; StreamController _streamController; MarkersController({ - @required StreamController stream, - }): _streamController = stream, _markerIdToController = Map(); + @required StreamController stream, + }) : _streamController = stream, + _markerIdToController = Map(); void addMarkers(Set markersToAdd) { - if(markersToAdd != null) { + if (markersToAdd != null) { markersToAdd.forEach((marker) { _addMarker(marker); }); } } - void _addMarker(Marker marker){ - if(marker == null) return; + void _addMarker(Marker marker) { + if (marker == null) return; final infoWindoOptions = _infoWindowOPtionsFromMarker(marker); gmaps.InfoWindow gmInfoWindow = gmaps.InfoWindow(infoWindoOptions); - final populationOptions = _markerOptionsFromMarker(googleMap, marker); - gmaps.Marker gmMarker = gmaps.Marker(populationOptions); + final populationOptions = _markerOptionsFromMarker(googleMap, marker); + gmaps.Marker gmMarker = gmaps.Marker(populationOptions); gmMarker.map = googleMap; MarkerController controller = MarkerController( marker: gmMarker, - infoWindow : gmInfoWindow, - consumeTapEvents:marker.consumeTapEvents, - onTap:(){ _onMarkerTap(marker.markerId);}, - onDragEnd : (gmaps.LatLng latLng){ - _onMarkerDragEnd(marker.markerId, latLng);}, - onInfoWindowTap : (){ _onInfoWindowTap(marker.markerId);} - ); + infoWindow: gmInfoWindow, + consumeTapEvents: marker.consumeTapEvents, + onTap: () { + _onMarkerTap(marker.markerId); + }, + onDragEnd: (gmaps.LatLng latLng) { + _onMarkerDragEnd(marker.markerId, latLng); + }, + onInfoWindowTap: () { + _onInfoWindowTap(marker.markerId); + }); _markerIdToController[marker.markerId] = controller; } @@ -46,20 +50,24 @@ class MarkersController extends AbstractController { } void changeMarker(Marker marker) { - if (marker == null) { return;} + if (marker == null) { + return; + } MarkerController markerController = _markerIdToController[marker.markerId]; if (markerController != null) { - markerController.update( - _markerOptionsFromMarker(googleMap, marker)); + markerController.update(_markerOptionsFromMarker(googleMap, marker)); } } void removeMarkers(Set markerIdsToRemove) { - if (markerIdsToRemove == null) {return;} + if (markerIdsToRemove == null) { + return; + } markerIdsToRemove.forEach((markerId) { - if(markerId != null) { - final MarkerController markerController = _markerIdToController[markerId]; - if(markerController != null) { + if (markerId != null) { + final MarkerController markerController = + _markerIdToController[markerId]; + if (markerController != null) { markerController.remove(); _markerIdToController.remove(markerId.value); } @@ -102,10 +110,12 @@ class MarkersController extends AbstractController { } void _onMarkerDragEnd(MarkerId markerId, gmaps.LatLng latLng) { - _streamController.add(MarkerDragEndEvent(mapId, - _gmLatlngToLatlng(latLng), markerId,)); + _streamController.add(MarkerDragEndEvent( + mapId, + _gmLatlngToLatlng(latLng), + markerId, + )); } - } -typedef LatLngCallback = void Function(gmaps.LatLng latLng); \ No newline at end of file +typedef LatLngCallback = void Function(gmaps.LatLng latLng); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index 88d369053281..a7ad55b3b1b3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -1,29 +1,25 @@ part of google_maps_flutter_web; - /// class PolygonController { - gmaps.Polygon _polygon; bool consumeTapEvents = false; ui.VoidCallback onTap; /// - PolygonController({ - @required gmaps.Polygon polygon, - bool consumeTapEvents, - this.onTap - }){ + PolygonController( + {@required gmaps.Polygon polygon, bool consumeTapEvents, this.onTap}) { _polygon = polygon; - if(consumeTapEvents) { + if (consumeTapEvents) { polygon.onClick.listen((event) { - if(onTap !=null) onTap.call(); + if (onTap != null) onTap.call(); }); } } - - set polygon (gmaps.Polygon polygon) { _polygon = polygon; } + set polygon(gmaps.Polygon polygon) { + _polygon = polygon; + } void update(gmaps.PolygonOptions options) { _polygon.options = options; @@ -35,4 +31,4 @@ class PolygonController { _polygon = null; //_polygon.remove(); } -} \ No newline at end of file +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index d0067524387b..22f41bfbe00e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -1,32 +1,34 @@ part of google_maps_flutter_web; class PolygonsController extends AbstractController { - final Map _polygonIdToController; StreamController _streamController; PolygonsController({ @required StreamController stream, - }): _streamController = stream, _polygonIdToController = Map(); + }) : _streamController = stream, + _polygonIdToController = Map(); void addPolygons(Set polygonsToAdd) { - if(polygonsToAdd != null) { + if (polygonsToAdd != null) { polygonsToAdd.forEach((polygon) { _addPolygon(polygon); }); } } - void _addPolygon(Polygon polygon){ - if(polygon == null) return; - final populationOptions = _polygonOptionsFromPolygon(googleMap, polygon); - gmaps.Polygon gmPolygon = gmaps.Polygon(populationOptions); + void _addPolygon(Polygon polygon) { + if (polygon == null) return; + final populationOptions = _polygonOptionsFromPolygon(googleMap, polygon); + gmaps.Polygon gmPolygon = gmaps.Polygon(populationOptions); gmPolygon.map = googleMap; PolygonController controller = PolygonController( polygon: gmPolygon, - consumeTapEvents:polygon.consumeTapEvents, - onTap:(){ _onPolygonTap(polygon.polygonId);}); + consumeTapEvents: polygon.consumeTapEvents, + onTap: () { + _onPolygonTap(polygon.polygonId); + }); _polygonIdToController[polygon.polygonId] = controller; } @@ -39,21 +41,26 @@ class PolygonsController extends AbstractController { } void changePolygon(Polygon polygon) { - if (polygon == null) { return;} + if (polygon == null) { + return; + } - PolygonController polygonController = _polygonIdToController[polygon.polygonId]; + PolygonController polygonController = + _polygonIdToController[polygon.polygonId]; if (polygonController != null) { - polygonController.update( - _polygonOptionsFromPolygon(googleMap, polygon)); + polygonController.update(_polygonOptionsFromPolygon(googleMap, polygon)); } } void removePolygons(Set polygonIdsToRemove) { - if (polygonIdsToRemove == null) {return;} + if (polygonIdsToRemove == null) { + return; + } polygonIdsToRemove.forEach((polygonId) { - if(polygonId != null) { - final PolygonController polygonController = _polygonIdToController[polygonId]; - if(polygonController != null) { + if (polygonId != null) { + final PolygonController polygonController = + _polygonIdToController[polygonId]; + if (polygonController != null) { polygonController.remove(); _polygonIdToController.remove(polygonId.value); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index 653d4a1bdb35..446d0d59d69c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -1,28 +1,24 @@ part of google_maps_flutter_web; - class PolylineController { - gmaps.Polyline _polyline; bool consumeTapEvents = false; ui.VoidCallback onTap; /// - PolylineController({ - @required gmaps.Polyline polyline, - bool consumeTapEvents, - this.onTap - }){ + PolylineController( + {@required gmaps.Polyline polyline, bool consumeTapEvents, this.onTap}) { _polyline = polyline; - if(consumeTapEvents) { + if (consumeTapEvents) { polyline.onClick.listen((event) { - if(onTap !=null) onTap.call(); + if (onTap != null) onTap.call(); }); } } - - set polyline (gmaps.Polyline polyline) {_polyline = polyline; } + set polyline(gmaps.Polyline polyline) { + _polyline = polyline; + } void update(gmaps.PolylineOptions options) { _polyline.options = options; @@ -34,4 +30,4 @@ class PolylineController { _polyline = null; //_polyline.remove(); } -} \ No newline at end of file +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index 70af17af78b3..b5c0a48aae44 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -1,32 +1,34 @@ part of google_maps_flutter_web; class PolylinesController extends AbstractController { - final Map _polylineIdToController; StreamController _streamController; PolylinesController({ @required StreamController stream, - }): _streamController = stream, _polylineIdToController = Map(); + }) : _streamController = stream, + _polylineIdToController = Map(); void addPolylines(Set polylinesToAdd) { - if(polylinesToAdd != null) { + if (polylinesToAdd != null) { polylinesToAdd.forEach((polyline) { _addPolyline(polyline); }); } } - void _addPolyline(Polyline polyline){ - if(polyline == null) return; - final populationOptions = _polylineOptionsFromPolyline(googleMap, polyline); - gmaps.Polyline gmPolyline = gmaps.Polyline(populationOptions); + void _addPolyline(Polyline polyline) { + if (polyline == null) return; + final populationOptions = _polylineOptionsFromPolyline(googleMap, polyline); + gmaps.Polyline gmPolyline = gmaps.Polyline(populationOptions); gmPolyline.map = googleMap; PolylineController controller = PolylineController( polyline: gmPolyline, - consumeTapEvents:polyline.consumeTapEvents, - onTap:(){ _onPolylineTap(polyline.polylineId);}); + consumeTapEvents: polyline.consumeTapEvents, + onTap: () { + _onPolylineTap(polyline.polylineId); + }); _polylineIdToController[polyline.polylineId] = controller; } @@ -39,20 +41,26 @@ class PolylinesController extends AbstractController { } void changePolyline(Polyline polyline) { - if (polyline == null) { return;} - PolylineController polylineController = _polylineIdToController[polyline.polylineId]; + if (polyline == null) { + return; + } + PolylineController polylineController = + _polylineIdToController[polyline.polylineId]; if (polylineController != null) { - polylineController.update( - _polylineOptionsFromPolyline(googleMap, polyline)); + polylineController + .update(_polylineOptionsFromPolyline(googleMap, polyline)); } } void removePolylines(Set polylineIdsToRemove) { - if (polylineIdsToRemove == null) {return;} + if (polylineIdsToRemove == null) { + return; + } polylineIdsToRemove.forEach((polylineId) { - if(polylineId != null) { - final PolylineController polylineController = _polylineIdToController[polylineId]; - if(polylineController != null) { + if (polylineId != null) { + final PolylineController polylineController = + _polylineIdToController[polylineId]; + if (polylineController != null) { polylineController.remove(); _polylineIdToController.remove(polylineId); } From 998f3780565150e5e48dca25613d43f8fbadeeaa Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Mon, 27 Jul 2020 15:59:54 -0700 Subject: [PATCH 27/58] Test circle.dart --- .../lib/src/circle.dart | 32 +++++------ .../google_maps_flutter_web/pubspec.yaml | 5 +- .../google_maps_flutter_web/test/.gitignore | 8 +++ .../test/lib/main.dart | 5 ++ .../test/lib/src/circle_test.dart | 53 +++++++++++++++++++ .../google_maps_flutter_web/test/pubspec.yaml | 27 ++++++++++ .../test/web/index.html | 10 ++++ 7 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index abd7adab5602..1c40bcda66d6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -1,38 +1,38 @@ part of google_maps_flutter_web; -/// +/// The CircleController class wraps a Circle and its onTap behavior. class CircleController { gmaps.Circle _circle; - bool consumeTapEvents = false; + final bool _consumeTapEvents; - ui.VoidCallback onTap; - - /// - CircleController( - {@required gmaps.Circle circle, this.consumeTapEvents, this.onTap}) { - _circle = circle; - if (consumeTapEvents) { - circle.onClick.listen((event) { - if (onTap != null) onTap.call(); + /// Creates a CircleController, that wraps a Circle object and its onTap behavior. + CircleController({ + @required gmaps.Circle circle, + bool consumeTapEvents = false, + ui.VoidCallback onTap, + }) : _circle = circle, + _consumeTapEvents = consumeTapEvents { + if (_consumeTapEvents) { + circle.onClick.listen((_) { + onTap?.call(); }); } } - set circle(gmaps.Circle circle) { - _circle = circle; - } + /// Returns [true] if this Controller will use its own onTap handler to consume events. + bool get consumeTapEvents => _consumeTapEvents; - ///TODO + /// Updates the options of the wrapped [gmaps.Circle] object. void update(gmaps.CircleOptions options) { _circle.options = options; } + /// Disposes of the currently wrapped [gmaps.Circle]. void remove() { _circle.visible = false; _circle.radius = 0; _circle.map = null; _circle = null; - //_circle.remove(); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 8d766c81c0dc..9cd2b8c3c553 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: sdk: flutter meta: ^1.1.7 google_maps_flutter_platform_interface: ^1.0.1 - google_maps: ">=3.0.0 <4.0.0" + google_maps: ^3.0.0 stream_transform: ^1.2.0 dev_dependencies: @@ -25,8 +25,7 @@ dev_dependencies: sdk: flutter url_launcher: ^5.2.5 pedantic: ^1.8.0 - google_maps_flutter: - path: ../google_maps_flutter + mockito: ^4.1.1 environment: sdk: ">=2.3.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore b/packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore new file mode 100644 index 000000000000..d7dee828a6b9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ +lib/generated_plugin_registrant.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart new file mode 100644 index 000000000000..6e4a0efb3c4a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart @@ -0,0 +1,5 @@ +import 'src/circle_test.dart'; + +void main() { + circleTests(); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart new file mode 100644 index 000000000000..266a45a47d4a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart @@ -0,0 +1,53 @@ +// @TestOn('chrome') // Uses web-only Flutter SDKs... + +import 'dart:async'; + +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class _MockCircle extends Mock implements gmaps.Circle { + final onClickController = StreamController(); + @override + Stream get onClick => onClickController.stream; +} + +/// Test Circle +void circleTests() { + group('Circle', () { + _MockCircle circle; + bool called = false; + void onTap() { + called = true; + } + + setUp(() { + called = false; + circle = _MockCircle(); + }); + + test('_consumeTapEvents true', () async { + CircleController(circle: circle, consumeTapEvents: true, onTap: onTap); + expect(circle.onClickController.hasListener, isTrue); + // Simulate a click + await circle.onClickController.add(null); + expect(called, isTrue); + }); + + test('_consumeTapEvents false', () async { + CircleController(circle: circle, consumeTapEvents: false, onTap: onTap); + expect(circle.onClickController.hasListener, isFalse); + // Simulate a click + await circle.onClickController.add(null); + expect(called, isFalse); + }); + + test('update', () { + final controller = CircleController(circle: circle); + final options = gmaps.CircleOptions()..draggable = false; + controller.update(options); + verify(circle.options = options); + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml new file mode 100644 index 000000000000..8b8abd887980 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml @@ -0,0 +1,27 @@ +name: google_maps_flutter_web_tests +description: Tests web app for the google_maps_flutter_web plugin +version: 0.1.0 +homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_web + +dependencies: + google_maps_flutter_web: + path: ../ + js: ^0.6.1+1 + flutter_web_plugins: + sdk: flutter + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_driver: + sdk: flutter + e2e: + path: ../../../e2e + mockito: ^4.1.1 + +environment: + sdk: ">=2.6.0 <3.0.0" + flutter: ">=1.12.13+hotfix.4" + diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html b/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html new file mode 100644 index 000000000000..6eff9a740d43 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html @@ -0,0 +1,10 @@ + + + + + example + + + + + From b9144abf24e65510d8107d66a90d586f920fb89c Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 28 Jul 2020 10:40:48 -0700 Subject: [PATCH 28/58] Format and add tests to Polyline and PolygonControllers --- .../lib/src/polygon.dart | 27 ++-- .../lib/src/polyline.dart | 24 ++-- .../test/lib/main.dart | 4 +- .../test/lib/src/circle_test.dart | 53 ------- .../test/lib/src/shape_test.dart | 134 ++++++++++++++++++ 5 files changed, 165 insertions(+), 77 deletions(-) delete mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index a7ad55b3b1b3..072aba0188ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -1,34 +1,37 @@ part of google_maps_flutter_web; -/// +/// The PolygonController class wraps a Polygon and its onTap behavior. class PolygonController { gmaps.Polygon _polygon; - bool consumeTapEvents = false; - ui.VoidCallback onTap; - /// - PolygonController( - {@required gmaps.Polygon polygon, bool consumeTapEvents, this.onTap}) { - _polygon = polygon; - if (consumeTapEvents) { + final bool _consumeTapEvents; + + /// Creates a PolygonController, that wraps a Polygon object and its onTap behavior. + PolygonController({ + @required gmaps.Polygon polygon, + bool consumeTapEvents = false, + ui.VoidCallback onTap, + }) : _polygon = polygon, + _consumeTapEvents = consumeTapEvents { + if (_consumeTapEvents) { polygon.onClick.listen((event) { if (onTap != null) onTap.call(); }); } } - set polygon(gmaps.Polygon polygon) { - _polygon = polygon; - } + /// Returns [true] if this Controller will use its own onTap handler to consume events. + bool get consumeTapEvents => _consumeTapEvents; + /// Updates the options of the wrapped [gmaps.Polygon] object. void update(gmaps.PolygonOptions options) { _polygon.options = options; } + /// Disposes of the currently wrapped [gmaps.Polygon]. void remove() { _polygon.visible = false; _polygon.map = null; _polygon = null; - //_polygon.remove(); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index 446d0d59d69c..58f9995b76f4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -1,14 +1,18 @@ part of google_maps_flutter_web; +/// The PolygonController class wraps a [gmaps.Polyline] and its onTap behavior. class PolylineController { gmaps.Polyline _polyline; - bool consumeTapEvents = false; - ui.VoidCallback onTap; - /// - PolylineController( - {@required gmaps.Polyline polyline, bool consumeTapEvents, this.onTap}) { - _polyline = polyline; + final bool _consumeTapEvents; + + /// Creates a PolylineController, that wraps a Polyline object and its onTap behavior. + PolylineController({ + @required gmaps.Polyline polyline, + bool consumeTapEvents = false, + ui.VoidCallback onTap, + }) : _polyline = polyline, + _consumeTapEvents = consumeTapEvents { if (consumeTapEvents) { polyline.onClick.listen((event) { if (onTap != null) onTap.call(); @@ -16,18 +20,18 @@ class PolylineController { } } - set polyline(gmaps.Polyline polyline) { - _polyline = polyline; - } + /// Returns [true] if this Controller will use its own onTap handler to consume events. + bool get consumeTapEvents => _consumeTapEvents; + /// Updates the options of the wrapped Polyline object. void update(gmaps.PolylineOptions options) { _polyline.options = options; } + /// Disposes of the currently wrapped Polyline. void remove() { _polyline.visible = false; _polyline.map = null; _polyline = null; - //_polyline.remove(); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart index 6e4a0efb3c4a..84b36ee944a7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart @@ -1,5 +1,5 @@ -import 'src/circle_test.dart'; +import 'src/shape_test.dart'; void main() { - circleTests(); + shapeTests(); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart deleted file mode 100644 index 266a45a47d4a..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/circle_test.dart +++ /dev/null @@ -1,53 +0,0 @@ -// @TestOn('chrome') // Uses web-only Flutter SDKs... - -import 'dart:async'; - -import 'package:google_maps/google_maps.dart' as gmaps; -import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -class _MockCircle extends Mock implements gmaps.Circle { - final onClickController = StreamController(); - @override - Stream get onClick => onClickController.stream; -} - -/// Test Circle -void circleTests() { - group('Circle', () { - _MockCircle circle; - bool called = false; - void onTap() { - called = true; - } - - setUp(() { - called = false; - circle = _MockCircle(); - }); - - test('_consumeTapEvents true', () async { - CircleController(circle: circle, consumeTapEvents: true, onTap: onTap); - expect(circle.onClickController.hasListener, isTrue); - // Simulate a click - await circle.onClickController.add(null); - expect(called, isTrue); - }); - - test('_consumeTapEvents false', () async { - CircleController(circle: circle, consumeTapEvents: false, onTap: onTap); - expect(circle.onClickController.hasListener, isFalse); - // Simulate a click - await circle.onClickController.add(null); - expect(called, isFalse); - }); - - test('update', () { - final controller = CircleController(circle: circle); - final options = gmaps.CircleOptions()..draggable = false; - controller.update(options); - verify(circle.options = options); - }); - }); -} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart new file mode 100644 index 000000000000..69ad0d89d303 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart @@ -0,0 +1,134 @@ +@TestOn('chrome') // Uses web-only Flutter SDKs... + +import 'dart:async'; + +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class _MockCircle extends Mock implements gmaps.Circle { + final onClickController = StreamController(); + @override + Stream get onClick => onClickController.stream; +} + +class _MockPolygon extends Mock implements gmaps.Polygon { + final onClickController = StreamController(); + @override + Stream get onClick => onClickController.stream; +} + +class _MockPolyline extends Mock implements gmaps.Polyline { + final onClickController = StreamController(); + @override + Stream get onClick => onClickController.stream; +} + +/// Test Shapes (Circle, Polygon, Polyline) +void shapeTests() { + bool called = false; + void onTap() { + called = true; + } + + setUp(() { + called = false; + }); + + group('CircleController', () { + _MockCircle circle; + + setUp(() { + circle = _MockCircle(); + }); + + test('_consumeTapEvents true', () async { + CircleController(circle: circle, consumeTapEvents: true, onTap: onTap); + expect(circle.onClickController.hasListener, isTrue); + // Simulate a click + await circle.onClickController.add(null); + expect(called, isTrue); + }); + + test('_consumeTapEvents false', () async { + CircleController(circle: circle, consumeTapEvents: false, onTap: onTap); + expect(circle.onClickController.hasListener, isFalse); + // Simulate a click + await circle.onClickController.add(null); + expect(called, isFalse); + }); + + test('update', () { + final controller = CircleController(circle: circle); + final options = gmaps.CircleOptions()..draggable = false; + controller.update(options); + verify(circle.options = options); + }); + }); + + group('PolygonController', () { + _MockPolygon polygon; + + setUp(() { + polygon = _MockPolygon(); + }); + + test('_consumeTapEvents true', () async { + PolygonController(polygon: polygon, consumeTapEvents: true, onTap: onTap); + expect(polygon.onClickController.hasListener, isTrue); + // Simulate a click + await polygon.onClickController.add(null); + expect(called, isTrue); + }); + + test('_consumeTapEvents false', () async { + PolygonController( + polygon: polygon, consumeTapEvents: false, onTap: onTap); + expect(polygon.onClickController.hasListener, isFalse); + // Simulate a click + await polygon.onClickController.add(null); + expect(called, isFalse); + }); + + test('update', () { + final controller = PolygonController(polygon: polygon); + final options = gmaps.PolygonOptions()..draggable = false; + controller.update(options); + verify(polygon.options = options); + }); + }); + + group('PolylineController', () { + _MockPolyline polyline; + + setUp(() { + polyline = _MockPolyline(); + }); + + test('_consumeTapEvents true', () async { + PolylineController( + polyline: polyline, consumeTapEvents: true, onTap: onTap); + expect(polyline.onClickController.hasListener, isTrue); + // Simulate a click + await polyline.onClickController.add(null); + expect(called, isTrue); + }); + + test('_consumeTapEvents false', () async { + PolylineController( + polyline: polyline, consumeTapEvents: false, onTap: onTap); + expect(polyline.onClickController.hasListener, isFalse); + // Simulate a click + await polyline.onClickController.add(null); + expect(called, isFalse); + }); + + test('update', () { + final controller = PolylineController(polyline: polyline); + final options = gmaps.PolylineOptions()..draggable = false; + controller.update(options); + verify(polyline.options = options); + }); + }); +} From d21903861b106f3547ec053a5b87c7e7c8c6ff59 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 28 Jul 2020 13:34:42 -0700 Subject: [PATCH 29/58] Format Marker and add tests --- .../lib/src/circle.dart | 4 +- .../lib/src/marker.dart | 72 +++++------ .../lib/src/markers.dart | 42 +++---- .../lib/src/polygon.dart | 4 +- .../lib/src/polyline.dart | 4 +- .../test/lib/main.dart | 2 + .../test/lib/src/marker_test.dart | 114 ++++++++++++++++++ 7 files changed, 176 insertions(+), 66 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 1c40bcda66d6..3a0451afa2c4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -13,9 +13,9 @@ class CircleController { ui.VoidCallback onTap, }) : _circle = circle, _consumeTapEvents = consumeTapEvents { - if (_consumeTapEvents) { + if (_consumeTapEvents && onTap != null) { circle.onClick.listen((_) { - onTap?.call(); + onTap.call(); }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 4473592cb315..1a7ca6fb6403 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -1,68 +1,70 @@ part of google_maps_flutter_web; +typedef LatLngCallback = void Function(gmaps.LatLng latLng); + +/// This class wraps a [gmaps.Marker], how it handles events, and its associated [gmaps.InfoWindow] widget (optional). class MarkerController { gmaps.Marker _marker; - gmaps.InfoWindow infoWindow; - bool consumeTapEvents = false; - ui.VoidCallback onTap; - ui.VoidCallback onInfoWindowTap; - LatLngCallback onDragEnd; - bool infoWindowShown = false; - /// + final bool _consumeTapEvents; + + final gmaps.InfoWindow _infoWindow; + + bool _infoWindowShown = false; + + /// Creates a MarkerController, that wraps a Marker object, its onTap/Drag behavior, and its associated InfoWindow. MarkerController({ @required gmaps.Marker marker, - this.infoWindow, - this.consumeTapEvents, - this.onTap, - this.onDragEnd, - this.onInfoWindowTap, - }) { - _marker = marker; - if (consumeTapEvents) {} - if (onTap != null) { + gmaps.InfoWindow infoWindow, + bool consumeTapEvents = false, + LatLngCallback onDragEnd, + ui.VoidCallback onTap, + }) : _marker = marker, + _infoWindow = infoWindow, + _consumeTapEvents = consumeTapEvents { + if (consumeTapEvents && onTap != null) { marker.onClick.listen((event) { onTap.call(); }); } - if (_marker.draggable) { + if ((_marker?.draggable ?? false) && onDragEnd != null) { marker.onDragend.listen((event) { - if (onDragEnd != null) onDragEnd.call(event.latLng); + onDragEnd.call(event.latLng); }); } - if (onInfoWindowTap != null) { - infoWindow.addListener('click', onInfoWindowTap); - } } - set marker(gmaps.Marker marker) { - _marker = marker; - } + /// Returns [true] if this Controller will use its own onTap handler to consume events. + bool get consumeTapEvents => _consumeTapEvents; + + /// Returns [true] if the InfoWindow associated to this marker is being shown. + bool get infoWindowShown => _infoWindowShown; + /// Updates the options of the wrapped [gmaps.Polygon] object. void update(gmaps.MarkerOptions options) { _marker.options = options; } + /// Disposes of the currently wrapped Marker. void remove() { _marker.visible = false; _marker.map = null; _marker = null; - //_marker.remove(); } + /// Hide the associated InfoWindow. void hideInfoWindow() { - if (infoWindow != null) { - infoWindow.close(); - infoWindowShown = false; + if (_infoWindow != null) { + _infoWindow.close(); + _infoWindowShown = false; } } - void showMarkerInfoWindow() { - infoWindow.open(_marker.map); - infoWindowShown = true; - } - - bool isInfoWindowShown() { - return infoWindowShown; + /// Show the associated InfoWindow. + void showInfoWindow() { + if (_infoWindow != null) { + _infoWindow.open(_marker.map); + _infoWindowShown = true; + } } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index ff32b767eb14..dc8a5863f5dc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -21,23 +21,24 @@ class MarkersController extends AbstractController { void _addMarker(Marker marker) { if (marker == null) return; final infoWindoOptions = _infoWindowOPtionsFromMarker(marker); - gmaps.InfoWindow gmInfoWindow = gmaps.InfoWindow(infoWindoOptions); + gmaps.InfoWindow gmInfoWindow = gmaps.InfoWindow(infoWindoOptions) + ..addListener('click', () { + _onInfoWindowTap(marker.markerId); + }); final populationOptions = _markerOptionsFromMarker(googleMap, marker); gmaps.Marker gmMarker = gmaps.Marker(populationOptions); gmMarker.map = googleMap; MarkerController controller = MarkerController( - marker: gmMarker, - infoWindow: gmInfoWindow, - consumeTapEvents: marker.consumeTapEvents, - onTap: () { - _onMarkerTap(marker.markerId); - }, - onDragEnd: (gmaps.LatLng latLng) { - _onMarkerDragEnd(marker.markerId, latLng); - }, - onInfoWindowTap: () { - _onInfoWindowTap(marker.markerId); - }); + marker: gmMarker, + infoWindow: gmInfoWindow, + consumeTapEvents: marker.consumeTapEvents, + onTap: () { + _onMarkerTap(marker.markerId); + }, + onDragEnd: (gmaps.LatLng latLng) { + _onMarkerDragEnd(marker.markerId, latLng); + }, + ); _markerIdToController[marker.markerId] = controller; } @@ -77,24 +78,17 @@ class MarkersController extends AbstractController { void showMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; - if (markerController != null) { - markerController.showMarkerInfoWindow(); - } + markerController?.showInfoWindow(); } bool isInfoWindowShown(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; - if (markerController != null) { - return markerController.isInfoWindowShown(); - } - return false; + return markerController?.infoWindowShown ?? false; } void hideMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; - if (markerController != null) { - markerController.hideInfoWindow(); - } + markerController?.hideInfoWindow(); } // Handle internal events @@ -117,5 +111,3 @@ class MarkersController extends AbstractController { )); } } - -typedef LatLngCallback = void Function(gmaps.LatLng latLng); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index 072aba0188ed..6e18c906efe1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -13,9 +13,9 @@ class PolygonController { ui.VoidCallback onTap, }) : _polygon = polygon, _consumeTapEvents = consumeTapEvents { - if (_consumeTapEvents) { + if (_consumeTapEvents && onTap != null) { polygon.onClick.listen((event) { - if (onTap != null) onTap.call(); + onTap.call(); }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index 58f9995b76f4..633eaa08d6e9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -13,9 +13,9 @@ class PolylineController { ui.VoidCallback onTap, }) : _polyline = polyline, _consumeTapEvents = consumeTapEvents { - if (consumeTapEvents) { + if (consumeTapEvents && onTap != null) { polyline.onClick.listen((event) { - if (onTap != null) onTap.call(); + onTap.call(); }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart index 84b36ee944a7..e53b460ef694 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart @@ -1,5 +1,7 @@ import 'src/shape_test.dart'; +import 'src/marker_test.dart'; void main() { shapeTests(); + markerTests(); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart new file mode 100644 index 000000000000..c048288b1a21 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart @@ -0,0 +1,114 @@ +@TestOn('chrome') // Uses web-only Flutter SDKs... + +import 'dart:async'; + +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class _MockMarker extends Mock implements gmaps.Marker { + final onClickController = StreamController(); + final onDragEndController = StreamController(); + + @override + Stream get onClick => onClickController.stream; + + @override + Stream get onDragend => onDragEndController.stream; +} + +class _MockMouseEvent extends Mock implements gmaps.MouseEvent {} + +class _MockInfoWindow extends Mock implements gmaps.InfoWindow {} + +final gmaps.LatLng _nullIsland = gmaps.LatLng(0, 0); + +/// Test Shapes (Circle, Polygon, Polyline) +void markerTests() { + bool called = false; + void onTap() { + called = true; + } + + void onDragEnd(gmaps.LatLng _) { + called = true; + } + + setUp(() { + called = false; + }); + + group('MarkerController', () { + _MockMarker marker; + + setUp(() { + marker = _MockMarker(); + }); + + test('_consumeTapEvents true', () async { + MarkerController(marker: marker, consumeTapEvents: true, onTap: onTap); + expect(marker.onClickController.hasListener, isTrue); + // Simulate a click + await marker.onClickController.add(null); + expect(called, isTrue); + }); + + test('_consumeTapEvents false', () async { + MarkerController(marker: marker, consumeTapEvents: false, onTap: onTap); + expect(marker.onClickController.hasListener, isFalse); + // Simulate a click + await marker.onClickController.add(null); + expect(called, isFalse); + }); + + test('marker.draggable true', () async { + when(marker.draggable).thenReturn(true); + MarkerController(marker: marker, onDragEnd: onDragEnd); + expect(marker.onDragEndController.hasListener, isTrue); + // Simulate a click + await marker.onDragEndController.add(_MockMouseEvent()); + expect(called, isTrue); + }); + + test('marker.draggable false', () async { + when(marker.draggable).thenReturn(false); + MarkerController(marker: marker, onDragEnd: onDragEnd); + expect(marker.onDragEndController.hasListener, isFalse); + // Simulate a click + await marker.onDragEndController.add(null); + expect(called, isFalse); + }); + + test('update', () { + final controller = MarkerController(marker: marker); + final options = gmaps.MarkerOptions()..draggable = false; + controller.update(options); + verify(marker.options = options); + }); + + test('infoWindow null, showInfoWindow.', () { + final controller = MarkerController(marker: marker); + controller.showInfoWindow(); + expect(controller.infoWindowShown, isFalse); + }); + + test('showInfoWindow', () { + final infoWindow = _MockInfoWindow(); + final controller = + MarkerController(marker: marker, infoWindow: infoWindow); + controller.showInfoWindow(); + verify(infoWindow.open(any)).called(1); + expect(controller.infoWindowShown, isTrue); + }); + + test('hideInfoWindow', () { + final infoWindow = _MockInfoWindow(); + final controller = + MarkerController(marker: marker, infoWindow: infoWindow); + controller.hideInfoWindow(); + verify(infoWindow.close()).called(1); + expect(controller.infoWindowShown, isFalse); + }); + }); +} From 8798c5e11bc19ad3a2b798c9a9bdb5006a809287 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 29 Jul 2020 10:28:55 -0700 Subject: [PATCH 30/58] Make onTap independent from consumeTapEvents --- .../google_maps_flutter_web/README.md | 2 ++ .../lib/src/circle.dart | 2 +- .../lib/src/marker.dart | 9 +++--- .../lib/src/polygon.dart | 2 +- .../lib/src/polyline.dart | 2 +- .../test/lib/src/marker_test.dart | 27 +++------------- .../test/lib/src/shape_test.dart | 32 ++----------------- 7 files changed, 17 insertions(+), 59 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index 71fb22b6aaa3..c99d3a85c356 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -46,4 +46,6 @@ There's no "My Location" widget in web (for now), so the following options are i * `myLocationButtonEnabled` ([doable](https://developers.google.com/maps/documentation/javascript/examples/control-custom)) * `myLocationEnabled` ([doable](https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html)) +There's no `defaultMarkerWithHue` in web. If you want colored pins/markers, you should use your own asset images. + Traffic, indoor and building layers are still not available on the web. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 3a0451afa2c4..c513c4f4dfd7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -13,7 +13,7 @@ class CircleController { ui.VoidCallback onTap, }) : _circle = circle, _consumeTapEvents = consumeTapEvents { - if (_consumeTapEvents && onTap != null) { + if (onTap != null) { circle.onClick.listen((_) { onTap.call(); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 1a7ca6fb6403..cbb200752113 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -22,13 +22,14 @@ class MarkerController { }) : _marker = marker, _infoWindow = infoWindow, _consumeTapEvents = consumeTapEvents { - if (consumeTapEvents && onTap != null) { - marker.onClick.listen((event) { + if (onTap != null) { + _marker.onClick.listen((event) { onTap.call(); }); } - if ((_marker?.draggable ?? false) && onDragEnd != null) { - marker.onDragend.listen((event) { + if (onDragEnd != null) { + _marker.onDragend.listen((event) { + _marker.position = event.latLng; onDragEnd.call(event.latLng); }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index 6e18c906efe1..e4bf722e5623 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -13,7 +13,7 @@ class PolygonController { ui.VoidCallback onTap, }) : _polygon = polygon, _consumeTapEvents = consumeTapEvents { - if (_consumeTapEvents && onTap != null) { + if (onTap != null) { polygon.onClick.listen((event) { onTap.call(); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index 633eaa08d6e9..d0f939a99e75 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -13,7 +13,7 @@ class PolylineController { ui.VoidCallback onTap, }) : _polyline = polyline, _consumeTapEvents = consumeTapEvents { - if (consumeTapEvents && onTap != null) { + if (onTap != null) { polyline.onClick.listen((event) { onTap.call(); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart index c048288b1a21..49ebc61d6696 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart @@ -46,40 +46,21 @@ void markerTests() { marker = _MockMarker(); }); - test('_consumeTapEvents true', () async { - MarkerController(marker: marker, consumeTapEvents: true, onTap: onTap); - expect(marker.onClickController.hasListener, isTrue); + test('onTap gets called', () async { + MarkerController(marker: marker, onTap: onTap); // Simulate a click await marker.onClickController.add(null); expect(called, isTrue); }); - test('_consumeTapEvents false', () async { - MarkerController(marker: marker, consumeTapEvents: false, onTap: onTap); - expect(marker.onClickController.hasListener, isFalse); - // Simulate a click - await marker.onClickController.add(null); - expect(called, isFalse); - }); - - test('marker.draggable true', () async { + test('onDragEnd gets called', () async { when(marker.draggable).thenReturn(true); MarkerController(marker: marker, onDragEnd: onDragEnd); - expect(marker.onDragEndController.hasListener, isTrue); - // Simulate a click + // Simulate a drag end await marker.onDragEndController.add(_MockMouseEvent()); expect(called, isTrue); }); - test('marker.draggable false', () async { - when(marker.draggable).thenReturn(false); - MarkerController(marker: marker, onDragEnd: onDragEnd); - expect(marker.onDragEndController.hasListener, isFalse); - // Simulate a click - await marker.onDragEndController.add(null); - expect(called, isFalse); - }); - test('update', () { final controller = MarkerController(marker: marker); final options = gmaps.MarkerOptions()..draggable = false; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart index 69ad0d89d303..2d52eca7c641 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart @@ -43,7 +43,7 @@ void shapeTests() { circle = _MockCircle(); }); - test('_consumeTapEvents true', () async { + test('onTap gets called', () async { CircleController(circle: circle, consumeTapEvents: true, onTap: onTap); expect(circle.onClickController.hasListener, isTrue); // Simulate a click @@ -51,14 +51,6 @@ void shapeTests() { expect(called, isTrue); }); - test('_consumeTapEvents false', () async { - CircleController(circle: circle, consumeTapEvents: false, onTap: onTap); - expect(circle.onClickController.hasListener, isFalse); - // Simulate a click - await circle.onClickController.add(null); - expect(called, isFalse); - }); - test('update', () { final controller = CircleController(circle: circle); final options = gmaps.CircleOptions()..draggable = false; @@ -74,7 +66,7 @@ void shapeTests() { polygon = _MockPolygon(); }); - test('_consumeTapEvents true', () async { + test('onTap gets called', () async { PolygonController(polygon: polygon, consumeTapEvents: true, onTap: onTap); expect(polygon.onClickController.hasListener, isTrue); // Simulate a click @@ -82,15 +74,6 @@ void shapeTests() { expect(called, isTrue); }); - test('_consumeTapEvents false', () async { - PolygonController( - polygon: polygon, consumeTapEvents: false, onTap: onTap); - expect(polygon.onClickController.hasListener, isFalse); - // Simulate a click - await polygon.onClickController.add(null); - expect(called, isFalse); - }); - test('update', () { final controller = PolygonController(polygon: polygon); final options = gmaps.PolygonOptions()..draggable = false; @@ -106,7 +89,7 @@ void shapeTests() { polyline = _MockPolyline(); }); - test('_consumeTapEvents true', () async { + test('onTap gets called', () async { PolylineController( polyline: polyline, consumeTapEvents: true, onTap: onTap); expect(polyline.onClickController.hasListener, isTrue); @@ -115,15 +98,6 @@ void shapeTests() { expect(called, isTrue); }); - test('_consumeTapEvents false', () async { - PolylineController( - polyline: polyline, consumeTapEvents: false, onTap: onTap); - expect(polyline.onClickController.hasListener, isFalse); - // Simulate a click - await polyline.onClickController.add(null); - expect(called, isFalse); - }); - test('update', () { final controller = PolylineController(polyline: polyline); final options = gmaps.PolylineOptions()..draggable = false; From eadab635b17a43f8ec0407d86a457ed569c793c4 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 29 Jul 2020 15:14:04 -0700 Subject: [PATCH 31/58] Convert tests in e2e tests with flutter driver --- .../google_maps_flutter_web/test/.gitignore | 8 ----- .../google_maps_flutter_web/test/README.md | 17 +++++++++++ .../test/lib/main.dart | 23 ++++++++++++--- .../google_maps_flutter_web/test/pubspec.yaml | 29 ++++++++----------- .../google_maps_flutter_web/test/run_test | 17 +++++++++++ .../marker_e2e.dart} | 22 +++++++------- .../test/test_driver/marker_e2e_test.dart | 7 +++++ .../shape_e2e.dart} | 19 ++++++------ .../test/test_driver/shape_e2e_test.dart | 7 +++++ .../test/web/index.html | 19 +++++++----- 10 files changed, 111 insertions(+), 57 deletions(-) delete mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/README.md create mode 100755 packages/google_maps_flutter/google_maps_flutter_web/test/run_test rename packages/google_maps_flutter/google_maps_flutter_web/test/{lib/src/marker_test.dart => test_driver/marker_e2e.dart} (81%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart rename packages/google_maps_flutter/google_maps_flutter_web/test/{lib/src/shape_test.dart => test_driver/shape_e2e.dart} (85%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore b/packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore deleted file mode 100644 index d7dee828a6b9..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -.dart_tool/ - -.packages -.pub/ - -build/ -lib/generated_plugin_registrant.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/README.md b/packages/google_maps_flutter/google_maps_flutter_web/test/README.md new file mode 100644 index 000000000000..f623e9fd08d3 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/README.md @@ -0,0 +1,17 @@ +# Running browser_tests + +Make sure you have updated to the latest Flutter master. + +1. Check what version of Chrome is running on the machine you're running tests on. + +2. Download and install driver for that version from here: + * + +3. Start the driver using `chromedriver --port=4444` + +4. Change into the `test` directory of your clone. + +5. Run tests: `flutter drive -d web-server --release --browser-name=chrome --target=test_driver/TEST_NAME_e2e.dart`, or (in Linux): + + * Single: `./run_test test_driver/TEST_NAME_e2e.dart` + * All: `./run_test` diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart index e53b460ef694..4c55f6c95a7a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart @@ -1,7 +1,22 @@ -import 'src/shape_test.dart'; -import 'src/marker_test.dart'; +// 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 'package:flutter/material.dart'; void main() { - shapeTests(); - markerTests(); + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + MyAppState createState() => MyAppState(); } + +class MyAppState extends State { + @override + Widget build(BuildContext context) { + return Text('Testing... Look at the console output for results!'); + } +} + diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml index 8b8abd887980..ec091f00a56f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/pubspec.yaml @@ -1,27 +1,22 @@ -name: google_maps_flutter_web_tests -description: Tests web app for the google_maps_flutter_web plugin -version: 0.1.0 -homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_web +name: regular_integration_tests +publish_to: none + +environment: + sdk: ">=2.2.2 <3.0.0" dependencies: - google_maps_flutter_web: - path: ../ - js: ^0.6.1+1 - flutter_web_plugins: - sdk: flutter flutter: sdk: flutter dev_dependencies: - flutter_test: - sdk: flutter + google_maps_flutter_web: + path: ../ + google_maps: ^3.4.4 flutter_driver: sdk: flutter - e2e: - path: ../../../e2e + flutter_test: + sdk: flutter + e2e: ^0.6.1 + http: ^0.12.2 mockito: ^4.1.1 -environment: - sdk: ">=2.6.0 <3.0.0" - flutter: ">=1.12.13+hotfix.4" - diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/run_test b/packages/google_maps_flutter/google_maps_flutter_web/test/run_test new file mode 100755 index 000000000000..3a20948c360a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/run_test @@ -0,0 +1,17 @@ +#!/usr/bin/bash +if pgrep -lf chromedriver > /dev/null; then + echo "chromedriver is running." + + if [ $# -eq 0 ]; then + echo "No target specified, running all tests..." + find test_driver/ -iname *_e2e.dart | xargs -n1 -i -t flutter drive -d web-server --release --browser-name=chrome --target='{}' + else + echo "Running test target: $1..." + set -x + flutter drive -d web-server --release --browser-name=chrome --target=$1 + fi + + else + echo "chromedriver is not running." +fi + diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart similarity index 81% rename from packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart rename to packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart index 49ebc61d6696..f912cb0e7af3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart @@ -1,7 +1,6 @@ -@TestOn('chrome') // Uses web-only Flutter SDKs... - import 'dart:async'; +import 'package:e2e/e2e.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -22,10 +21,11 @@ class _MockMouseEvent extends Mock implements gmaps.MouseEvent {} class _MockInfoWindow extends Mock implements gmaps.InfoWindow {} -final gmaps.LatLng _nullIsland = gmaps.LatLng(0, 0); -/// Test Shapes (Circle, Polygon, Polyline) -void markerTests() { +/// Test Markers +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + bool called = false; void onTap() { called = true; @@ -46,14 +46,14 @@ void markerTests() { marker = _MockMarker(); }); - test('onTap gets called', () async { + testWidgets('onTap gets called', (WidgetTester tester) async { MarkerController(marker: marker, onTap: onTap); // Simulate a click await marker.onClickController.add(null); expect(called, isTrue); }); - test('onDragEnd gets called', () async { + testWidgets('onDragEnd gets called', (WidgetTester tester) async { when(marker.draggable).thenReturn(true); MarkerController(marker: marker, onDragEnd: onDragEnd); // Simulate a drag end @@ -61,20 +61,20 @@ void markerTests() { expect(called, isTrue); }); - test('update', () { + testWidgets('update', (WidgetTester tester) async { final controller = MarkerController(marker: marker); final options = gmaps.MarkerOptions()..draggable = false; controller.update(options); verify(marker.options = options); }); - test('infoWindow null, showInfoWindow.', () { + testWidgets('infoWindow null, showInfoWindow.', (WidgetTester tester) async { final controller = MarkerController(marker: marker); controller.showInfoWindow(); expect(controller.infoWindowShown, isFalse); }); - test('showInfoWindow', () { + testWidgets('showInfoWindow', (WidgetTester tester) async { final infoWindow = _MockInfoWindow(); final controller = MarkerController(marker: marker, infoWindow: infoWindow); @@ -83,7 +83,7 @@ void markerTests() { expect(controller.infoWindowShown, isTrue); }); - test('hideInfoWindow', () { + testWidgets('hideInfoWindow', (WidgetTester tester) async { final infoWindow = _MockInfoWindow(); final controller = MarkerController(marker: marker, infoWindow: infoWindow); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart @@ -0,0 +1,7 @@ +// 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 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e.dart similarity index 85% rename from packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart rename to packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e.dart index 2d52eca7c641..c29707625770 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/src/shape_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e.dart @@ -1,7 +1,6 @@ -@TestOn('chrome') // Uses web-only Flutter SDKs... - import 'dart:async'; +import 'package:e2e/e2e.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -26,7 +25,9 @@ class _MockPolyline extends Mock implements gmaps.Polyline { } /// Test Shapes (Circle, Polygon, Polyline) -void shapeTests() { +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + bool called = false; void onTap() { called = true; @@ -43,7 +44,7 @@ void shapeTests() { circle = _MockCircle(); }); - test('onTap gets called', () async { + testWidgets('onTap gets called', (WidgetTester tester) async { CircleController(circle: circle, consumeTapEvents: true, onTap: onTap); expect(circle.onClickController.hasListener, isTrue); // Simulate a click @@ -51,7 +52,7 @@ void shapeTests() { expect(called, isTrue); }); - test('update', () { + testWidgets('update', (WidgetTester tester) async { final controller = CircleController(circle: circle); final options = gmaps.CircleOptions()..draggable = false; controller.update(options); @@ -66,7 +67,7 @@ void shapeTests() { polygon = _MockPolygon(); }); - test('onTap gets called', () async { + testWidgets('onTap gets called', (WidgetTester tester) async { PolygonController(polygon: polygon, consumeTapEvents: true, onTap: onTap); expect(polygon.onClickController.hasListener, isTrue); // Simulate a click @@ -74,7 +75,7 @@ void shapeTests() { expect(called, isTrue); }); - test('update', () { + testWidgets('update', (WidgetTester tester) async { final controller = PolygonController(polygon: polygon); final options = gmaps.PolygonOptions()..draggable = false; controller.update(options); @@ -89,7 +90,7 @@ void shapeTests() { polyline = _MockPolyline(); }); - test('onTap gets called', () async { + testWidgets('onTap gets called', (WidgetTester tester) async { PolylineController( polyline: polyline, consumeTapEvents: true, onTap: onTap); expect(polyline.onClickController.hasListener, isTrue); @@ -98,7 +99,7 @@ void shapeTests() { expect(called, isTrue); }); - test('update', () { + testWidgets('update', (WidgetTester tester) async { final controller = PolylineController(polyline: polyline); final options = gmaps.PolylineOptions()..draggable = false; controller.update(options); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart @@ -0,0 +1,7 @@ +// 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 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html b/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html index 6eff9a740d43..59a832b5de4c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html @@ -1,10 +1,13 @@ - + + - - - example - - - - + + Browser Tests + + + + + From 3c092d50cba690fff6b29dbbe52c4425964e05d9 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 29 Jul 2020 16:47:16 -0700 Subject: [PATCH 32/58] 'Resolve' asset URL so custom pins work in prod. --- .../google_maps_flutter_web/lib/src/convert.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 14940bf71d10..c768a429ed2f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -341,7 +341,11 @@ gmaps.MarkerOptions _markerOptionsFromMarker( gmaps.Icon icon; if (iconConfig[0] == 'fromAssetImage') { - icon = gmaps.Icon()..url = iconConfig[1]; + // iconConfig[2] contains the DPIs of the screen, but that information is + // already encoded in the iconConfig[1] + + icon = gmaps.Icon() + ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); } return gmaps.MarkerOptions() ..position = gmaps.LatLng( From 2745d15797fe0424d97b559693bb53f3ee000b85 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 30 Jul 2020 14:36:07 -0700 Subject: [PATCH 33/58] Update test command to use debug so test failures are visible. --- .../google_maps_flutter_web/test/README.md | 2 +- .../google_maps_flutter_web/test/lib/main.dart | 1 - .../google_maps_flutter/google_maps_flutter_web/test/run_test | 4 ++-- .../google_maps_flutter_web/test/test_driver/marker_e2e.dart | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/README.md b/packages/google_maps_flutter/google_maps_flutter_web/test/README.md index f623e9fd08d3..6eae799ce11c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/README.md @@ -11,7 +11,7 @@ Make sure you have updated to the latest Flutter master. 4. Change into the `test` directory of your clone. -5. Run tests: `flutter drive -d web-server --release --browser-name=chrome --target=test_driver/TEST_NAME_e2e.dart`, or (in Linux): +5. Run tests: `flutter drive -d web-server --browser-name=chrome --target=test_driver/TEST_NAME_e2e.dart`, or (in Linux): * Single: `./run_test test_driver/TEST_NAME_e2e.dart` * All: `./run_test` diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart index 4c55f6c95a7a..da6e56f8ea2b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart @@ -19,4 +19,3 @@ class MyAppState extends State { return Text('Testing... Look at the console output for results!'); } } - diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/run_test b/packages/google_maps_flutter/google_maps_flutter_web/test/run_test index 3a20948c360a..7d43f4271672 100755 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/run_test +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/run_test @@ -4,11 +4,11 @@ if pgrep -lf chromedriver > /dev/null; then if [ $# -eq 0 ]; then echo "No target specified, running all tests..." - find test_driver/ -iname *_e2e.dart | xargs -n1 -i -t flutter drive -d web-server --release --browser-name=chrome --target='{}' + find test_driver/ -iname *_e2e.dart | xargs -n1 -i -t flutter drive -d web-server --browser-name=chrome --target='{}' else echo "Running test target: $1..." set -x - flutter drive -d web-server --release --browser-name=chrome --target=$1 + flutter drive -d web-server --browser-name=chrome --target=$1 fi else diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart index f912cb0e7af3..84c677d009f2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart @@ -21,7 +21,6 @@ class _MockMouseEvent extends Mock implements gmaps.MouseEvent {} class _MockInfoWindow extends Mock implements gmaps.InfoWindow {} - /// Test Markers void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; @@ -68,7 +67,8 @@ void main() { verify(marker.options = options); }); - testWidgets('infoWindow null, showInfoWindow.', (WidgetTester tester) async { + testWidgets('infoWindow null, showInfoWindow.', + (WidgetTester tester) async { final controller = MarkerController(marker: marker); controller.showInfoWindow(); expect(controller.infoWindowShown, isFalse); From 556cec6bd164ac74fcc65667f87251537ec6d6f3 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 31 Jul 2020 10:08:15 -0700 Subject: [PATCH 34/58] Update README with info about Markers jumping back to their original position. --- .../google_maps_flutter_web/README.md | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index c99d3a85c356..9c36740fef57 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -31,6 +31,34 @@ Modify the `` tag of your `web/index.html` to load the Google Maps JavaScr Now you should be able to use the Google Maps plugin normally. +## My draggable markers are resetting their position after other changes + +The web version of the plugin does not attempt to reconcile the currently-rendered Markers with the incoming set of markers from your App. + +If you're keeping a cache of Markers on your app (similar to how the `example` app works), you'll have to refresh your Markers' positions `onDragEnd`, so the map renders your Markers in the correct position after they've been dropped: + +```dart + // When creating your Marker... + final Marker marker = Marker( + markerId: markerId, + position: LatLng(...), + ... + // Add the onDragEnd handler: + onDragEnd: (LatLng position) { + _onMarkerDragEnd(markerId, position); + }, + ); + +void _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async { + ... + if (markers[markerId] != null) { + // Then update your local cache with the latest position: + markers[markerId] = markers[markerId].copyWith(positionParam: newPosition); + ... +``` + +That way your App's Marker cache will reflect all the latest changes. + ## Limitations of the web version The following map options are not available in web, because the map doesn't rotate there: @@ -43,8 +71,8 @@ There's no "Map Toolbar" in web, so the `mapToolbarEnabled` option is unused. There's no "My Location" widget in web (for now), so the following options are ignored: -* `myLocationButtonEnabled` ([doable](https://developers.google.com/maps/documentation/javascript/examples/control-custom)) -* `myLocationEnabled` ([doable](https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html)) +* `myLocationButtonEnabled` ([seems doable](https://developers.google.com/maps/documentation/javascript/examples/control-custom)) +* `myLocationEnabled` ([seems doable](https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html)) There's no `defaultMarkerWithHue` in web. If you want colored pins/markers, you should use your own asset images. From 25dedb719ed7ef0c709e9ce84c0d3946834628a5 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 31 Jul 2020 10:30:29 -0700 Subject: [PATCH 35/58] Reconcile new Marker with existing gmaps.Marker. This prevents the 'location' changes from being forgotten when rendering Markers. This change needs to be done for every property that may change from *outside* of the map controllers! (In this case, the Position of the Marker changes, but it's never propagated to the map, unless the user explicitly subscribes to change events on the Marker they've created). --- .../google_maps_flutter_web/README.md | 28 ------------------- .../lib/src/convert.dart | 8 ++++-- .../lib/src/marker.dart | 3 ++ .../lib/src/markers.dart | 8 ++++-- 4 files changed, 14 insertions(+), 33 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index 9c36740fef57..4e43eb88406c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -31,34 +31,6 @@ Modify the `` tag of your `web/index.html` to load the Google Maps JavaScr Now you should be able to use the Google Maps plugin normally. -## My draggable markers are resetting their position after other changes - -The web version of the plugin does not attempt to reconcile the currently-rendered Markers with the incoming set of markers from your App. - -If you're keeping a cache of Markers on your app (similar to how the `example` app works), you'll have to refresh your Markers' positions `onDragEnd`, so the map renders your Markers in the correct position after they've been dropped: - -```dart - // When creating your Marker... - final Marker marker = Marker( - markerId: markerId, - position: LatLng(...), - ... - // Add the onDragEnd handler: - onDragEnd: (LatLng position) { - _onMarkerDragEnd(markerId, position); - }, - ); - -void _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async { - ... - if (markers[markerId] != null) { - // Then update your local cache with the latest position: - markers[markerId] = markers[markerId].copyWith(positionParam: newPosition); - ... -``` - -That way your App's Marker cache will reflect all the latest changes. - ## Limitations of the web version The following map options are not available in web, because the map doesn't rotate there: diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index c768a429ed2f..f054849a3791 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -335,8 +335,10 @@ gmaps.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { gmaps.LatLng(marker.position.latitude, marker.position.longitude); } -gmaps.MarkerOptions _markerOptionsFromMarker( - gmaps.GMap googleMap, Marker marker) { +// Computes the options for a new [gmaps.Marker] from an incoming set of options +// [marker], and the existing marker registered with the map: [currentMarker]. +// Preserves the position from the [currentMarker], if set. +gmaps.MarkerOptions _markerOptionsFromMarker(Marker marker, gmaps.Marker currentMarker) { final iconConfig = marker.icon.toJson(); gmaps.Icon icon; @@ -348,7 +350,7 @@ gmaps.MarkerOptions _markerOptionsFromMarker( ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); } return gmaps.MarkerOptions() - ..position = gmaps.LatLng( + ..position = currentMarker?.position ?? gmaps.LatLng( marker.position.latitude, marker.position.longitude, ) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index cbb200752113..81eb477ab850 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -41,6 +41,9 @@ class MarkerController { /// Returns [true] if the InfoWindow associated to this marker is being shown. bool get infoWindowShown => _infoWindowShown; + /// Returns the marker associated to this controller. + gmaps.Marker get marker => _marker; + /// Updates the options of the wrapped [gmaps.Polygon] object. void update(gmaps.MarkerOptions options) { _marker.options = options; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index dc8a5863f5dc..10a5634af37c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -20,12 +20,16 @@ class MarkersController extends AbstractController { void _addMarker(Marker marker) { if (marker == null) return; + final infoWindoOptions = _infoWindowOPtionsFromMarker(marker); gmaps.InfoWindow gmInfoWindow = gmaps.InfoWindow(infoWindoOptions) ..addListener('click', () { _onInfoWindowTap(marker.markerId); }); - final populationOptions = _markerOptionsFromMarker(googleMap, marker); + + final currentMarker = _markerIdToController[marker.markerId]?.marker; + + final populationOptions = _markerOptionsFromMarker(marker, currentMarker); gmaps.Marker gmMarker = gmaps.Marker(populationOptions); gmMarker.map = googleMap; MarkerController controller = MarkerController( @@ -56,7 +60,7 @@ class MarkersController extends AbstractController { } MarkerController markerController = _markerIdToController[marker.markerId]; if (markerController != null) { - markerController.update(_markerOptionsFromMarker(googleMap, marker)); + markerController.update(_markerOptionsFromMarker(marker, markerController.marker)); } } From cbe22e22d503a20d907cfede3fe7d1d1b4f4e81b Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 31 Jul 2020 14:36:55 -0700 Subject: [PATCH 36/58] Use icon width/height if present when converting. --- .../google_maps_flutter_web/lib/src/convert.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index f054849a3791..0ece45ef4aed 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -346,8 +346,13 @@ gmaps.MarkerOptions _markerOptionsFromMarker(Marker marker, gmaps.Marker current // iconConfig[2] contains the DPIs of the screen, but that information is // already encoded in the iconConfig[1] + // iconConfig[3] contain the [width, height] of the image, if available icon = gmaps.Icon() ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); + + if (iconConfig[3] != null) { + icon..size = gmaps.Size(iconConfig[3][0], iconConfig[3][1]); + } } return gmaps.MarkerOptions() ..position = currentMarker?.position ?? gmaps.LatLng( From 4de56cc6947a2e850f6f96e9b2edafbd984a0a37 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Mon, 3 Aug 2020 13:50:20 -0700 Subject: [PATCH 37/58] Use the new new attribute name --- .../lib/src/google_maps_flutter_web.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 8ac8062b9bc9..8c3c0f3f90bd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -374,14 +374,14 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map creationParams, Set> gestureRecognizers, PlatformViewCreatedCallback onPlatformViewCreated) { - int mapId = creationParams['creationMapId']; - creationParams.remove('creationMapId'); + int mapId = creationParams['_webOnlyMapCreationId']; + creationParams.remove('_webOnlyMapCreationId'); if (mapId == null) { throw PlatformException( code: 'maps_web_missing_creation_map_id', message: - 'Pass a `creationMapId` in creationParams to prevent reloads in web.', + 'Pass a `_webOnlyMapCreationId` in creationParams to prevent reloads in web.', ); } From d49481bbf6e19a63316a418b81dc6fd426a47498 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Mon, 3 Aug 2020 18:07:26 -0700 Subject: [PATCH 38/58] Add tests for CirclesController. This execution path requires the SDK at some points, so add the gmaps script tag to the web/index.html, and modify the test so it runs on port 7357 (TEST). --- .../lib/src/circle.dart | 5 ++ .../lib/src/circles.dart | 49 +++++------ .../google_maps_flutter_web/test/run_test | 4 +- .../test/test_driver/shapes_e2e.dart | 82 +++++++++++++++++++ .../test/test_driver/shapes_e2e_test.dart | 7 ++ .../test/web/index.html | 1 + 6 files changed, 123 insertions(+), 25 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index c513c4f4dfd7..114ba6d64024 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -20,6 +20,11 @@ class CircleController { } } + @visibleForTesting + + /// Returns the wrapped circle. Only used for testing. + gmaps.Circle get circle => _circle; + /// Returns [true] if this Controller will use its own onTap handler to consume events. bool get consumeTapEvents => _consumeTapEvents; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index a1da31d76b79..6bcce8439005 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -1,25 +1,32 @@ part of google_maps_flutter_web; +/// This class manages all the circles associated to any given Google Map Controller. class CirclesController extends AbstractController { + // A cache of circleIDs to their controllers final Map _circleIdToController; + // The stream over which circles broadcast their events StreamController _streamController; + /// Initialize the cache. The StreamController is shared with the Google Map Controller. CirclesController({ @required StreamController stream, }) : _streamController = stream, _circleIdToController = Map(); + @visibleForTesting + Map get circles => _circleIdToController; + + /// Adds a set of [Circle] objects to the cache. + /// + /// (Wraps each Circle into its corresponding [CircleController]) void addCircles(Set circlesToAdd) { - if (circlesToAdd != null) { - circlesToAdd.forEach((circle) { - addCircle(circle); - }); - } + circlesToAdd?.forEach((circle) { + _addCircle(circle); + }); } - /// add [gmaps.Circle] to [gmaps.GMap]. - void addCircle(Circle circle) { + void _addCircle(Circle circle) { if (circle == null) return; final populationOptions = _circleOptionsFromCircle(circle); gmaps.Circle gmCircle = gmaps.Circle(populationOptions); @@ -33,29 +40,23 @@ class CirclesController extends AbstractController { _circleIdToController[circle.circleId] = controller; } + /// Updates a set of [Circle] objects with new options. void changeCircles(Set circlesToChange) { - if (circlesToChange != null) { - circlesToChange.forEach((circleToChange) { - changeCircle(circleToChange); - }); - } + circlesToChange?.forEach((circleToChange) { + _changeCircle(circleToChange); + }); } - void changeCircle(Circle circle) { - if (circle == null) { - return; - } - CircleController circleController = _circleIdToController[circle.circleId]; - if (circleController != null) { - circleController.update(_circleOptionsFromCircle(circle)); + void _changeCircle(Circle circle) { + if (circle != null) { + final circleController = _circleIdToController[circle.circleId]; + circleController?.update(_circleOptionsFromCircle(circle)); } } + /// Removes a set of [CircleId]s from the cache. void removeCircles(Set circleIdsToRemove) { - if (circleIdsToRemove == null) { - return; - } - circleIdsToRemove.forEach((circleId) { + circleIdsToRemove?.forEach((circleId) { if (circleId != null) { final CircleController circleController = _circleIdToController[circleId]; @@ -67,7 +68,9 @@ class CirclesController extends AbstractController { }); } + // Handles the global onCircleTap function to funnel events from circles into the stream. bool _onCircleTap(CircleId circleId) { + // TODO: Should consumeTapEvents prevent events from being added to the stream? _streamController.add(CircleTapEvent(mapId, circleId)); // Stop propagation? return _circleIdToController[circleId]?.consumeTapEvents ?? false; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/run_test b/packages/google_maps_flutter/google_maps_flutter_web/test/run_test index 7d43f4271672..4b43cf0947dc 100755 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/run_test +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/run_test @@ -4,11 +4,11 @@ if pgrep -lf chromedriver > /dev/null; then if [ $# -eq 0 ]; then echo "No target specified, running all tests..." - find test_driver/ -iname *_e2e.dart | xargs -n1 -i -t flutter drive -d web-server --browser-name=chrome --target='{}' + find test_driver/ -iname *_e2e.dart | xargs -n1 -i -t flutter drive -d web-server --web-port=7357 --browser-name=chrome --target='{}' else echo "Running test target: $1..." set -x - flutter drive -d web-server --browser-name=chrome --target=$1 + flutter drive -d web-server --web-port=7357 --browser-name=chrome --target=$1 fi else diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart new file mode 100644 index 000000000000..ab1e52a77abc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart @@ -0,0 +1,82 @@ +import 'dart:async'; + +import 'package:e2e/e2e.dart'; +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +/// Test Shapes (Circle, Polygon, Polyline) +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + group('CirclesController', () { + StreamController stream; + CirclesController controller; + + setUp(() { + stream = StreamController(); + controller = CirclesController(stream: stream); + }); + + testWidgets('addCircles', (WidgetTester tester) async { + final circles = { + Circle(circleId: CircleId('1')), + Circle(circleId: CircleId('2')), + }; + + controller.addCircles(circles); + + expect(controller.circles.length, 2); + expect(controller.circles, contains(CircleId('1'))); + expect(controller.circles, contains(CircleId('2'))); + expect(controller.circles, isNot(contains(CircleId('66')))); + }); + + testWidgets('changeCircles', (WidgetTester tester) async { + final circles = { + Circle(circleId: CircleId('1')), + }; + controller.addCircles(circles); + + expect(controller.circles[CircleId('1')].circle.radius, 0); + + // Update the circle with radius 10 + final updatedCircles = { + Circle(circleId: CircleId('1'), radius: 10), + }; + controller.changeCircles(updatedCircles); + + expect(controller.circles.length, 1); + expect(controller.circles[CircleId('1')].circle.radius, 10); + }); + + testWidgets('removeCircles', (WidgetTester tester) async { + final circles = { + Circle(circleId: CircleId('1')), + Circle(circleId: CircleId('2')), + Circle(circleId: CircleId('3')), + }; + + controller.addCircles(circles); + + expect(controller.circles.length, 3); + + // Remove some circles... + final circleIdsToRemove = { + CircleId('1'), + CircleId('3'), + }; + + controller.removeCircles(circleIdsToRemove); + + expect(controller.circles.length, 1); + expect(controller.circles, isNot(contains(CircleId('1')))); + expect(controller.circles, contains(CircleId('2'))); + expect(controller.circles, isNot(contains(CircleId('3')))); + }); + + testWidgets('update', (WidgetTester tester) async {}); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart @@ -0,0 +1,7 @@ +// 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 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html b/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html index 59a832b5de4c..3b7e4edc3df1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/web/index.html @@ -5,6 +5,7 @@ Browser Tests + From 8cdfc26d655f4bc8855de348080db40aa90ac2a7 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 4 Aug 2020 12:08:30 -0700 Subject: [PATCH 39/58] Test MarkersController. --- .../lib/src/markers.dart | 37 +++++++-- .../test/test_driver/markers_e2e.dart | 79 +++++++++++++++++++ .../test/test_driver/markers_e2e_test.dart | 7 ++ 3 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 10a5634af37c..0f811b8f4848 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -1,15 +1,26 @@ part of google_maps_flutter_web; +/// This class manages a set of [MarkerController]s associated to a Google Map Controller. class MarkersController extends AbstractController { + // A cache of markerIds to their controllers final Map _markerIdToController; + // The stream over which markers broadcast their events StreamController _streamController; + /// Initializes the cache. The StreamController is shared with the Google Map Controller. MarkersController({ @required StreamController stream, }) : _streamController = stream, _markerIdToController = Map(); + @visibleForTesting + /// Returns the cache of markers. Test only. + Map get markers => _markerIdToController; + + /// Adds a set of [Marker] objects to the cache. + /// + /// (Wraps each Marker into its corresponding [MarkerController]) void addMarkers(Set markersToAdd) { if (markersToAdd != null) { markersToAdd.forEach((marker) { @@ -46,24 +57,29 @@ class MarkersController extends AbstractController { _markerIdToController[marker.markerId] = controller; } + /// Updates a set of [Marker] objects with new options. void changeMarkers(Set markersToChange) { if (markersToChange != null) { markersToChange.forEach((markerToChange) { - changeMarker(markerToChange); + _changeMarker(markerToChange); }); } } - void changeMarker(Marker marker) { + void _changeMarker(Marker marker) { if (marker == null) { return; } MarkerController markerController = _markerIdToController[marker.markerId]; if (markerController != null) { - markerController.update(_markerOptionsFromMarker(marker, markerController.marker)); + markerController.update(_markerOptionsFromMarker( + marker, + markerController.marker, + )); } } + /// Removes a set of [MarkerId]s from the cache. void removeMarkers(Set markerIdsToRemove) { if (markerIdsToRemove == null) { return; @@ -74,25 +90,30 @@ class MarkersController extends AbstractController { _markerIdToController[markerId]; if (markerController != null) { markerController.remove(); - _markerIdToController.remove(markerId.value); + _markerIdToController.remove(markerId); } } }); } + // InfoWindow... + + /// Shows the [InfoWindow] of a Marker. void showMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; markerController?.showInfoWindow(); } - bool isInfoWindowShown(MarkerId markerId) { + /// Hides the [InfoWindow] of a Marker. + void hideMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; - return markerController?.infoWindowShown ?? false; + markerController?.hideInfoWindow(); } - void hideMarkerInfoWindow(MarkerId markerId) { + /// Returns whether or not the [InfoWindow] of a Marker is shown. + bool isInfoWindowShown(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; - markerController?.hideInfoWindow(); + return markerController?.infoWindowShown ?? false; } // Handle internal events diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart new file mode 100644 index 000000000000..7c551a756378 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart @@ -0,0 +1,79 @@ +import 'dart:async'; + +import 'package:e2e/e2e.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + group('MarkersController', () { + StreamController stream; + MarkersController controller; + + setUp(() { + stream = StreamController(); + controller = MarkersController(stream: stream); + }); + + testWidgets('addMarkers', (WidgetTester tester) async { + final markers = { + Marker(markerId: MarkerId('1')), + Marker(markerId: MarkerId('2')), + }; + + controller.addMarkers(markers); + + expect(controller.markers.length, 2); + expect(controller.markers, contains(MarkerId('1'))); + expect(controller.markers, contains(MarkerId('2'))); + expect(controller.markers, isNot(contains(MarkerId('66')))); + }); + + testWidgets('changeMarkers', (WidgetTester tester) async { + final markers = { + Marker(markerId: MarkerId('1')), + }; + controller.addMarkers(markers); + + expect(controller.markers[MarkerId('1')].marker.draggable, isFalse); + + // Update the marker with radius 10 + final updatedMarkers = { + Marker(markerId: MarkerId('1'), draggable: true), + }; + controller.changeMarkers(updatedMarkers); + + expect(controller.markers.length, 1); + expect(controller.markers[MarkerId('1')].marker.draggable, isTrue); + }); + + testWidgets('removeMarkers', (WidgetTester tester) async { + final markers = { + Marker(markerId: MarkerId('1')), + Marker(markerId: MarkerId('2')), + Marker(markerId: MarkerId('3')), + }; + + controller.addMarkers(markers); + + expect(controller.markers.length, 3); + + // Remove some markers... + final markerIdsToRemove = { + MarkerId('1'), + MarkerId('3'), + }; + + controller.removeMarkers(markerIdsToRemove); + + expect(controller.markers.length, 1); + expect(controller.markers, isNot(contains(MarkerId('1')))); + expect(controller.markers, contains(MarkerId('2'))); + expect(controller.markers, isNot(contains(MarkerId('3')))); + }); + + testWidgets('update', (WidgetTester tester) async {}); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart @@ -0,0 +1,7 @@ +// 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 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); From e5d91e1e478f99403a19e010d98af4acf73b16e9 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 4 Aug 2020 12:08:56 -0700 Subject: [PATCH 40/58] Some formatting/analyzer fixes. --- .../google_maps_flutter_web/lib/src/circles.dart | 1 + .../google_maps_flutter_web/lib/src/convert.dart | 16 +++++++++++----- .../test/test_driver/shapes_e2e.dart | 2 -- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index 6bcce8439005..6ab08303150a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -15,6 +15,7 @@ class CirclesController extends AbstractController { _circleIdToController = Map(); @visibleForTesting + /// Returns the cache of circles. Test only. Map get circles => _circleIdToController; /// Adds a set of [Circle] objects to the cache. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 0ece45ef4aed..3ab5d16d7108 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -338,7 +338,10 @@ gmaps.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { // Computes the options for a new [gmaps.Marker] from an incoming set of options // [marker], and the existing marker registered with the map: [currentMarker]. // Preserves the position from the [currentMarker], if set. -gmaps.MarkerOptions _markerOptionsFromMarker(Marker marker, gmaps.Marker currentMarker) { +gmaps.MarkerOptions _markerOptionsFromMarker( + Marker marker, + gmaps.Marker currentMarker, +) { final iconConfig = marker.icon.toJson(); gmaps.Icon icon; @@ -348,6 +351,8 @@ gmaps.MarkerOptions _markerOptionsFromMarker(Marker marker, gmaps.Marker current // iconConfig[3] contain the [width, height] of the image, if available icon = gmaps.Icon() + // TODO: Move the comment below to analysis-options.yaml + // ignore:undefined_prefixed_name ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); if (iconConfig[3] != null) { @@ -355,10 +360,11 @@ gmaps.MarkerOptions _markerOptionsFromMarker(Marker marker, gmaps.Marker current } } return gmaps.MarkerOptions() - ..position = currentMarker?.position ?? gmaps.LatLng( - marker.position.latitude, - marker.position.longitude, - ) + ..position = currentMarker?.position ?? + gmaps.LatLng( + marker.position.latitude, + marker.position.longitude, + ) ..title = marker.infoWindow.title ..zIndex = marker.zIndex ..visible = marker.visible diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart index ab1e52a77abc..bb54906d88b9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart @@ -1,11 +1,9 @@ import 'dart:async'; import 'package:e2e/e2e.dart'; -import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; /// Test Shapes (Circle, Polygon, Polyline) void main() { From 8520de3cca2c11a5dc9618cc59e7a6d0486e1bba Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 4 Aug 2020 18:39:20 -0700 Subject: [PATCH 41/58] Test MarkersController infoWindow open/close --- .../test/test_driver/markers_e2e.dart | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart index 7c551a756378..11380a1d584d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart @@ -74,6 +74,25 @@ void main() { expect(controller.markers, isNot(contains(MarkerId('3')))); }); - testWidgets('update', (WidgetTester tester) async {}); + testWidgets('InfoWindow show/hide', (WidgetTester tester) async { + final markers = { + Marker( + markerId: MarkerId('1'), + infoWindow: InfoWindow(title: "Title", snippet: "Snippet"), + ), + }; + + controller.addMarkers(markers); + + expect(controller.markers[MarkerId('1')].infoWindowShown, isFalse); + + controller.showMarkerInfoWindow(MarkerId('1')); + + expect(controller.markers[MarkerId('1')].infoWindowShown, isTrue); + + controller.hideMarkerInfoWindow(MarkerId('1')); + + expect(controller.markers[MarkerId('1')].infoWindowShown, isFalse); + }); }); } From 1536ea6d6f2df59605a7fd6eb1f4477dbc7872ca Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 4 Aug 2020 19:18:16 -0700 Subject: [PATCH 42/58] Format and test the rest of the shapes. --- .../lib/src/circle.dart | 3 +- .../lib/src/circles.dart | 2 +- .../lib/src/markers.dart | 2 +- .../lib/src/polygon.dart | 4 + .../lib/src/polygons.dart | 20 ++- .../lib/src/polyline.dart | 4 + .../lib/src/polylines.dart | 17 ++- .../test/test_driver/shapes_e2e.dart | 140 +++++++++++++++++- 8 files changed, 177 insertions(+), 15 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 114ba6d64024..7450e375622b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -20,9 +20,8 @@ class CircleController { } } - @visibleForTesting - /// Returns the wrapped circle. Only used for testing. + @visibleForTesting gmaps.Circle get circle => _circle; /// Returns [true] if this Controller will use its own onTap handler to consume events. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index 6ab08303150a..6313e03e0ca0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -14,8 +14,8 @@ class CirclesController extends AbstractController { }) : _streamController = stream, _circleIdToController = Map(); - @visibleForTesting /// Returns the cache of circles. Test only. + @visibleForTesting Map get circles => _circleIdToController; /// Adds a set of [Circle] objects to the cache. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 0f811b8f4848..4a114bd13cf1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -14,8 +14,8 @@ class MarkersController extends AbstractController { }) : _streamController = stream, _markerIdToController = Map(); - @visibleForTesting /// Returns the cache of markers. Test only. + @visibleForTesting Map get markers => _markerIdToController; /// Adds a set of [Marker] objects to the cache. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index e4bf722e5623..e5cd79b5f020 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -20,6 +20,10 @@ class PolygonController { } } + /// Returns the wrapped circle. Only used for testing. + @visibleForTesting + gmaps.Polygon get polygon => _polygon; + /// Returns [true] if this Controller will use its own onTap handler to consume events. bool get consumeTapEvents => _consumeTapEvents; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index 22f41bfbe00e..a2e52d7933af 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -1,15 +1,26 @@ part of google_maps_flutter_web; +/// This class manages all the polygons associated to any given Google Map Controller. class PolygonsController extends AbstractController { + // A cache of polygonIDs to their controllers final Map _polygonIdToController; + // The stream over which polygons broadcast events StreamController _streamController; + /// Initializes the cache. The StreamController is shared with the Google Map Controller. PolygonsController({ @required StreamController stream, }) : _streamController = stream, _polygonIdToController = Map(); + /// Returns the cache of polygons. Test only. + @visibleForTesting + Map get polygons => _polygonIdToController; + + /// Adds a set of [Polygon] objects to the cache. + /// + /// (Wraps each Polygon into its corresponding [PolygonController]) void addPolygons(Set polygonsToAdd) { if (polygonsToAdd != null) { polygonsToAdd.forEach((polygon) { @@ -32,15 +43,16 @@ class PolygonsController extends AbstractController { _polygonIdToController[polygon.polygonId] = controller; } + /// Updates a set of [Polygon] objects with new options. void changePolygons(Set polygonsToChange) { if (polygonsToChange != null) { polygonsToChange.forEach((polygonToChange) { - changePolygon(polygonToChange); + _changePolygon(polygonToChange); }); } } - void changePolygon(Polygon polygon) { + void _changePolygon(Polygon polygon) { if (polygon == null) { return; } @@ -52,6 +64,7 @@ class PolygonsController extends AbstractController { } } + /// Removes a set of [PolygonId]s from the cache. void removePolygons(Set polygonIdsToRemove) { if (polygonIdsToRemove == null) { return; @@ -62,14 +75,13 @@ class PolygonsController extends AbstractController { _polygonIdToController[polygonId]; if (polygonController != null) { polygonController.remove(); - _polygonIdToController.remove(polygonId.value); + _polygonIdToController.remove(polygonId); } } }); } // Handle internal events - bool _onPolygonTap(PolygonId polygonId) { _streamController.add(PolygonTapEvent(mapId, polygonId)); // Stop propagation? diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index d0f939a99e75..2890028702c3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -20,6 +20,10 @@ class PolylineController { } } + /// Returns the wrapped circle. Only used for testing. + @visibleForTesting + gmaps.Polyline get line => _polyline; + /// Returns [true] if this Controller will use its own onTap handler to consume events. bool get consumeTapEvents => _consumeTapEvents; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index b5c0a48aae44..8076ca2819b2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -1,15 +1,26 @@ part of google_maps_flutter_web; +/// This class manages all the (poly)lines associated to any given Google Map Controller. class PolylinesController extends AbstractController { + // A cache of polylineIds to their controllers final Map _polylineIdToController; + // The stream over which polylines broadcast their events StreamController _streamController; + /// Initializes the cache. The StreamController is shared with the Google Map Controller. PolylinesController({ @required StreamController stream, }) : _streamController = stream, _polylineIdToController = Map(); + /// Returns the cache of polylines. Test only. + @visibleForTesting + Map get lines => _polylineIdToController; + + /// Adds a set of [Polyline] objects to the cache. + /// + /// (Wraps each line into its corresponding [PolylineController]) void addPolylines(Set polylinesToAdd) { if (polylinesToAdd != null) { polylinesToAdd.forEach((polyline) { @@ -32,15 +43,16 @@ class PolylinesController extends AbstractController { _polylineIdToController[polyline.polylineId] = controller; } + /// Updates a set of [Polyline] objects with new options. void changePolylines(Set polylinesToChange) { if (polylinesToChange != null) { polylinesToChange.forEach((polylineToChange) { - changePolyline(polylineToChange); + _changePolyline(polylineToChange); }); } } - void changePolyline(Polyline polyline) { + void _changePolyline(Polyline polyline) { if (polyline == null) { return; } @@ -52,6 +64,7 @@ class PolylinesController extends AbstractController { } } + /// Removes a set of [PolylineId]s from the cache. void removePolylines(Set polylineIdsToRemove) { if (polylineIdsToRemove == null) { return; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart index bb54906d88b9..a8a21959a4d6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart @@ -38,16 +38,15 @@ void main() { }; controller.addCircles(circles); - expect(controller.circles[CircleId('1')].circle.radius, 0); + expect(controller.circles[CircleId('1')].circle.visible, isTrue); - // Update the circle with radius 10 final updatedCircles = { - Circle(circleId: CircleId('1'), radius: 10), + Circle(circleId: CircleId('1'), visible: false), }; controller.changeCircles(updatedCircles); expect(controller.circles.length, 1); - expect(controller.circles[CircleId('1')].circle.radius, 10); + expect(controller.circles[CircleId('1')].circle.visible, isFalse); }); testWidgets('removeCircles', (WidgetTester tester) async { @@ -74,7 +73,138 @@ void main() { expect(controller.circles, contains(CircleId('2'))); expect(controller.circles, isNot(contains(CircleId('3')))); }); + }); + + group('PolygonsController', () { + StreamController stream; + PolygonsController controller; + + setUp(() { + stream = StreamController(); + controller = PolygonsController(stream: stream); + }); + + testWidgets('addPolygons', (WidgetTester tester) async { + final polygons = { + Polygon(polygonId: PolygonId('1')), + Polygon(polygonId: PolygonId('2')), + }; + + controller.addPolygons(polygons); + + expect(controller.polygons.length, 2); + expect(controller.polygons, contains(PolygonId('1'))); + expect(controller.polygons, contains(PolygonId('2'))); + expect(controller.polygons, isNot(contains(PolygonId('66')))); + }); + + testWidgets('changePolygons', (WidgetTester tester) async { + final polygons = { + Polygon(polygonId: PolygonId('1')), + }; + controller.addPolygons(polygons); - testWidgets('update', (WidgetTester tester) async {}); + expect(controller.polygons[PolygonId('1')].polygon.visible, isTrue); + + // Update the polygon + final updatedPolygons = { + Polygon(polygonId: PolygonId('1'), visible: false), + }; + controller.changePolygons(updatedPolygons); + + expect(controller.polygons.length, 1); + expect(controller.polygons[PolygonId('1')].polygon.visible, isFalse); + }); + + testWidgets('removePolygons', (WidgetTester tester) async { + final polygons = { + Polygon(polygonId: PolygonId('1')), + Polygon(polygonId: PolygonId('2')), + Polygon(polygonId: PolygonId('3')), + }; + + controller.addPolygons(polygons); + + expect(controller.polygons.length, 3); + + // Remove some polygons... + final polygonIdsToRemove = { + PolygonId('1'), + PolygonId('3'), + }; + + controller.removePolygons(polygonIdsToRemove); + + expect(controller.polygons.length, 1); + expect(controller.polygons, isNot(contains(PolygonId('1')))); + expect(controller.polygons, contains(PolygonId('2'))); + expect(controller.polygons, isNot(contains(PolygonId('3')))); + }); + }); + + group('PolylinesController', () { + StreamController stream; + PolylinesController controller; + + setUp(() { + stream = StreamController(); + controller = PolylinesController(stream: stream); + }); + + testWidgets('addPolylines', (WidgetTester tester) async { + final polylines = { + Polyline(polylineId: PolylineId('1')), + Polyline(polylineId: PolylineId('2')), + }; + + controller.addPolylines(polylines); + + expect(controller.lines.length, 2); + expect(controller.lines, contains(PolylineId('1'))); + expect(controller.lines, contains(PolylineId('2'))); + expect(controller.lines, isNot(contains(PolylineId('66')))); + }); + + testWidgets('changePolylines', (WidgetTester tester) async { + final polylines = { + Polyline(polylineId: PolylineId('1')), + }; + controller.addPolylines(polylines); + + expect(controller.lines[PolylineId('1')].line.visible, isTrue); + + final updatedPolylines = { + Polyline(polylineId: PolylineId('1'), visible: false), + }; + controller.changePolylines(updatedPolylines); + + expect(controller.lines.length, 1); + expect(controller.lines[PolylineId('1')].line.visible, isFalse); + }); + + testWidgets('removePolylines', (WidgetTester tester) async { + final polylines = { + Polyline(polylineId: PolylineId('1')), + Polyline(polylineId: PolylineId('2')), + Polyline(polylineId: PolylineId('3')), + }; + + controller.addPolylines(polylines); + + expect(controller.lines.length, 3); + + // Remove some polylines... + final polylineIdsToRemove = { + PolylineId('1'), + PolylineId('3'), + }; + + controller.removePolylines(polylineIdsToRemove); + + expect(controller.lines.length, 1); + expect(controller.lines, isNot(contains(PolylineId('1')))); + expect(controller.lines, contains(PolylineId('2'))); + expect(controller.lines, isNot(contains(PolylineId('3')))); + }); }); } From f5e54336394e6424ff1729c4ab103fbc90eead6e Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 4 Aug 2020 19:23:55 -0700 Subject: [PATCH 43/58] Apply BOTH size and scaledSize properties to custom markers --- .../google_maps_flutter_web/lib/src/convert.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 3ab5d16d7108..8f974c97ab8f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -356,7 +356,10 @@ gmaps.MarkerOptions _markerOptionsFromMarker( ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); if (iconConfig[3] != null) { - icon..size = gmaps.Size(iconConfig[3][0], iconConfig[3][1]); + final size = gmaps.Size(iconConfig[3][0], iconConfig[3][1]); + icon + ..size = size + ..scaledSize = size; } } return gmaps.MarkerOptions() From 9fcb61d78910f8b3028762f38f4ee8800cfb5044 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 5 Aug 2020 12:50:36 -0700 Subject: [PATCH 44/58] Check if the size has actually been passed from the plugin. --- .../google_maps_flutter_web/lib/src/convert.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 8f974c97ab8f..bb1d826be5e0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -342,20 +342,20 @@ gmaps.MarkerOptions _markerOptionsFromMarker( Marker marker, gmaps.Marker currentMarker, ) { - final iconConfig = marker.icon.toJson(); + final iconConfig = marker.icon.toJson() as List; gmaps.Icon icon; if (iconConfig[0] == 'fromAssetImage') { // iconConfig[2] contains the DPIs of the screen, but that information is // already encoded in the iconConfig[1] - // iconConfig[3] contain the [width, height] of the image, if available icon = gmaps.Icon() // TODO: Move the comment below to analysis-options.yaml // ignore:undefined_prefixed_name ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); - if (iconConfig[3] != null) { + // iconConfig[3] may contain the [width, height] of the image, if passed! + if (iconConfig.length >= 4 && iconConfig[3] != null) { final size = gmaps.Size(iconConfig[3][0], iconConfig[3][1]); icon ..size = size From fe950ac482a319e7c2cc1e1f81d202c44aabb5e8 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 5 Aug 2020 18:28:52 -0700 Subject: [PATCH 45/58] Add traffic layer support, when creating the map, and toggling options. --- .../lib/src/google_maps_flutter_web.dart | 110 ++++++------------ 1 file changed, 34 insertions(+), 76 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 8c3c0f3f90bd..92883cb7b024 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -41,11 +41,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) async { GoogleMapController googleMapController = _mapById[mapId]; if (googleMapController != null) { + final mergedRawOptions = _mergeRawMapOptions(optionsUpdate, mapId); googleMapController.setOptions( - _optionsFromParams( - _mergeRawMapOptions(optionsUpdate, mapId), - ), + _optionsFromParams(mergedRawOptions), ); + googleMapController + .setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); } else { throw StateError("updateMapOptions called prior to map initialization"); } @@ -56,13 +57,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { MarkerUpdates markerUpdates, { @required int mapId, }) async { - _mapById[mapId].markersController.addMarkers(markerUpdates.markersToAdd); - _mapById[mapId] - .markersController - .changeMarkers(markerUpdates.markersToChange); - _mapById[mapId] - .markersController - .removeMarkers(markerUpdates.markerIdsToRemove); + final markers = _mapById[mapId]?.markersController; + markers?.addMarkers(markerUpdates.markersToAdd); + markers?.changeMarkers(markerUpdates.markersToChange); + markers?.removeMarkers(markerUpdates.markerIdsToRemove); } @override @@ -70,15 +68,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { PolygonUpdates polygonUpdates, { @required int mapId, }) async { - _mapById[mapId] - .polygonsController - .addPolygons(polygonUpdates.polygonsToAdd); - _mapById[mapId] - .polygonsController - .changePolygons(polygonUpdates.polygonsToChange); - _mapById[mapId] - .polygonsController - .removePolygons(polygonUpdates.polygonIdsToRemove); + final polygons = _mapById[mapId]?.polygonsController; + polygons?.addPolygons(polygonUpdates.polygonsToAdd); + polygons?.changePolygons(polygonUpdates.polygonsToChange); + polygons?.removePolygons(polygonUpdates.polygonIdsToRemove); } @override @@ -86,15 +79,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { PolylineUpdates polylineUpdates, { @required int mapId, }) async { - _mapById[mapId] - .polylinesController - .addPolylines(polylineUpdates.polylinesToAdd); - _mapById[mapId] - .polylinesController - .changePolylines(polylineUpdates.polylinesToChange); - _mapById[mapId] - .polylinesController - .removePolylines(polylineUpdates.polylineIdsToRemove); + final polylines = _mapById[mapId]?.polylinesController; + polylines?.addPolylines(polylineUpdates.polylinesToAdd); + polylines?.changePolylines(polylineUpdates.polylinesToChange); + polylines?.removePolylines(polylineUpdates.polylineIdsToRemove); } @override @@ -102,13 +90,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { CircleUpdates circleUpdates, { @required int mapId, }) async { - _mapById[mapId].circlesController.addCircles(circleUpdates.circlesToAdd); - _mapById[mapId] - .circlesController - .changeCircles(circleUpdates.circlesToChange); - _mapById[mapId] - .circlesController - .removeCircles(circleUpdates.circleIdsToRemove); + final circles = _mapById[mapId].circlesController; + circles?.addCircles(circleUpdates.circlesToAdd); + circles?.changeCircles(circleUpdates.circlesToChange); + circles?.removeCircles(circleUpdates.circleIdsToRemove); } @override @@ -279,31 +264,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return Future.value(googleMapController.googleMap.zoom.toDouble()); } - @override - Future takeSnapshot({ - @required int mapId, - }) { - throw UnimplementedError('takeSnapshot() has not been implemented.'); - /**takeSnapshot - * if (googleMap != null) { - final MethodChannel.Result _result = result; - gmaps.snapshot( - new SnapshotReadyCallback() { - @Override - public void onSnapshotReady(Bitmap bitmap) { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); - byte[] byteArray = stream.toByteArray(); - bitmap.recycle(); - _result.success(byteArray); - } - }); - } else { - result.error("GoogleMap uninitialized", "takeSnapshot", null); - } - */ - } - // The following are the 11 possible streams of data from the native side // into the plugin @@ -374,8 +334,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map creationParams, Set> gestureRecognizers, PlatformViewCreatedCallback onPlatformViewCreated) { - int mapId = creationParams['_webOnlyMapCreationId']; - creationParams.remove('_webOnlyMapCreationId'); + int mapId = creationParams.remove('_webOnlyMapCreationId'); if (mapId == null) { throw PlatformException( @@ -385,20 +344,16 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { ); } - Map mergedRawOptions; + // Bail fast if we've already rendered this mapId... + if (_mapById[mapId]?.html != null) { + return _mapById[mapId].html; + } - // Merge the raw options now, so we can adjust the traffic layer on the cached controller, if needed. + Map mergedRawOptions; if (creationParams['options'] != null) { mergedRawOptions = _mergeRawMapOptions(creationParams['options'], mapId); } - if (_mapById[mapId]?.html != null) { - // TODO: Toggling the traffic layer here needs a repaint that isn't happening. - // How to achieve that? - // _mapById[mapId].setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); - return _mapById[mapId].html; - } - gmaps.MapOptions options = gmaps.MapOptions(); CameraPosition position; @@ -428,7 +383,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } }); - _mapById[mapId] = GoogleMapController.build( + final mapController = GoogleMapController.build( mapId: mapId, streamController: _controller, onPlatformViewCreated: onPlatformViewCreated, @@ -440,11 +395,14 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { initialMarkers: initialMarkers?.markersToAdd, ); - onPlatformViewCreated.call(mapId); + _mapById[mapId] = mapController; - // TODO: Enable layer support, once toggling works. - // _mapById[mapId].setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); + mapController.setTrafficLayer( + mergedRawOptions['trafficEnabled'] ?? false, + ); + + onPlatformViewCreated.call(mapId); - return _mapById[mapId].html; + return mapController.html; } } From deca0ebf52eb9fbb1fe2090e6e4e911ace48db12 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 5 Aug 2020 18:30:00 -0700 Subject: [PATCH 46/58] Add InfoWindow support. * Sanitize title/content to prevent XSS * Open InfoWindow automatically on marker click * Do not render infoWindow if it's empty * Update contents of infoWindow alongside its Marker --- .../lib/google_maps_flutter_web.dart | 2 + .../lib/src/convert.dart | 29 ++++++---- .../lib/src/marker.dart | 10 +++- .../lib/src/markers.dart | 54 +++++++++++-------- .../google_maps_flutter_web/pubspec.yaml | 1 + 5 files changed, 60 insertions(+), 36 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index d4730df67fe2..1feee72c7a64 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -13,6 +13,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/gestures.dart'; +import 'package:sanitize_html/sanitize_html.dart'; + import 'package:stream_transform/stream_transform.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index bb1d826be5e0..2bb5577c21ba 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -327,12 +327,22 @@ MarkerUpdates _markerFromParams(value) { return null; } -gmaps.InfoWindowOptions _infoWindowOPtionsFromMarker(Marker marker) { +gmaps.InfoWindowOptions _infoWindowOptionsFromMarker(Marker marker) { + if ((marker.infoWindow?.title?.isEmpty ?? true) && + (marker.infoWindow?.snippet?.isEmpty ?? true)) { + return null; + } + + final content = '

' + + sanitizeHtml(marker.infoWindow.title) + + '

' + + sanitizeHtml(marker.infoWindow.snippet); + return gmaps.InfoWindowOptions() - ..content = marker.infoWindow.snippet - ..zIndex = marker.zIndex - ..position = - gmaps.LatLng(marker.position.latitude, marker.position.longitude); + ..content = content + ..zIndex = marker.zIndex; + // TODO: Compute the pixelOffset of the infoWindow, from the size of the Marker, + // and the marker.infoWindow.anchor property. } // Computes the options for a new [gmaps.Marker] from an incoming set of options @@ -368,16 +378,13 @@ gmaps.MarkerOptions _markerOptionsFromMarker( marker.position.latitude, marker.position.longitude, ) - ..title = marker.infoWindow.title + ..title = sanitizeHtml(marker.infoWindow?.title ?? "") ..zIndex = marker.zIndex ..visible = marker.visible ..opacity = marker.alpha ..draggable = marker.draggable - ..icon = icon - ..anchorPoint = gmaps.Point( - marker.anchor.dx, - marker.anchor.dy, - ); + ..icon = icon; + // TODO: Compute anchor properly, otherwise infowindows attach to the wrong spot. // Flat and Rotation are not supported directly on the web. } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 81eb477ab850..1bf8db196494 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -45,8 +45,14 @@ class MarkerController { gmaps.Marker get marker => _marker; /// Updates the options of the wrapped [gmaps.Polygon] object. - void update(gmaps.MarkerOptions options) { + void update( + gmaps.MarkerOptions options, { + String newInfoWindowContent, + }) { _marker.options = options; + if (_infoWindow != null && newInfoWindowContent != null) { + _infoWindow.content = newInfoWindowContent; + } } /// Disposes of the currently wrapped Marker. @@ -67,7 +73,7 @@ class MarkerController { /// Show the associated InfoWindow. void showInfoWindow() { if (_infoWindow != null) { - _infoWindow.open(_marker.map); + _infoWindow.open(_marker.map, _marker); _infoWindowShown = true; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 4a114bd13cf1..87682175e6dc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -23,20 +23,22 @@ class MarkersController extends AbstractController { /// (Wraps each Marker into its corresponding [MarkerController]) void addMarkers(Set markersToAdd) { if (markersToAdd != null) { - markersToAdd.forEach((marker) { - _addMarker(marker); - }); + markersToAdd.forEach(_addMarker); } } void _addMarker(Marker marker) { if (marker == null) return; - final infoWindoOptions = _infoWindowOPtionsFromMarker(marker); - gmaps.InfoWindow gmInfoWindow = gmaps.InfoWindow(infoWindoOptions) - ..addListener('click', () { - _onInfoWindowTap(marker.markerId); - }); + final infoWindowOptions = _infoWindowOptionsFromMarker(marker); + gmaps.InfoWindow gmInfoWindow; + + if (infoWindowOptions != null) { + gmInfoWindow = gmaps.InfoWindow(infoWindowOptions) + ..addListener('click', () { + _onInfoWindowTap(marker.markerId); + }); + } final currentMarker = _markerIdToController[marker.markerId]?.marker; @@ -48,6 +50,8 @@ class MarkersController extends AbstractController { infoWindow: gmInfoWindow, consumeTapEvents: marker.consumeTapEvents, onTap: () { + // TODO: If has infowindow... + this.showMarkerInfoWindow(marker.markerId); _onMarkerTap(marker.markerId); }, onDragEnd: (gmaps.LatLng latLng) { @@ -60,9 +64,7 @@ class MarkersController extends AbstractController { /// Updates a set of [Marker] objects with new options. void changeMarkers(Set markersToChange) { if (markersToChange != null) { - markersToChange.forEach((markerToChange) { - _changeMarker(markerToChange); - }); + markersToChange.forEach(_changeMarker); } } @@ -72,10 +74,15 @@ class MarkersController extends AbstractController { } MarkerController markerController = _markerIdToController[marker.markerId]; if (markerController != null) { - markerController.update(_markerOptionsFromMarker( + final markerOptions = _markerOptionsFromMarker( marker, markerController.marker, - )); + ); + final infoWindow = _infoWindowOptionsFromMarker(marker); + markerController.update( + markerOptions, + newInfoWindowContent: infoWindow?.content, + ); } } @@ -84,16 +91,17 @@ class MarkersController extends AbstractController { if (markerIdsToRemove == null) { return; } - markerIdsToRemove.forEach((markerId) { - if (markerId != null) { - final MarkerController markerController = - _markerIdToController[markerId]; - if (markerController != null) { - markerController.remove(); - _markerIdToController.remove(markerId); - } - } - }); + markerIdsToRemove.forEach(_removeMarker); + } + + void _removeMarker(MarkerId markerId) { + if (markerId == null) return; + + final MarkerController markerController = _markerIdToController[markerId]; + if (markerController != null) { + markerController.remove(); + _markerIdToController.remove(markerId); + } } // InfoWindow... diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 9cd2b8c3c553..771bdb3e9e52 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: google_maps_flutter_platform_interface: ^1.0.1 google_maps: ^3.0.0 stream_transform: ^1.2.0 + sanitize_html: ^1.3.0 dev_dependencies: flutter_test: From 74fff64fe8248981f58b4a879a70e7d7ac9c7b2a Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 6 Aug 2020 14:58:34 -0700 Subject: [PATCH 47/58] Hide most of the directly accessible properties in the GoogleMap Controller. Substitute direct access by semantic methods. Use those methods in the platform implementation instead. --- .../lib/google_maps_flutter_web.dart | 2 - .../lib/src/convert.dart | 61 ++++ .../lib/src/google_maps_controller.dart | 296 +++++++++++------- .../lib/src/google_maps_flutter_web.dart | 208 +++++------- 4 files changed, 311 insertions(+), 256 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index 1feee72c7a64..1a315d82d7c4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:html'; import 'dart:ui' as ui; import 'dart:convert'; -import 'dart:typed_data'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -20,7 +19,6 @@ import 'package:stream_transform/stream_transform.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:google_maps/google_maps.dart' as gmaps; -//import 'package:js/js.dart'; part 'src/google_maps_flutter_web.dart'; part 'src/google_maps_controller.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 2bb5577c21ba..1032a1249d79 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -1,5 +1,11 @@ part of google_maps_flutter_web; +final _nullLatLng = LatLng(0, 0); +final _nullLatLngBounds = LatLngBounds( + northeast: _nullLatLng, + southwest: _nullLatLng, +); + // Indices in the plugin side don't match with the ones // in the gmaps lib. This translates from plugin -> gmaps. final _mapTypeToMapTypeId = { @@ -281,6 +287,10 @@ LatLng _gmLatlngToLatlng(gmaps.LatLng latLng) { } LatLngBounds _gmLatLngBoundsTolatLngBounds(gmaps.LatLngBounds latLngBounds) { + if (latLngBounds == null) { + return _nullLatLngBounds; + } + return LatLngBounds( southwest: _gmLatlngToLatlng(latLngBounds.southWest), northeast: _gmLatlngToLatlng(latLngBounds.northEast), @@ -423,3 +433,54 @@ CircleUpdates _circleFromParams(value) { } return null; } + +// Translates a [CameraUpdate] into operations on a [gmaps.GMap]. +void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { + final json = update.toJson(); + switch (json[0]) { + case 'newCameraPosition': + map.heading = json[1]['bearing']; + map.zoom = json[1]['zoom']; + map.panTo(gmaps.LatLng(json[1]['target'][0], json[1]['target'][1])); + map.tilt = json[1]['tilt']; + break; + case 'newLatLng': + map.panTo(gmaps.LatLng(json[1][0], json[1][1])); + break; + case 'newLatLngZoom': + map.zoom = json[2]; + map.panTo(gmaps.LatLng(json[1][0], json[1][1])); + break; + case 'newLatLngBounds': + map.fitBounds(gmaps.LatLngBounds( + gmaps.LatLng(json[1][0][0], json[1][0][1]), + gmaps.LatLng(json[1][1][0], json[1][1][1]))); + // padding = json[2]; + // Needs package:google_maps ^4.0.0 to adjust the padding in fitBounds + break; + case 'scrollBy': + map.panBy(json[1], json[2]); + break; + case 'zoomBy': + double zoomDelta = json[1] ?? 0; + // Web only supports integer changes... + int newZoomDelta = zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); + map.zoom = map.zoom + newZoomDelta; + if (json.length == 3) { + // With focus + map.panTo(gmaps.LatLng(json[2][0], json[2][1])); + } + break; + case 'zoomIn': + map.zoom++; + break; + case 'zoomOut': + map.zoom--; + break; + case 'zoomTo': + map.zoom = json[1]; + break; + default: + throw UnimplementedError('Unimplemented CameraMove: ${json[0]}.'); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 2c326b18aa34..a72fda155f3a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -1,184 +1,248 @@ part of google_maps_flutter_web; -///TODO +/// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered. class GoogleMapController { - ///TODO - final int mapId; + // The internal ID of the map. Used to broadcast events, DOM IDs and everything where a unique ID is needed. + final int _mapId; - ///TODO - HtmlElementView html; + // The Flutter widget that contains the rendered Map. + HtmlElementView _widget; + /// The Flutter widget that contains the rendered Map. Used for caching. + HtmlElementView get widget => _widget; + + // The currently-enabled traffic layer. gmaps.TrafficLayer _trafficLayer; - /// TODO - gmaps.GMap googleMap; + // The underlying GMap instance. This is the interface with the JS SDK. + gmaps.GMap _googleMap; - final StreamController streamController; - CameraPosition position; - CirclesController circlesController; - PolygonsController polygonsController; - PolylinesController polylinesController; - MarkersController markersController; + // The Stream over which all controllers broadcast their events. + final StreamController _streamController; - Set initialCircles; - Set initialPolygons; - Set initialPolylines; - Set initialMarkers; + // Geometry controllers, for different features of the map. + CirclesController _circlesController; + PolygonsController _polygonsController; + PolylinesController _polylinesController; + MarkersController _markersController; + // Keeps track if _attachGeometryControllers has been called or not. + bool _controllersBoundToMap = false; + // Keeps track if the map is moving or not. bool _mapIsMoving = false; - ///TODO - GoogleMapController.build({ - @required this.mapId, - @required this.streamController, + /// Initializes the GMap, and the sub-controllers related to it. Wires events. + GoogleMapController({ + @required int mapId, + @required StreamController streamController, @required gmaps.MapOptions options, - @required this.position, - @required onPlatformViewCreated, - @required this.initialCircles, - @required this.initialPolygons, - @required this.initialPolylines, - @required this.initialMarkers, - }) { - circlesController = CirclesController(stream: this.streamController); - polygonsController = PolygonsController(stream: this.streamController); - polylinesController = PolylinesController(stream: this.streamController); - markersController = MarkersController(stream: this.streamController); - html = HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$mapId'); - DivElement div = DivElement()..id = 'plugins.flutter.io/google_maps_$mapId'; - + @required Set initialCircles, + @required Set initialPolygons, + @required Set initialPolylines, + @required Set initialMarkers, + }) : this._mapId = mapId, + this._streamController = streamController { + _circlesController = CirclesController(stream: this._streamController); + _polygonsController = PolygonsController(stream: this._streamController); + _polylinesController = PolylinesController(stream: this._streamController); + _markersController = MarkersController(stream: this._streamController); + + // Create the widget. Note that we need to "leak" the div, so it can be used + // to build the gmaps.GMap object. + _widget = + HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$mapId'); + final div = DivElement()..id = 'plugins.flutter.io/google_maps_$mapId'; // TODO: Move the comment below to analysis-options.yaml // ignore:undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( 'plugins.flutter.io/google_maps_$mapId', (int viewId) => div, ); - - googleMap = gmaps.GMap(div, options); - onMapReady(googleMap); - _attachMapEvents(googleMap); - } - - void dispose() { - html = null; - googleMap = null; - circlesController = null; - polygonsController = null; - polylinesController = null; - markersController = null; - streamController.close(); - } - - /// Attaches/detaches a Traffic Layer on the current googleMap. - void setTrafficLayer(bool attach) { - if (attach && _trafficLayer == null) { - _trafficLayer = gmaps.TrafficLayer(); - _trafficLayer.set('map', googleMap); - googleMap.panBy(1, 0); - googleMap.panBy(-1, 0); - } - if (!attach && _trafficLayer != null) { - _trafficLayer.set('map', null); - _trafficLayer = null; - googleMap.panBy(1, 0); - googleMap.panBy(-1, 0); - } + _googleMap = gmaps.GMap(div, options); + + _attachMapEvents(_googleMap); + _attachGeometryControllers(_googleMap); + _renderInitialGeometry( + markers: initialMarkers, + circles: initialCircles, + polygons: initialPolygons, + polylines: initialPolylines, + ); } + // Funnels map gmap events into the plugin's stream controller. void _attachMapEvents(gmaps.GMap map) { map.onClick.listen((event) { - streamController.add( - MapTapEvent(mapId, _gmLatlngToLatlng(event.latLng)), + _streamController.add( + MapTapEvent(_mapId, _gmLatlngToLatlng(event.latLng)), ); }); map.onRightclick.listen((event) { - streamController.add( - MapLongPressEvent(mapId, _gmLatlngToLatlng(event.latLng)), + _streamController.add( + MapLongPressEvent(_mapId, _gmLatlngToLatlng(event.latLng)), ); }); map.onBoundsChanged.listen((event) { if (!_mapIsMoving) { _mapIsMoving = true; - streamController.add(CameraMoveStartedEvent(mapId)); + _streamController.add(CameraMoveStartedEvent(_mapId)); } - streamController.add( - CameraMoveEvent(mapId, _gmViewportToCameraPosition(map)), + _streamController.add( + CameraMoveEvent(_mapId, _gmViewportToCameraPosition(map)), ); }); map.onIdle.listen((event) { _mapIsMoving = false; - streamController.add(CameraIdleEvent(mapId)); + _streamController.add(CameraIdleEvent(_mapId)); }); } - void onMapReady(gmaps.GMap googleMap) { - this.googleMap = googleMap; - // Bind map instance to the other geometry controllers. - circlesController.bindToMap(mapId, googleMap); - polygonsController.bindToMap(mapId, googleMap); - polylinesController.bindToMap(mapId, googleMap); - markersController.bindToMap(mapId, googleMap); - updateInitialCircles(); - updateInitialPolygons(); - updateInitialPolylines(); - updateInitialMarkers(); + // Binds the Geometry controllers to a map instance + void _attachGeometryControllers(gmaps.GMap map) { + // Now we can add the initial geometry. + // And bind the (ready) map instance to the other geometry controllers. + _circlesController.bindToMap(_mapId, map); + _polygonsController.bindToMap(_mapId, map); + _polylinesController.bindToMap(_mapId, map); + _markersController.bindToMap(_mapId, map); + _controllersBoundToMap = true; } + // Renders the initial sets of geometry. + void _renderInitialGeometry({ + Set markers, + Set circles, + Set polygons, + Set polylines, + }) { + assert( + _controllersBoundToMap, + 'Geometry controllers must be bound to a map before any geometry can ' + + 'be added to them. Ensure _attachGeometryControllers is called first.'); + _markersController.addMarkers(markers); + _circlesController.addCircles(circles); + _polygonsController.addPolygons(polygons); + _polylinesController.addPolylines(polylines); + } + + /// Sets new [gmaps.MapOptions] on the wrapped map. void setOptions(gmaps.MapOptions options) { - googleMap?.options = options; + _googleMap?.options = options; } - void setInitialCircles(Set initialCircles) { - this.initialCircles = initialCircles; - if (googleMap != null) { - updateInitialCircles(); + /// Attaches/detaches a Traffic Layer on the current googleMap. + void setTrafficLayer(bool attach) { + if (attach && _trafficLayer == null) { + _trafficLayer = gmaps.TrafficLayer(); + _trafficLayer.set('map', _googleMap); + } + if (!attach && _trafficLayer != null) { + _trafficLayer.set('map', null); + _trafficLayer = null; } } - void updateInitialCircles() { - if (initialCircles == null) return; - circlesController.addCircles(initialCircles); + // _googleMap manipulation + // Viewport + + /// Returns the [LatLngBounds] of the current viewport. + Future getVisibleRegion() async { + return _gmLatLngBoundsTolatLngBounds(await _googleMap.bounds); } - void setInitialPolygons(Set initialPolygons) { - this.initialPolygons = initialPolygons; - if (googleMap != null) { - updateInitialPolygons(); - } + /// Returns the [ScreenCoordinate] for a given viewport [LatLng]. + Future getScreenCoordinate(LatLng latLng) async { + final point = + _googleMap.projection.fromLatLngToPoint(_latlngToGmLatlng(latLng)); + return ScreenCoordinate(x: point.x, y: point.y); } - void setInitialPolylines(Set initialPolylines) { - this.initialPolylines = initialPolylines; - if (googleMap != null) { - updateInitialPolylines(); - } + /// Returns the [LatLng] for a `screenCoordinate` (in pixels) of the viewport. + Future getLatLng(ScreenCoordinate screenCoordinate) async { + final latLng = _googleMap.projection.fromPointToLatLng( + gmaps.Point(screenCoordinate.x, screenCoordinate.y), + ); + return _gmLatlngToLatlng(latLng); } - void setInitialMarkers(Set initialMarkers) { - this.initialMarkers = initialMarkers; - if (googleMap != null) { - updateInitialMarkers(); - } + /// Applies a `cameraUpdate` to the current viewport. + Future moveCamera(CameraUpdate cameraUpdate) async { + return _applyCameraUpdate(_googleMap, cameraUpdate); + } + + /// Returns the zoom level of the current viewport. + Future getZoomLevel() async => _googleMap.zoom.toDouble(); + + // Geometry manipulation + + /// Applies [CircleUpdates] to the currently managed circles. + void updateCircles(CircleUpdates updates) { + _circlesController?.addCircles(updates.circlesToAdd); + _circlesController?.changeCircles(updates.circlesToChange); + _circlesController?.removeCircles(updates.circleIdsToRemove); + } + + /// Applies [PolygonUpdates] to the currently managed polygons. + void updatePolygons(PolygonUpdates updates) { + _polygonsController?.addPolygons(updates.polygonsToAdd); + _polygonsController?.changePolygons(updates.polygonsToChange); + _polygonsController?.removePolygons(updates.polygonIdsToRemove); + } + + /// Applies [PolylineUpdates] to the currently managed lines. + void updatePolylines(PolylineUpdates updates) { + _polylinesController?.addPolylines(updates.polylinesToAdd); + _polylinesController?.changePolylines(updates.polylinesToChange); + _polylinesController?.removePolylines(updates.polylineIdsToRemove); + } + + /// Applies [MarkerUpdates] to the currently managed markers. + void updateMarkers(MarkerUpdates updates) { + _markersController?.addMarkers(updates.markersToAdd); + _markersController?.changeMarkers(updates.markersToChange); + _markersController?.removeMarkers(updates.markerIdsToRemove); } - void updateInitialPolygons() { - if (initialPolygons == null) return; - polygonsController.addPolygons(initialPolygons); + /// Shows the [InfoWindow] of the marker identified by its [MarkerId]. + void showInfoWindow(MarkerId markerId) { + _markersController?.showMarkerInfoWindow(markerId); } - void updateInitialPolylines() { - if (initialPolylines == null) return; - polylinesController.addPolylines(initialPolylines); + /// Hides the [InfoWindow] of the marker identified by its [MarkerId]. + void hideInfoWindow(MarkerId markerId) { + _markersController?.hideMarkerInfoWindow(markerId); } - void updateInitialMarkers() { - if (initialMarkers == null) return; - markersController.addMarkers(initialMarkers); + /// Returns true if the [InfoWindow] of the marker identified by [MarkerId] is shown. + Future isInfoWindowShown(MarkerId markerId) async { + return _markersController?.isInfoWindowShown(markerId); + } + + // Cleanup + + /// Disposes of this controller and its resources. + void dispose() { + _widget = null; + _googleMap = null; + _circlesController = null; + _polygonsController = null; + _polylinesController = null; + _markersController = null; + _streamController.close(); } } +/// The base class for all "geometry" controllers. +/// +/// This lets all Geometry controllers be bound to a given mapID and GMap. abstract class AbstractController { + /// The GMap instance that this controller operates on. gmaps.GMap googleMap; + + /// The map ID for events. int mapId; + + /// Binds a mapId and its instance to this controller. void bindToMap(int mapId, gmaps.GMap googleMap) { this.mapId = mapId; this.googleMap = googleMap; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 92883cb7b024..00a20de23b8e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -9,19 +9,25 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { GoogleMapsFlutterPlatform.instance = GoogleMapsPlugin(); } - // This is a cache of rendered maps <-> GoogleMapControllers + // A cache of map controllers by map Id. Map _mapById = Map(); + // A cache of map options by map Id Map _optionsById = Map>(); + // A broadcast StreamControlled shared across all controllers. final StreamController _controller = StreamController.broadcast(); + // Convenience getter for a stream of events filtered by their mapId. Stream _events(int mapId) => _controller.stream.where((event) => event.mapId == mapId); - @override - Future init(int mapId) async { - /* Noop */ + // Convenience getter for a map controller by its mapId. + GoogleMapController _map(int mapId) { + final controller = _mapById[mapId]; + assert(controller != null, + 'Attempted to retrieve a map before its initialization!'); + return controller; } // Updates the cache of map options for a given mapId, so we can @@ -34,68 +40,64 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _optionsById[mapId]; } + @override + Future init(int mapId) async { + /* Noop */ + } + + /// Updates the options of a given `mapId`. + /// + /// This attempts to merge the new `optionsUpdate` passed in, with the previous + /// options passed to the map (in other updates, or when creating it). @override Future updateMapOptions( Map optionsUpdate, { @required int mapId, }) async { - GoogleMapController googleMapController = _mapById[mapId]; - if (googleMapController != null) { - final mergedRawOptions = _mergeRawMapOptions(optionsUpdate, mapId); - googleMapController.setOptions( - _optionsFromParams(mergedRawOptions), - ); - googleMapController - .setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); - } else { - throw StateError("updateMapOptions called prior to map initialization"); - } + final map = _map(mapId); + final mergedRawOptions = _mergeRawMapOptions(optionsUpdate, mapId); + + map.setOptions(_optionsFromParams(mergedRawOptions)); + map.setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); } + /// Applies the passed in `markerUpdates` to the `mapId`. @override Future updateMarkers( MarkerUpdates markerUpdates, { @required int mapId, }) async { - final markers = _mapById[mapId]?.markersController; - markers?.addMarkers(markerUpdates.markersToAdd); - markers?.changeMarkers(markerUpdates.markersToChange); - markers?.removeMarkers(markerUpdates.markerIdsToRemove); + _map(mapId).updateMarkers(markerUpdates); } + /// Applies the passed in `polygonUpdates` to the `mapId`. @override Future updatePolygons( PolygonUpdates polygonUpdates, { @required int mapId, }) async { - final polygons = _mapById[mapId]?.polygonsController; - polygons?.addPolygons(polygonUpdates.polygonsToAdd); - polygons?.changePolygons(polygonUpdates.polygonsToChange); - polygons?.removePolygons(polygonUpdates.polygonIdsToRemove); + _map(mapId).updatePolygons(polygonUpdates); } + /// Applies the passed in `polylineUpdates` to the `mapId`. @override Future updatePolylines( PolylineUpdates polylineUpdates, { @required int mapId, }) async { - final polylines = _mapById[mapId]?.polylinesController; - polylines?.addPolylines(polylineUpdates.polylinesToAdd); - polylines?.changePolylines(polylineUpdates.polylinesToChange); - polylines?.removePolylines(polylineUpdates.polylineIdsToRemove); + _map(mapId).updatePolylines(polylineUpdates); } + /// Applies the passed in `circleUpdates` to the `mapId`. @override Future updateCircles( CircleUpdates circleUpdates, { @required int mapId, }) async { - final circles = _mapById[mapId].circlesController; - circles?.addCircles(circleUpdates.circlesToAdd); - circles?.changeCircles(circleUpdates.circlesToChange); - circles?.removeCircles(circleUpdates.circleIdsToRemove); + _map(mapId).updateCircles(circleUpdates); } + /// Applies the given `cameraUpdate` to the current viewport (with animation). @override Future animateCamera( CameraUpdate cameraUpdate, { @@ -104,164 +106,97 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return moveCamera(cameraUpdate, mapId: mapId); } + /// Applies the given `cameraUpdate` to the current viewport. + // + // TODO: do not access googleMap directly !!! @override Future moveCamera( CameraUpdate cameraUpdate, { @required int mapId, }) async { - GoogleMapController googleMapController = _mapById[mapId]; - if (googleMapController == null) { - return null; - } - - gmaps.GMap map = googleMapController.googleMap; - // TODO: Subclass CameraUpdate so the below code is not so stringly-typed? - dynamic json = cameraUpdate.toJson(); - - switch (json[0]) { - case 'newCameraPosition': - map.heading = json[1]['bearing']; - map.zoom = json[1]['zoom']; - map.panTo(gmaps.LatLng(json[1]['target'][0], json[1]['target'][1])); - map.tilt = json[1]['tilt']; - break; - case 'newLatLng': - map.panTo(gmaps.LatLng(json[1][0], json[1][1])); - break; - case 'newLatLngZoom': - map.zoom = json[2]; - map.panTo(gmaps.LatLng(json[1][0], json[1][1])); - break; - case 'newLatLngBounds': - map.fitBounds(gmaps.LatLngBounds( - gmaps.LatLng(json[1][0][0], json[1][0][1]), - gmaps.LatLng(json[1][1][0], json[1][1][1]))); - // padding = json[2]; - // Needs package:google_maps ^4.0.0 to adjust the padding in fitBounds - break; - case 'scrollBy': - map.panBy(json[1], json[2]); - break; - case 'zoomBy': - double zoomDelta = json[1] ?? 0; - // Web only supports integer changes... - int newZoomDelta = zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); - map.zoom = map.zoom + newZoomDelta; - if (json.length == 3) { - // With focus - map.panTo(gmaps.LatLng(json[2][0], json[2][1])); - } - break; - case 'zoomIn': - map.zoom++; - break; - case 'zoomOut': - map.zoom--; - break; - case 'zoomTo': - map.zoom = json[1]; - break; - default: - throw UnimplementedError( - 'moveCamera() does not implement: ${json[0]}.'); - } + return _map(mapId).moveCamera(cameraUpdate); } + /// Sets the passed-in `mapStyle` to the map. + /// + /// This function just adds a 'styles' option to the current map options. + /// + /// Subsequent calls to this method override previous calls, you need to + /// pass full styles. @override Future setMapStyle( String mapStyle, { @required int mapId, }) async { - GoogleMapController googleMapController = _mapById[mapId]; - if (googleMapController != null) { - googleMapController.setOptions( - _optionsFromParams(_mergeRawMapOptions({ + _map(mapId).setOptions( + _optionsFromParams(_mergeRawMapOptions( + { 'styles': _mapStyles(mapStyle), - }, mapId)), - ); - } + }, + mapId, + )), + ); } + /// Returns the bounds of the current viewport. @override Future getVisibleRegion({ @required int mapId, }) { - GoogleMapController googleMapController = _mapById[mapId]; - if (googleMapController != null) { - gmaps.LatLngBounds latLngBounds = googleMapController.googleMap.bounds; - if (latLngBounds != null) { - return Future.value(_gmLatLngBoundsTolatLngBounds(latLngBounds)); - } - } - return Future.value( - LatLngBounds(southwest: LatLng(0, 0), northeast: LatLng(0, 0))); + return _map(mapId).getVisibleRegion(); } + /// Returns the screen coordinate (in pixels) of a given `latLng`. @override Future getScreenCoordinate( LatLng latLng, { @required int mapId, }) { - GoogleMapController googleMapController = _mapById[mapId]; - if (googleMapController != null) { - gmaps.Point point = googleMapController.googleMap.projection - .fromLatLngToPoint(_latlngToGmLatlng(latLng)); - return Future.value(ScreenCoordinate(x: point.x, y: point.y)); - } - return Future.error( - StateError("getScreenCoordinate called prior to map initialization")); + return _map(mapId).getScreenCoordinate(latLng); } + /// Returns the [LatLng] of a [ScreenCoordinate] of the viewport. @override Future getLatLng( ScreenCoordinate screenCoordinate, { @required int mapId, }) { - GoogleMapController googleMapController = _mapById[mapId]; - if (googleMapController != null) { - gmaps.LatLng latLng = googleMapController.googleMap.projection - .fromPointToLatLng( - gmaps.Point(screenCoordinate.x, screenCoordinate.y)); - return Future.value(_gmLatlngToLatlng(latLng)); - } - return Future.error( - StateError("getLatLng called prior to map initialization")); + return _map(mapId).getLatLng(screenCoordinate); } + /// Shows the [InfoWindow] (if any) of the [Marker] identified by `markerId`. @override Future showMarkerInfoWindow( MarkerId markerId, { @required int mapId, }) async { - GoogleMapController googleMapController = _mapById[mapId]; - googleMapController.markersController.showMarkerInfoWindow(markerId); + _map(mapId).showInfoWindow(markerId); } + /// Hides the [InfoWindow] (if any) of the [Marker] identified by `markerId`. @override Future hideMarkerInfoWindow( MarkerId markerId, { @required int mapId, }) async { - GoogleMapController googleMapController = _mapById[mapId]; - googleMapController.markersController.hideMarkerInfoWindow(markerId); + _map(mapId).hideInfoWindow(markerId); } + /// Returns true if the [InfoWindow] of the [Marker] identified by `markerId` is shown. @override Future isMarkerInfoWindowShown( MarkerId markerId, { @required int mapId, }) { - GoogleMapController googleMapController = _mapById[mapId]; - return Future.value( - googleMapController.markersController.isInfoWindowShown(markerId)); + return _map(mapId).isInfoWindowShown(markerId); } + // TODO: do not access googleMap directly !!! @override Future getZoomLevel({ @required int mapId, }) { - GoogleMapController googleMapController = _mapById[mapId]; - return Future.value(googleMapController.googleMap.zoom.toDouble()); + return _map(mapId).getZoomLevel(); } // The following are the 11 possible streams of data from the native side @@ -322,6 +257,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + /// Disposes of the current map. The map can't be used afterwards! // TODO: Make this method part of the interface! void dispose({@required int mapId}) { _mapById[mapId]?.dispose(); @@ -345,8 +281,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } // Bail fast if we've already rendered this mapId... - if (_mapById[mapId]?.html != null) { - return _mapById[mapId].html; + if (_mapById[mapId]?.widget != null) { + return _mapById[mapId].widget; } Map mergedRawOptions; @@ -355,8 +291,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } gmaps.MapOptions options = gmaps.MapOptions(); - CameraPosition position; - CircleUpdates initialCircles; PolygonUpdates initialPolygons; PolylineUpdates initialPolylines; @@ -374,7 +308,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } else if (key == 'circlesToAdd') { initialCircles = _circleFromParams(value); } else if (key == 'initialCameraPosition') { - position = CameraPosition.fromMap(value); + final position = CameraPosition.fromMap(value); options.zoom = position.zoom; options.center = gmaps.LatLng(position.target.latitude, position.target.longitude); @@ -383,12 +317,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } }); - final mapController = GoogleMapController.build( + final mapController = GoogleMapController( mapId: mapId, streamController: _controller, - onPlatformViewCreated: onPlatformViewCreated, options: options, - position: position, initialCircles: initialCircles?.circlesToAdd, initialPolygons: initialPolygons?.polygonsToAdd, initialPolylines: initialPolylines?.polylinesToAdd, @@ -403,6 +335,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { onPlatformViewCreated.call(mapId); - return mapController.html; + return mapController.widget; } } From 825eff86d5e11d8d3fd7d6b4db1b1c5df2ece19a Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 6 Aug 2020 15:01:03 -0700 Subject: [PATCH 48/58] Add e2e to dev_deps so the package becomes publishable --- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 771bdb3e9e52..b7fb676468c4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -27,6 +27,7 @@ dev_dependencies: url_launcher: ^5.2.5 pedantic: ^1.8.0 mockito: ^4.1.1 + e2e: ^0.6.1 environment: sdk: ">=2.3.0 <3.0.0" From de87feeff7e8fc8a0d1f964ec59c82d7e1db1596 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 6 Aug 2020 15:25:14 -0700 Subject: [PATCH 49/58] Make flutter analyze happy again. --- .../lib/src/google_maps_flutter_web.dart | 6 ++---- .../google_maps_flutter_web/test/lib/main.dart | 5 +++-- .../test/test_driver/marker_e2e.dart | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 00a20de23b8e..b0551e5a6f97 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -107,8 +107,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } /// Applies the given `cameraUpdate` to the current viewport. - // - // TODO: do not access googleMap directly !!! @override Future moveCamera( CameraUpdate cameraUpdate, { @@ -191,8 +189,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _map(mapId).isInfoWindowShown(markerId); } - // TODO: do not access googleMap directly !!! @override + /// Returns the zoom level of the `mapId`. Future getZoomLevel({ @required int mapId, }) { @@ -260,7 +258,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { /// Disposes of the current map. The map can't be used afterwards! // TODO: Make this method part of the interface! void dispose({@required int mapId}) { - _mapById[mapId]?.dispose(); + _map(mapId)?.dispose(); _mapById.remove(mapId); _optionsById.remove(mapId); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart index da6e56f8ea2b..10415204570c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/lib/main.dart @@ -8,12 +8,13 @@ void main() { runApp(MyApp()); } +/// App for testing class MyApp extends StatefulWidget { @override - MyAppState createState() => MyAppState(); + _MyAppState createState() => _MyAppState(); } -class MyAppState extends State { +class _MyAppState extends State { @override Widget build(BuildContext context) { return Text('Testing... Look at the console output for results!'); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart index 84c677d009f2..7e6727b47ba0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart @@ -79,7 +79,7 @@ void main() { final controller = MarkerController(marker: marker, infoWindow: infoWindow); controller.showInfoWindow(); - verify(infoWindow.open(any)).called(1); + verify(infoWindow.open(any, any)).called(1); expect(controller.infoWindowShown, isTrue); }); From 9f9a6dfeb578c3f697150de89415b490ec17ec17 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 6 Aug 2020 15:52:49 -0700 Subject: [PATCH 50/58] some of the infoWindow content may be null. --- .../google_maps_flutter_web/lib/src/convert.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 1032a1249d79..af77664092c6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -344,9 +344,9 @@ gmaps.InfoWindowOptions _infoWindowOptionsFromMarker(Marker marker) { } final content = '

' + - sanitizeHtml(marker.infoWindow.title) + + sanitizeHtml(marker.infoWindow.title ?? "") + '

' + - sanitizeHtml(marker.infoWindow.snippet); + sanitizeHtml(marker.infoWindow.snippet ?? ""); return gmaps.InfoWindowOptions() ..content = content From 8dd132c08b5aab478f19cc78f3963b62b25abe3b Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 6 Aug 2020 19:32:17 -0700 Subject: [PATCH 51/58] Do not share the StreamController across mapIds. --- .../lib/src/google_maps_flutter_web.dart | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index b0551e5a6f97..778dc6b1a890 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -11,16 +11,13 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { // A cache of map controllers by map Id. Map _mapById = Map(); - // A cache of map options by map Id + // A cache of map options by map Id. Map _optionsById = Map>(); - - // A broadcast StreamControlled shared across all controllers. - final StreamController _controller = - StreamController.broadcast(); + // A cache of Stream controllers by map Id. + Map _streamsById = Map>(); // Convenience getter for a stream of events filtered by their mapId. - Stream _events(int mapId) => - _controller.stream.where((event) => event.mapId == mapId); + Stream _events(int mapId) => _streamsById[mapId].stream; // Convenience getter for a map controller by its mapId. GoogleMapController _map(int mapId) { @@ -256,11 +253,13 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } /// Disposes of the current map. The map can't be used afterwards! - // TODO: Make this method part of the interface! + @override + // ignore:override_on_non_overriding_member void dispose({@required int mapId}) { _map(mapId)?.dispose(); _mapById.remove(mapId); _optionsById.remove(mapId); + _streamsById.remove(mapId); } @override @@ -311,13 +310,16 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { options.center = gmaps.LatLng(position.target.latitude, position.target.longitude); } else { - print('un-handle >>$key'); + print('Unknown creation parameter: $key'); } }); + final StreamController controller = + StreamController.broadcast(); + final mapController = GoogleMapController( mapId: mapId, - streamController: _controller, + streamController: controller, options: options, initialCircles: initialCircles?.circlesToAdd, initialPolygons: initialPolygons?.polygonsToAdd, @@ -326,6 +328,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { ); _mapById[mapId] = mapController; + _streamsById[mapId] = controller; mapController.setTrafficLayer( mergedRawOptions['trafficEnabled'] ?? false, From a1fe1d7f4c82373775f415b12ca2b5146d5159d8 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 7 Aug 2020 17:37:41 -0700 Subject: [PATCH 52/58] Do not cache the StreamController in the Plugin, leave it only in the Maps Controller. --- .../lib/src/google_maps_controller.dart | 5 ++++- .../lib/src/google_maps_flutter_web.dart | 6 +----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index a72fda155f3a..c0857aa39301 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -17,9 +17,12 @@ class GoogleMapController { // The underlying GMap instance. This is the interface with the JS SDK. gmaps.GMap _googleMap; - // The Stream over which all controllers broadcast their events. + // The StreamController used by this controller and the geometry ones. final StreamController _streamController; + /// The Stream over which this controller broadcasts events. + Stream get events => _streamController.stream; + // Geometry controllers, for different features of the map. CirclesController _circlesController; PolygonsController _polygonsController; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 778dc6b1a890..9f7e5f3d1d32 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -13,11 +13,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map _mapById = Map(); // A cache of map options by map Id. Map _optionsById = Map>(); - // A cache of Stream controllers by map Id. - Map _streamsById = Map>(); // Convenience getter for a stream of events filtered by their mapId. - Stream _events(int mapId) => _streamsById[mapId].stream; + Stream _events(int mapId) => _map(mapId).events; // Convenience getter for a map controller by its mapId. GoogleMapController _map(int mapId) { @@ -259,7 +257,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { _map(mapId)?.dispose(); _mapById.remove(mapId); _optionsById.remove(mapId); - _streamsById.remove(mapId); } @override @@ -328,7 +325,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { ); _mapById[mapId] = mapController; - _streamsById[mapId] = controller; mapController.setTrafficLayer( mergedRawOptions['trafficEnabled'] ?? false, From 941e511eecfcd408574a609e137ded4f0e52e7d2 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 12 Aug 2020 00:38:09 -0700 Subject: [PATCH 53/58] Update minimum platform_interface version to 1.0.4 to have an overridable dispose method. --- .../lib/src/google_maps_flutter_web.dart | 1 - .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 9f7e5f3d1d32..f9258b1ce4a7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -252,7 +252,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { /// Disposes of the current map. The map can't be used afterwards! @override - // ignore:override_on_non_overriding_member void dispose({@required int mapId}) { _map(mapId)?.dispose(); _mapById.remove(mapId); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index b7fb676468c4..a4a734ac773e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: flutter_web_plugins: sdk: flutter meta: ^1.1.7 - google_maps_flutter_platform_interface: ^1.0.1 + google_maps_flutter_platform_interface: ^1.0.4 google_maps: ^3.0.0 stream_transform: ^1.2.0 sanitize_html: ^1.3.0 From cb7a793d531898f9af6c5d0b9d55d880772abd6d Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 12 Aug 2020 16:25:56 -0700 Subject: [PATCH 54/58] Remove all 'options' handling from the plugin implementation, and into the GoogleMapController. --- .../lib/src/convert.dart | 313 +++++++++--------- .../lib/src/google_maps_controller.dart | 67 +++- .../lib/src/google_maps_flutter_web.dart | 78 +---- 3 files changed, 219 insertions(+), 239 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index af77664092c6..994c76706369 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -16,7 +16,7 @@ final _mapTypeToMapTypeId = { 4: gmaps.MapTypeId.HYBRID, }; -/// Converts options from the plugin into gmaps.MapOptions that can be used by the JS SDK. +// Converts options from the plugin into gmaps.MapOptions that can be used by the JS SDK. // The following options are not handled here, for various reasons: // The following are not available in web, because the map doesn't rotate there: // compassEnabled @@ -32,11 +32,10 @@ final _mapTypeToMapTypeId = { // indoorViewEnabled seems to not have an equivalent in web // buildingsEnabled seems to not have an equivalent in web // padding seems to behave differently in web than mobile. You can't move UI elements in web. -gmaps.MapOptions _optionsFromParams( - Map optionsUpdate, { - gmaps.MapOptions existingOptions, -}) { - gmaps.MapOptions options = existingOptions ?? gmaps.MapOptions(); +gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { + Map optionsUpdate = rawOptions['options'] ?? {}; + + gmaps.MapOptions options = gmaps.MapOptions(); if (_mapTypeToMapTypeId.containsKey(optionsUpdate['mapType'])) { options.mapTypeId = _mapTypeToMapTypeId[optionsUpdate['mapType']]; @@ -76,6 +75,26 @@ gmaps.MapOptions _optionsFromParams( return options; } +gmaps.MapOptions _setInitialPosition(Map rawOptions, gmaps.MapOptions options) { + // Adjust the initial position, if passed... + Map initialPosition = rawOptions['initialCameraPosition']; + if (initialPosition != null) { + final position = CameraPosition.fromMap(initialPosition); + options.zoom = position.zoom; + options.center = + gmaps.LatLng(position.target.latitude, position.target.longitude); + } + return options; +} + +// Extracts the status of the traffic layer from the rawOptions map. +bool _isTrafficLayerEnabled(Map rawOptions) { + if (rawOptions['options'] == null) { + return false; + } + return rawOptions['options']['trafficEnabled'] ?? false; +} + // Coverts the incoming JSON object into a List of MapTypeStyler objects. List _parseStylers(List stylerJsons) { return stylerJsons?.map((styler) { @@ -179,105 +198,6 @@ List _mapStyles(String mapStyleJson) { return styles; } -PolylineUpdates _polylineFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((polyline) { - PolylineId polylineId = PolylineId(polyline['polylineId']); - List points = []; - List jsonPoints = polyline['points']; - jsonPoints.forEach((p) { - points.add(LatLng.fromJson(p)); - }); - current.add(Polyline( - polylineId: polylineId, - consumeTapEvents: polyline['consumeTapEvents'], - color: Color(polyline['color']), - geodesic: polyline['geodesic'], - visible: polyline['visible'], - zIndex: polyline['zIndex'], - width: polyline['width'], - points: points, -// endCap = Cap.buttCap, -// jointType = JointType.mitered, -// patterns = const [], -// startCap = Cap.buttCap, - )); - }); - return PolylineUpdates.from(null, current); - } - return null; -} - -gmaps.PolylineOptions _polylineOptionsFromPolyline( - gmaps.GMap googleMap, Polyline polyline) { - List paths = []; - polyline.points.forEach((point) { - paths.add(_latlngToGmLatlng(point)); - }); - - return gmaps.PolylineOptions() - ..path = paths - ..strokeOpacity = 1.0 - ..strokeWeight = polyline.width - ..strokeColor = '#' + polyline.color.value.toRadixString(16).substring(0, 6) - ..visible = polyline.visible - ..zIndex = polyline.zIndex - ..geodesic = polyline.geodesic; -// this.endCap = Cap.buttCap, -// this.jointType = JointType.mitered, -// this.patterns = const [], -// this.startCap = Cap.buttCap, -// this.width = 10, -} - -PolygonUpdates _polygonFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((polygon) { - PolygonId polygonId = PolygonId(polygon['polygonId']); - List points = []; - List jsonPoints = polygon['points']; - jsonPoints.forEach((p) { - points.add(LatLng.fromJson(p)); - }); - current.add(Polygon( - polygonId: polygonId, - consumeTapEvents: polygon['consumeTapEvents'], - fillColor: Color(polygon['fillColor']), - geodesic: polygon['geodesic'], - strokeColor: Color(polygon['strokeColor']), - strokeWidth: polygon['strokeWidth'], - visible: polygon['visible'], - zIndex: polygon['zIndex'], - points: points, - )); - }); - return PolygonUpdates.from(null, current); - } - return null; -} - -gmaps.PolygonOptions _polygonOptionsFromPolygon( - gmaps.GMap googleMap, Polygon polygon) { - List paths = []; - polygon.points.forEach((point) { - paths.add(_latlngToGmLatlng(point)); - }); - return gmaps.PolygonOptions() - ..paths = paths - ..strokeColor = '#' + polygon.strokeColor.value.toRadixString(16) - ..strokeOpacity = 0.8 - ..strokeWeight = polygon.strokeWidth - ..fillColor = '#' + polygon.fillColor.value.toRadixString(16) - ..fillOpacity = 0.35 - ..visible = polygon.visible - ..zIndex = polygon.zIndex - ..geodesic = polygon.geodesic; -} - gmaps.LatLng _latlngToGmLatlng(LatLng latLng) { return gmaps.LatLng(latLng.latitude, latLng.longitude); } @@ -306,37 +226,105 @@ CameraPosition _gmViewportToCameraPosition(gmaps.GMap map) { ); } -MarkerUpdates _markerFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((marker) { - MarkerId markerId = MarkerId(marker['markerId']); - Offset offset = Offset((marker['anchor'][0]), (marker['anchor'][1])); - current.add(Marker( - markerId: markerId, - alpha: marker['alpha'], - anchor: offset, - consumeTapEvents: marker['consumeTapEvents'], - draggable: marker['draggable'], - flat: marker['flat'], - icon: BitmapDescriptor.defaultMarker, - infoWindow: InfoWindow( - title: marker['infoWindow']['title'] ?? '', - snippet: marker['snippet'], +Set _rawOptionsToInitialMarkers(Map rawOptions) { + final List> list = rawOptions['markersToAdd']; + Set markers = {}; + markers.addAll(list?.map((rawMarker) { + Offset offset = + Offset((rawMarker['anchor'][0]), (rawMarker['anchor'][1])); + return Marker( + markerId: MarkerId(rawMarker['markerId']), + alpha: rawMarker['alpha'], anchor: offset, - ), - position: LatLng.fromJson(marker['position']), - rotation: marker['rotation'], - visible: marker['visible'], - zIndex: marker['zIndex'], - )); - }); - return MarkerUpdates.from(null, current); - } - return null; + consumeTapEvents: rawMarker['consumeTapEvents'], + draggable: rawMarker['draggable'], + flat: rawMarker['flat'], + icon: BitmapDescriptor + .defaultMarker, // TODO: Doesn't this support custom icons? + infoWindow: InfoWindow( + title: rawMarker['infoWindow']['title'] ?? '', + snippet: rawMarker['snippet'], + anchor: offset, // TODO: Check this value. Is it correct? + ), + position: LatLng.fromJson(rawMarker['position']), + rotation: rawMarker['rotation'], + visible: rawMarker['visible'], + zIndex: rawMarker['zIndex'], + ); + }) ?? + []); + return markers; } +Set _rawOptionsToInitialCircles(Map rawOptions) { + final List> list = rawOptions['circlesToAdd']; + Set circles = {}; + circles.addAll(list?.map((rawCircle) { + return Circle( + circleId: CircleId(rawCircle['circleId']), + consumeTapEvents: rawCircle['consumeTapEvents'], + fillColor: Color(rawCircle['fillColor']), + center: LatLng.fromJson(rawCircle['center']), + radius: rawCircle['radius'], + strokeColor: Color(rawCircle['strokeColor']), + strokeWidth: rawCircle['strokeWidth'], + visible: rawCircle['visible'], + zIndex: rawCircle['zIndex'], + ); + }) ?? + []); + return circles; +} + +// Unsupported on the web: endCap, jointType, patterns and startCap. +Set _rawOptionsToInitialPolylines(Map rawOptions) { + final List> list = rawOptions['polylinesToAdd']; + Set polylines = {}; + polylines.addAll(list?.map((rawPolyline) { + return Polyline( + polylineId: PolylineId(rawPolyline['polylineId']), + consumeTapEvents: rawPolyline['consumeTapEvents'], + color: Color(rawPolyline['color']), + geodesic: rawPolyline['geodesic'], + visible: rawPolyline['visible'], + zIndex: rawPolyline['zIndex'], + width: rawPolyline['width'], + points: rawPolyline['points'] + ?.map((rawPoint) => LatLng.fromJson(rawPoint)) + ?.toList(), + ); + }) ?? + []); + return polylines; +} + +Set _rawOptionsToInitialPolygons(Map rawOptions) { + final List> list = rawOptions['polygonsToAdd']; + Set polygons = {}; + + polygons.addAll(list?.map((rawPolygon) { + return Polygon( + polygonId: PolygonId(rawPolygon['polygonId']), + consumeTapEvents: rawPolygon['consumeTapEvents'], + fillColor: Color(rawPolygon['fillColor']), + geodesic: rawPolygon['geodesic'], + strokeColor: Color(rawPolygon['strokeColor']), + strokeWidth: rawPolygon['strokeWidth'], + visible: rawPolygon['visible'], + zIndex: rawPolygon['zIndex'], + points: rawPolygon['points'] + ?.map((rawPoint) => LatLng.fromJson(rawPoint)) + ?.toList(), + ); + }) ?? + []); + return polygons; +} + +// Convert plugin objects to gmaps.Options objects +// TODO: Move to their appropriate objects, maybe make these copy constructors: +// Marker.fromMarker(anotherMarker, moreOptions); + gmaps.InfoWindowOptions _infoWindowOptionsFromMarker(Marker marker) { if ((marker.infoWindow?.title?.isEmpty ?? true) && (marker.infoWindow?.snippet?.isEmpty ?? true)) { @@ -411,27 +399,44 @@ gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { return populationOptions; } -CircleUpdates _circleFromParams(value) { - if (value != null) { - List> list = value; - Set current = Set(); - list.forEach((circle) { - CircleId circleId = CircleId(circle['circleId']); - current.add(Circle( - circleId: circleId, - consumeTapEvents: circle['consumeTapEvents'], - fillColor: Color(circle['fillColor']), - center: LatLng.fromJson(circle['center']), - radius: circle['radius'], - strokeColor: Color(circle['strokeColor']), - strokeWidth: circle['strokeWidth'], - visible: circle['visible'], - zIndex: circle['zIndex'], - )); - }); - return CircleUpdates.from(null, current); - } - return null; +gmaps.PolygonOptions _polygonOptionsFromPolygon( + gmaps.GMap googleMap, Polygon polygon) { + List paths = []; + polygon.points.forEach((point) { + paths.add(_latlngToGmLatlng(point)); + }); + return gmaps.PolygonOptions() + ..paths = paths + ..strokeColor = '#' + polygon.strokeColor.value.toRadixString(16) + ..strokeOpacity = 0.8 + ..strokeWeight = polygon.strokeWidth + ..fillColor = '#' + polygon.fillColor.value.toRadixString(16) + ..fillOpacity = 0.35 + ..visible = polygon.visible + ..zIndex = polygon.zIndex + ..geodesic = polygon.geodesic; +} + +gmaps.PolylineOptions _polylineOptionsFromPolyline( + gmaps.GMap googleMap, Polyline polyline) { + List paths = []; + polyline.points.forEach((point) { + paths.add(_latlngToGmLatlng(point)); + }); + + return gmaps.PolylineOptions() + ..path = paths + ..strokeOpacity = 1.0 + ..strokeWeight = polyline.width + ..strokeColor = '#' + polyline.color.value.toRadixString(16).substring(0, 6) + ..visible = polyline.visible + ..zIndex = polyline.zIndex + ..geodesic = polyline.geodesic; +// this.endCap = Cap.buttCap, +// this.jointType = JointType.mitered, +// this.patterns = const [], +// this.startCap = Cap.buttCap, +// this.width = 10, } // Translates a [CameraUpdate] into operations on a [gmaps.GMap]. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index c0857aa39301..d77a50ff1730 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -5,8 +5,15 @@ class GoogleMapController { // The internal ID of the map. Used to broadcast events, DOM IDs and everything where a unique ID is needed. final int _mapId; + // The raw options passed by the user, before converting to gmaps. + // Caching this allows us to re-create the map faithfully when needed. + Map _rawOptions = { + 'options': {}, + }; + // The Flutter widget that contains the rendered Map. HtmlElementView _widget; + HtmlElement _div; /// The Flutter widget that contains the rendered Map. Used for caching. HtmlElementView get widget => _widget; @@ -38,13 +45,10 @@ class GoogleMapController { GoogleMapController({ @required int mapId, @required StreamController streamController, - @required gmaps.MapOptions options, - @required Set initialCircles, - @required Set initialPolygons, - @required Set initialPolylines, - @required Set initialMarkers, + @required Map rawOptions, }) : this._mapId = mapId, - this._streamController = streamController { + this._streamController = streamController, + this._rawOptions = rawOptions { _circlesController = CirclesController(stream: this._streamController); _polygonsController = PolygonsController(stream: this._streamController); _polylinesController = PolylinesController(stream: this._streamController); @@ -54,23 +58,35 @@ class GoogleMapController { // to build the gmaps.GMap object. _widget = HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$mapId'); - final div = DivElement()..id = 'plugins.flutter.io/google_maps_$mapId'; + _div = DivElement()..id = 'plugins.flutter.io/google_maps_$mapId'; // TODO: Move the comment below to analysis-options.yaml // ignore:undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( 'plugins.flutter.io/google_maps_$mapId', - (int viewId) => div, + (int viewId) => _div, ); - _googleMap = gmaps.GMap(div, options); + } + + /// Initializes the [gmaps.GMap] instance from the stored `rawOptions`. + void init() { + var options = _rawOptionsToGmapsOptions(_rawOptions); + // Initial position can only to be set here! + options = _setInitialPosition(_rawOptions, options); + + // Create the map... + _googleMap = gmaps.GMap(_div, options); _attachMapEvents(_googleMap); _attachGeometryControllers(_googleMap); + _renderInitialGeometry( - markers: initialMarkers, - circles: initialCircles, - polygons: initialPolygons, - polylines: initialPolylines, + markers: _rawOptionsToInitialMarkers(_rawOptions), + circles: _rawOptionsToInitialCircles(_rawOptions), + polygons: _rawOptionsToInitialPolygons(_rawOptions), + polylines: _rawOptionsToInitialPolylines(_rawOptions), ); + + _setTrafficLayer(_isTrafficLayerEnabled(_rawOptions)); } // Funnels map gmap events into the plugin's stream controller. @@ -128,13 +144,34 @@ class GoogleMapController { _polylinesController.addPolylines(polylines); } + // Merges new options coming from the plugin into the 'options' entry of the + // _rawOptions map. + // Returns the updated _rawOptions object. + Map _mergeRawOptions(Map newOptions) { + _rawOptions['options'] = { + ..._rawOptions['options'], + ...newOptions, + }; + return _rawOptions; + } + + /// Updates the map options from a `Map`. + /// + /// This method converts the map into the proper [gmaps.MapOptions] + void updateRawOptions(Map optionsUpdate) { + final newOptions = _mergeRawOptions(optionsUpdate); + + _setOptions(_rawOptionsToGmapsOptions(newOptions)); + _setTrafficLayer(_isTrafficLayerEnabled(newOptions)); + } + /// Sets new [gmaps.MapOptions] on the wrapped map. - void setOptions(gmaps.MapOptions options) { + void _setOptions(gmaps.MapOptions options) { _googleMap?.options = options; } /// Attaches/detaches a Traffic Layer on the current googleMap. - void setTrafficLayer(bool attach) { + void _setTrafficLayer(bool attach) { if (attach && _trafficLayer == null) { _trafficLayer = gmaps.TrafficLayer(); _trafficLayer.set('map', _googleMap); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index f9258b1ce4a7..b8b1a62bb250 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -11,8 +11,6 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { // A cache of map controllers by map Id. Map _mapById = Map(); - // A cache of map options by map Id. - Map _optionsById = Map>(); // Convenience getter for a stream of events filtered by their mapId. Stream _events(int mapId) => _map(mapId).events; @@ -25,19 +23,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return controller; } - // Updates the cache of map options for a given mapId, so we can - // recrate the gmaps.MapOptions object from scratch. - Map _mergeRawMapOptions(dynamic newOptions, int mapId) { - _optionsById[mapId] = { - ..._optionsById[mapId] ?? {}, - ...newOptions, - }; - return _optionsById[mapId]; - } - @override Future init(int mapId) async { - /* Noop */ + _map(mapId).init(); } /// Updates the options of a given `mapId`. @@ -49,11 +37,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Map optionsUpdate, { @required int mapId, }) async { - final map = _map(mapId); - final mergedRawOptions = _mergeRawMapOptions(optionsUpdate, mapId); - - map.setOptions(_optionsFromParams(mergedRawOptions)); - map.setTrafficLayer(mergedRawOptions['trafficEnabled'] ?? false); + _map(mapId).updateRawOptions(optionsUpdate); } /// Applies the passed in `markerUpdates` to the `mapId`. @@ -121,14 +105,9 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { String mapStyle, { @required int mapId, }) async { - _map(mapId).setOptions( - _optionsFromParams(_mergeRawMapOptions( - { - 'styles': _mapStyles(mapStyle), - }, - mapId, - )), - ); + _map(mapId).updateRawOptions({ + 'styles': _mapStyles(mapStyle), + }); } /// Returns the bounds of the current viewport. @@ -184,8 +163,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _map(mapId).isInfoWindowShown(markerId); } - @override /// Returns the zoom level of the `mapId`. + @override Future getZoomLevel({ @required int mapId, }) { @@ -250,12 +229,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } - /// Disposes of the current map. The map can't be used afterwards! + /// Disposes of the current map. It can't be used afterwards! @override void dispose({@required int mapId}) { _map(mapId)?.dispose(); _mapById.remove(mapId); - _optionsById.remove(mapId); } @override @@ -278,57 +256,17 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _mapById[mapId].widget; } - Map mergedRawOptions; - if (creationParams['options'] != null) { - mergedRawOptions = _mergeRawMapOptions(creationParams['options'], mapId); - } - - gmaps.MapOptions options = gmaps.MapOptions(); - CircleUpdates initialCircles; - PolygonUpdates initialPolygons; - PolylineUpdates initialPolylines; - MarkerUpdates initialMarkers; - - creationParams.forEach((key, value) { - if (key == 'options') { - _optionsFromParams(mergedRawOptions, existingOptions: options); - } else if (key == 'markersToAdd') { - initialMarkers = _markerFromParams(value); - } else if (key == 'polygonsToAdd') { - initialPolygons = _polygonFromParams(value); - } else if (key == 'polylinesToAdd') { - initialPolylines = _polylineFromParams(value); - } else if (key == 'circlesToAdd') { - initialCircles = _circleFromParams(value); - } else if (key == 'initialCameraPosition') { - final position = CameraPosition.fromMap(value); - options.zoom = position.zoom; - options.center = - gmaps.LatLng(position.target.latitude, position.target.longitude); - } else { - print('Unknown creation parameter: $key'); - } - }); - final StreamController controller = StreamController.broadcast(); final mapController = GoogleMapController( mapId: mapId, streamController: controller, - options: options, - initialCircles: initialCircles?.circlesToAdd, - initialPolygons: initialPolygons?.polygonsToAdd, - initialPolylines: initialPolylines?.polylinesToAdd, - initialMarkers: initialMarkers?.markersToAdd, + rawOptions: creationParams, ); _mapById[mapId] = mapController; - mapController.setTrafficLayer( - mergedRawOptions['trafficEnabled'] ?? false, - ); - onPlatformViewCreated.call(mapId); return mapController.widget; From ac1d09aedf696acc087b473581834fb1a90a3230 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 13 Aug 2020 16:09:12 -0700 Subject: [PATCH 55/58] Add tests for GoogleMapsPlugin class. --- .../lib/src/google_maps_flutter_web.dart | 8 +- .../test_driver/google_maps_plugin_e2e.dart | 388 ++++++++++++++++++ .../google_maps_plugin_e2e_test.dart | 7 + 3 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index b8b1a62bb250..fae4a8a4135c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -12,6 +12,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { // A cache of map controllers by map Id. Map _mapById = Map(); + /// Allows tests to inject controllers without going through the buildView flow. + @visibleForTesting + void debugSetMapById(Map mapById) { + _mapById = mapById; + } + // Convenience getter for a stream of events filtered by their mapId. Stream _events(int mapId) => _map(mapId).events; @@ -19,7 +25,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { GoogleMapController _map(int mapId) { final controller = _mapById[mapId]; assert(controller != null, - 'Attempted to retrieve a map before its initialization!'); + 'Maps cannot be retrieved before calling buildView!'); return controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart new file mode 100644 index 000000000000..1f14a9108d2f --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart @@ -0,0 +1,388 @@ +import 'dart:async'; + +import 'package:e2e/e2e.dart'; +import 'package:flutter/widgets.dart'; +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +class _MockGoogleMapController extends Mock implements GoogleMapController {} + +/// Test GoogleMapsPlugin +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + group('GoogleMapsPlugin', () { + _MockGoogleMapController controller; + GoogleMapsPlugin plugin; + int reportedMapId; + + void onPlatformViewCreated(int id) { + reportedMapId = id; + } + + setUp(() { + controller = _MockGoogleMapController(); + plugin = GoogleMapsPlugin(); + reportedMapId = null; + }); + + group('init/dispose', () { + group('before buildWidget', () { + testWidgets('init throws assertion', (WidgetTester tester) async { + expect(() => plugin.init(0), throwsAssertionError); + }); + }); + + group('after buildWidget', () { + setUp(() { + plugin.debugSetMapById({0: controller}); + }); + + testWidgets('init initializes controller', (WidgetTester tester) async { + await plugin.init(0); + + verify(controller.init()); + }); + + testWidgets('cannot call methods after dispose', + (WidgetTester tester) async { + plugin.dispose(mapId: 0); + + verify(controller.dispose()); + expect( + () => plugin.init(0), + throwsAssertionError, + reason: 'Method calls should fail after dispose.', + ); + }); + }); + }); + + group('buildView', () { + final testMapId = 33930; + + testWidgets('throws without _webOnlyMapCreationId', + (WidgetTester tester) async { + expect( + () => plugin.buildView({}, null, onPlatformViewCreated), + throwsException, + reason: + '_webOnlyMapCreationId is mandatory to prevent unnecessary reloads in web.', + ); + }); + + testWidgets( + 'returns an HtmlElementView and caches the controller for later', + (WidgetTester tester) async { + final Map cache = {}; + plugin.debugSetMapById(cache); + + final HtmlElementView widget = plugin.buildView({ + '_webOnlyMapCreationId': testMapId, + }, null, onPlatformViewCreated); + + expect( + widget.viewType, + contains('$testMapId'), + reason: + 'view type should contain the mapId passed when creating the map.', + ); + expect( + reportedMapId, + testMapId, + reason: 'Should call onPlatformViewCreated with the mapId', + ); + expect(cache, contains(testMapId)); + expect( + cache[testMapId], + isNotNull, + reason: 'cached controller cannot be null.', + ); + }); + + testWidgets('returns cached instance if it already exists', + (WidgetTester tester) async { + final expected = HtmlElementView(viewType: 'only-for-testing'); + when(controller.widget).thenReturn(expected); + plugin.debugSetMapById({testMapId: controller}); + + final widget = plugin.buildView({ + '_webOnlyMapCreationId': testMapId, + }, null, onPlatformViewCreated); + + expect(widget, equals(expected)); + expect( + reportedMapId, + isNull, + reason: + 'onPlatformViewCreated should not be called when returning a cached controller', + ); + }); + }); + + group('setMapStyles', () { + String mapStyle = '''[{ + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [{"color": "#6b9a76"}] + }]'''; + + testWidgets('translates styles for controller', + (WidgetTester tester) async { + plugin.debugSetMapById({0: controller}); + + await plugin.setMapStyle(mapStyle, mapId: 0); + + var captured = + verify(controller.updateRawOptions(captureThat(isMap))).captured[0]; + + expect(captured, contains('styles')); + var styles = captured['styles']; + expect(styles.length, 1); + // Let's peek inside the styles... + var style = styles[0] as gmaps.MapTypeStyle; + expect(style.featureType, gmaps.MapTypeStyleFeatureType.POI_PARK); + expect( + style.elementType, gmaps.MapTypeStyleElementType.LABELS_TEXT_FILL); + expect(style.stylers.length, 1); + expect(style.stylers[0].color, '#6b9a76'); + }); + }); + + // These methods only pass-through values from the plugin to the controller + // so we verify them all together here... + group('Pass-through methods:', () { + int mapId = 0; + setUp(() { + plugin.debugSetMapById({mapId: controller}); + }); + // Options + testWidgets('updateMapOptions', (WidgetTester tester) async { + final expectedMapOptions = {'someOption': 12345}; + + await plugin.updateMapOptions(expectedMapOptions, mapId: mapId); + + verify(controller.updateRawOptions(expectedMapOptions)); + }); + // Geometry + testWidgets('updateMarkers', (WidgetTester tester) async { + final expectedUpdates = MarkerUpdates.from(null, null); + + await plugin.updateMarkers(expectedUpdates, mapId: mapId); + + verify(controller.updateMarkers(expectedUpdates)); + }); + testWidgets('updatePolygons', (WidgetTester tester) async { + final expectedUpdates = PolygonUpdates.from(null, null); + + await plugin.updatePolygons(expectedUpdates, mapId: mapId); + + verify(controller.updatePolygons(expectedUpdates)); + }); + testWidgets('updatePolylines', (WidgetTester tester) async { + final expectedUpdates = PolylineUpdates.from(null, null); + + await plugin.updatePolylines(expectedUpdates, mapId: mapId); + + verify(controller.updatePolylines(expectedUpdates)); + }); + testWidgets('updateCircles', (WidgetTester tester) async { + final expectedUpdates = CircleUpdates.from(null, null); + + await plugin.updateCircles(expectedUpdates, mapId: mapId); + + verify(controller.updateCircles(expectedUpdates)); + }); + // Camera + testWidgets('animateCamera', (WidgetTester tester) async { + final expectedUpdates = + CameraUpdate.newLatLng(LatLng(43.3626, -5.8433)); + + await plugin.animateCamera(expectedUpdates, mapId: mapId); + + verify(controller.moveCamera(expectedUpdates)); + }); + testWidgets('moveCamera', (WidgetTester tester) async { + final expectedUpdates = + CameraUpdate.newLatLng(LatLng(43.3628, -5.8478)); + + await plugin.moveCamera(expectedUpdates, mapId: mapId); + + verify(controller.moveCamera(expectedUpdates)); + }); + // Viewport + testWidgets('getVisibleRegion', (WidgetTester tester) async { + await plugin.getVisibleRegion(mapId: mapId); + + verify(controller.getVisibleRegion()); + }); + testWidgets('getZoomLevel', (WidgetTester tester) async { + await plugin.getZoomLevel(mapId: mapId); + + verify(controller.getZoomLevel()); + }); + testWidgets('getScreenCoordinate', (WidgetTester tester) async { + final latLng = LatLng(43.3613, -5.8499); + + await plugin.getScreenCoordinate(latLng, mapId: mapId); + + verify(controller.getScreenCoordinate(latLng)); + }); + testWidgets('getLatLng', (WidgetTester tester) async { + final coordinates = ScreenCoordinate(x: 19, y: 26); + + await plugin.getLatLng(coordinates, mapId: mapId); + + verify(controller.getLatLng(coordinates)); + }); + // InfoWindows + testWidgets('showMarkerInfoWindow', (WidgetTester tester) async { + final markerId = MarkerId('testing-123'); + + await plugin.showMarkerInfoWindow(markerId, mapId: mapId); + + verify(controller.showInfoWindow(markerId)); + }); + testWidgets('hideMarkerInfoWindow', (WidgetTester tester) async { + final markerId = MarkerId('testing-123'); + + await plugin.hideMarkerInfoWindow(markerId, mapId: mapId); + + verify(controller.hideInfoWindow(markerId)); + }); + testWidgets('isMarkerInfoWindowShown', (WidgetTester tester) async { + final markerId = MarkerId('testing-123'); + + await plugin.isMarkerInfoWindowShown(markerId, mapId: mapId); + + verify(controller.isInfoWindowShown(markerId)); + }); + }); + + // Verify all event streams are filtered correctly from the main one... + group('Event Streams', () { + int mapId = 0; + StreamController streamController; + setUp(() { + streamController = StreamController.broadcast(); + when(controller.events) + .thenAnswer((realInvocation) => streamController.stream); + plugin.debugSetMapById({mapId: controller}); + }); + + // Dispatches a few events in the global streamController, and expects *only* the passed event to be there. + void _testStreamFiltering(Stream stream, MapEvent event) async { + Timer.run(() { + streamController.add(_OtherMapEvent(mapId)); + streamController.add(event); + streamController.add(_OtherMapEvent(mapId)); + streamController.close(); + }); + + final events = await stream.toList(); + + expect(events.length, 1); + expect(events[0], event); + } + + // Camera events + testWidgets('onCameraMoveStarted', (WidgetTester tester) async { + final event = CameraMoveStartedEvent(mapId); + + final stream = plugin.onCameraMoveStarted(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + testWidgets('onCameraMoveStarted', (WidgetTester tester) async { + final event = CameraMoveEvent( + mapId, + CameraPosition( + target: LatLng(43.3790, -5.8660), + ), + ); + + final stream = plugin.onCameraMove(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + testWidgets('onCameraIdle', (WidgetTester tester) async { + final event = CameraIdleEvent(mapId); + + final stream = plugin.onCameraIdle(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + // Marker events + testWidgets('onMarkerTap', (WidgetTester tester) async { + final event = MarkerTapEvent(mapId, MarkerId('test-123')); + + final stream = plugin.onMarkerTap(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + testWidgets('onInfoWindowTap', (WidgetTester tester) async { + final event = InfoWindowTapEvent(mapId, MarkerId('test-123')); + + final stream = plugin.onInfoWindowTap(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + testWidgets('onMarkerDragEnd', (WidgetTester tester) async { + final event = MarkerDragEndEvent( + mapId, + LatLng(43.3677, -5.8372), + MarkerId('test-123'), + ); + + final stream = plugin.onMarkerDragEnd(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + // Geometry + testWidgets('onPolygonTap', (WidgetTester tester) async { + final event = PolygonTapEvent(mapId, PolygonId('test-123')); + + final stream = plugin.onPolygonTap(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + testWidgets('onPolylineTap', (WidgetTester tester) async { + final event = PolylineTapEvent(mapId, PolylineId('test-123')); + + final stream = plugin.onPolylineTap(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + testWidgets('onCircleTap', (WidgetTester tester) async { + final event = CircleTapEvent(mapId, CircleId('test-123')); + + final stream = plugin.onCircleTap(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + // Map taps + testWidgets('onTap', (WidgetTester tester) async { + final event = MapTapEvent(mapId, LatLng(43.3597, -5.8458)); + + final stream = plugin.onTap(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + testWidgets('onLongPress', (WidgetTester tester) async { + final event = MapLongPressEvent(mapId, LatLng(43.3608, -5.8425)); + + final stream = plugin.onLongPress(mapId: mapId); + + await _testStreamFiltering(stream, event); + }); + }); + }); +} + +class _OtherMapEvent extends MapEvent { + _OtherMapEvent(int mapId) : super(mapId, null); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart @@ -0,0 +1,7 @@ +// 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 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); From b0591961703012f50684da6636efb8dda5b1a7c9 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Sat, 15 Aug 2020 21:55:09 -0700 Subject: [PATCH 56/58] Add tests for google_maps_controller.dart * Add a few changes to the controller to allow for overrides when testing. * Make some conversion functions more null-resilient. * Fix some inconsistencies in some conversion function naming. --- .../lib/src/convert.dart | 107 +++- .../lib/src/google_maps_controller.dart | 97 +++- .../lib/src/google_maps_flutter_web.dart | 2 +- .../lib/src/markers.dart | 2 +- .../google_maps_controller_e2e.dart | 509 ++++++++++++++++++ .../google_maps_controller_e2e_test.dart | 7 + 6 files changed, 669 insertions(+), 55 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 994c76706369..63cccd87d4e0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -6,6 +6,10 @@ final _nullLatLngBounds = LatLngBounds( southwest: _nullLatLng, ); +// Defaults taken from the Google Maps Platform SDK documentation. +final _defaultStrokeColor = Colors.black.value; +final _defaultFillColor = Colors.transparent.value; + // Indices in the plugin side don't match with the ones // in the gmaps lib. This translates from plugin -> gmaps. final _mapTypeToMapTypeId = { @@ -75,7 +79,10 @@ gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { return options; } -gmaps.MapOptions _setInitialPosition(Map rawOptions, gmaps.MapOptions options) { +gmaps.MapOptions _applyInitialPosition( + Map rawOptions, + gmaps.MapOptions options, +) { // Adjust the initial position, if passed... Map initialPosition = rawOptions['initialCameraPosition']; if (initialPosition != null) { @@ -198,11 +205,13 @@ List _mapStyles(String mapStyleJson) { return styles; } -gmaps.LatLng _latlngToGmLatlng(LatLng latLng) { +gmaps.LatLng _latLngToGmLatLng(LatLng latLng) { + if (latLng == null) return null; return gmaps.LatLng(latLng.latitude, latLng.longitude); } -LatLng _gmLatlngToLatlng(gmaps.LatLng latLng) { +LatLng _gmLatLngToLatLng(gmaps.LatLng latLng) { + if (latLng == null) return _nullLatLng; return LatLng(latLng.lat, latLng.lng); } @@ -212,17 +221,17 @@ LatLngBounds _gmLatLngBoundsTolatLngBounds(gmaps.LatLngBounds latLngBounds) { } return LatLngBounds( - southwest: _gmLatlngToLatlng(latLngBounds.southWest), - northeast: _gmLatlngToLatlng(latLngBounds.northEast), + southwest: _gmLatLngToLatLng(latLngBounds.southWest), + northeast: _gmLatLngToLatLng(latLngBounds.northEast), ); } CameraPosition _gmViewportToCameraPosition(gmaps.GMap map) { return CameraPosition( - target: _gmLatlngToLatlng(map.center), + target: _gmLatLngToLatLng(map.center), bearing: map.heading ?? 0, - tilt: map.tilt, - zoom: map.zoom.toDouble(), + tilt: map.tilt ?? 0, + zoom: map.zoom?.toDouble() ?? 10, ); } @@ -230,8 +239,24 @@ Set _rawOptionsToInitialMarkers(Map rawOptions) { final List> list = rawOptions['markersToAdd']; Set markers = {}; markers.addAll(list?.map((rawMarker) { - Offset offset = - Offset((rawMarker['anchor'][0]), (rawMarker['anchor'][1])); + Offset offset; + LatLng position; + InfoWindow infoWindow; + if (rawMarker['anchor'] != null) { + offset = Offset((rawMarker['anchor'][0]), (rawMarker['anchor'][1])); + } + if (rawMarker['position'] != null) { + position = LatLng.fromJson(rawMarker['position']); + } + if (rawMarker['infoWindow'] != null || rawMarker['snippet'] != null) { + String title = rawMarker['infoWindow'] != null + ? rawMarker['infoWindow']['title'] + : null; + infoWindow = InfoWindow( + title: title ?? '', + snippet: rawMarker['snippet'] ?? '', + ); + } return Marker( markerId: MarkerId(rawMarker['markerId']), alpha: rawMarker['alpha'], @@ -239,14 +264,10 @@ Set _rawOptionsToInitialMarkers(Map rawOptions) { consumeTapEvents: rawMarker['consumeTapEvents'], draggable: rawMarker['draggable'], flat: rawMarker['flat'], - icon: BitmapDescriptor - .defaultMarker, // TODO: Doesn't this support custom icons? - infoWindow: InfoWindow( - title: rawMarker['infoWindow']['title'] ?? '', - snippet: rawMarker['snippet'], - anchor: offset, // TODO: Check this value. Is it correct? - ), - position: LatLng.fromJson(rawMarker['position']), + // TODO: Doesn't this support custom icons? + icon: BitmapDescriptor.defaultMarker, + infoWindow: infoWindow, + position: position ?? _nullLatLng, rotation: rawMarker['rotation'], visible: rawMarker['visible'], zIndex: rawMarker['zIndex'], @@ -260,13 +281,17 @@ Set _rawOptionsToInitialCircles(Map rawOptions) { final List> list = rawOptions['circlesToAdd']; Set circles = {}; circles.addAll(list?.map((rawCircle) { + LatLng center; + if (rawCircle['center'] != null) { + center = LatLng.fromJson(rawCircle['center']); + } return Circle( circleId: CircleId(rawCircle['circleId']), consumeTapEvents: rawCircle['consumeTapEvents'], - fillColor: Color(rawCircle['fillColor']), - center: LatLng.fromJson(rawCircle['center']), + fillColor: Color(rawCircle['fillColor'] ?? _defaultFillColor), + center: center ?? _nullLatLng, radius: rawCircle['radius'], - strokeColor: Color(rawCircle['strokeColor']), + strokeColor: Color(rawCircle['strokeColor'] ?? _defaultStrokeColor), strokeWidth: rawCircle['strokeWidth'], visible: rawCircle['visible'], zIndex: rawCircle['zIndex'], @@ -284,7 +309,7 @@ Set _rawOptionsToInitialPolylines(Map rawOptions) { return Polyline( polylineId: PolylineId(rawPolyline['polylineId']), consumeTapEvents: rawPolyline['consumeTapEvents'], - color: Color(rawPolyline['color']), + color: Color(rawPolyline['color'] ?? _defaultStrokeColor), geodesic: rawPolyline['geodesic'], visible: rawPolyline['visible'], zIndex: rawPolyline['zIndex'], @@ -306,9 +331,9 @@ Set _rawOptionsToInitialPolygons(Map rawOptions) { return Polygon( polygonId: PolygonId(rawPolygon['polygonId']), consumeTapEvents: rawPolygon['consumeTapEvents'], - fillColor: Color(rawPolygon['fillColor']), + fillColor: Color(rawPolygon['fillColor'] ?? _defaultFillColor), geodesic: rawPolygon['geodesic'], - strokeColor: Color(rawPolygon['strokeColor']), + strokeColor: Color(rawPolygon['strokeColor'] ?? _defaultStrokeColor), strokeWidth: rawPolygon['strokeWidth'], visible: rawPolygon['visible'], zIndex: rawPolygon['zIndex'], @@ -403,7 +428,7 @@ gmaps.PolygonOptions _polygonOptionsFromPolygon( gmaps.GMap googleMap, Polygon polygon) { List paths = []; polygon.points.forEach((point) { - paths.add(_latlngToGmLatlng(point)); + paths.add(_latLngToGmLatLng(point)); }); return gmaps.PolygonOptions() ..paths = paths @@ -421,7 +446,7 @@ gmaps.PolylineOptions _polylineOptionsFromPolyline( gmaps.GMap googleMap, Polyline polyline) { List paths = []; polyline.points.forEach((point) { - paths.add(_latlngToGmLatlng(point)); + paths.add(_latLngToGmLatLng(point)); }); return gmaps.PolylineOptions() @@ -467,13 +492,22 @@ void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { map.panBy(json[1], json[2]); break; case 'zoomBy': + gmaps.LatLng focusLatLng; double zoomDelta = json[1] ?? 0; // Web only supports integer changes... int newZoomDelta = zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); - map.zoom = map.zoom + newZoomDelta; if (json.length == 3) { // With focus - map.panTo(gmaps.LatLng(json[2][0], json[2][1])); + try { + focusLatLng = _pixelToLatLng(map, json[2][0], json[2][1]); + } catch (e) { + // https://github.com/a14n/dart-google-maps/issues/87 + // print('Error computing new focus LatLng. JS Error: ' + e.toString()); + } + } + map.zoom = map.zoom + newZoomDelta; + if (focusLatLng != null) { + map.panTo(focusLatLng); } break; case 'zoomIn': @@ -489,3 +523,20 @@ void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { throw UnimplementedError('Unimplemented CameraMove: ${json[0]}.'); } } + +// original JS by: Byron Singh (https://stackoverflow.com/a/30541162) +gmaps.LatLng _pixelToLatLng(gmaps.GMap map, int x, int y) { + final ne = map.bounds.northEast; + final sw = map.bounds.southWest; + final projection = map.projection; + + final topRight = projection.fromLatLngToPoint(ne); + final bottomLeft = projection.fromLatLngToPoint(sw); + + final scale = 1 << map.zoom; // 2 ^ zoom + + final point = + gmaps.Point((x / scale) + bottomLeft.x, (y / scale) + topRight.y); + + return projection.fromPointToLatLng(point); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index d77a50ff1730..a85ae095ed5c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -1,5 +1,10 @@ part of google_maps_flutter_web; +/// Type used when passing an override to the _createMap function. +@visibleForTesting +typedef DebugCreateMapFunction = gmaps.GMap Function( + HtmlElement div, gmaps.MapOptions options); + /// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered. class GoogleMapController { // The internal ID of the map. Used to broadcast events, DOM IDs and everything where a unique ID is needed. @@ -11,16 +16,30 @@ class GoogleMapController { 'options': {}, }; + // Creates the 'viewType' for the _widget + String _getViewType(int mapId) => 'plugins.flutter.io/google_maps_$mapId'; + // The Flutter widget that contains the rendered Map. HtmlElementView _widget; HtmlElement _div; - /// The Flutter widget that contains the rendered Map. Used for caching. - HtmlElementView get widget => _widget; + /// The Flutter widget that will contain the rendered Map. Used for caching. + HtmlElementView get widget { + if (_widget == null && !_streamController.isClosed) { + _widget = HtmlElementView( + viewType: _getViewType(_mapId), + ); + } + return _widget; + } // The currently-enabled traffic layer. gmaps.TrafficLayer _trafficLayer; + /// A getter for the current traffic layer. Only for tests. + @visibleForTesting + gmaps.TrafficLayer get trafficLayer => _trafficLayer; + // The underlying GMap instance. This is the interface with the JS SDK. gmaps.GMap _googleMap; @@ -54,27 +73,50 @@ class GoogleMapController { _polylinesController = PolylinesController(stream: this._streamController); _markersController = MarkersController(stream: this._streamController); - // Create the widget. Note that we need to "leak" the div, so it can be used - // to build the gmaps.GMap object. - _widget = - HtmlElementView(viewType: 'plugins.flutter.io/google_maps_$mapId'); - _div = DivElement()..id = 'plugins.flutter.io/google_maps_$mapId'; + // Register the view factory that will hold the `_div` that will hold the map. + // Note that we need to "leak" the div, so it can be used to build the gmaps.GMap object on init(). + _div = DivElement()..id = _getViewType(mapId); // TODO: Move the comment below to analysis-options.yaml // ignore:undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( - 'plugins.flutter.io/google_maps_$mapId', + _getViewType(mapId), (int viewId) => _div, ); } + /// Overrides certain properties to install mocks defined during testing. + @visibleForTesting + void debugSetOverrides({ + DebugCreateMapFunction createMap, + MarkersController markers, + CirclesController circles, + PolygonsController polygons, + PolylinesController polylines, + }) { + _overrideCreateMap = createMap; + _markersController = markers ?? _markersController; + _circlesController = circles ?? _circlesController; + _polygonsController = polygons ?? _polygonsController; + _polylinesController = polylines ?? _polylinesController; + } + + DebugCreateMapFunction _overrideCreateMap; + + gmaps.GMap _createMap(HtmlElement div, gmaps.MapOptions options) { + if (_overrideCreateMap != null) { + return _overrideCreateMap(div, options); + } + return gmaps.GMap(div, options); + } + /// Initializes the [gmaps.GMap] instance from the stored `rawOptions`. void init() { var options = _rawOptionsToGmapsOptions(_rawOptions); // Initial position can only to be set here! - options = _setInitialPosition(_rawOptions, options); + options = _applyInitialPosition(_rawOptions, options); // Create the map... - _googleMap = gmaps.GMap(_div, options); + _googleMap = _createMap(_div, options); _attachMapEvents(_googleMap); _attachGeometryControllers(_googleMap); @@ -86,19 +128,19 @@ class GoogleMapController { polylines: _rawOptionsToInitialPolylines(_rawOptions), ); - _setTrafficLayer(_isTrafficLayerEnabled(_rawOptions)); + _setTrafficLayer(_googleMap, _isTrafficLayerEnabled(_rawOptions)); } // Funnels map gmap events into the plugin's stream controller. void _attachMapEvents(gmaps.GMap map) { map.onClick.listen((event) { _streamController.add( - MapTapEvent(_mapId, _gmLatlngToLatlng(event.latLng)), + MapTapEvent(_mapId, _gmLatLngToLatLng(event.latLng)), ); }); map.onRightclick.listen((event) { _streamController.add( - MapLongPressEvent(_mapId, _gmLatlngToLatlng(event.latLng)), + MapLongPressEvent(_mapId, _gmLatLngToLatLng(event.latLng)), ); }); map.onBoundsChanged.listen((event) { @@ -144,12 +186,17 @@ class GoogleMapController { _polylinesController.addPolylines(polylines); } - // Merges new options coming from the plugin into the 'options' entry of the - // _rawOptions map. + // Merges new options coming from the plugin into the `key` entry of the _rawOptions map. + // + // By default: `key` is 'options'. + // // Returns the updated _rawOptions object. - Map _mergeRawOptions(Map newOptions) { - _rawOptions['options'] = { - ..._rawOptions['options'], + Map _mergeRawOptions( + Map newOptions, { + String key = 'options', + }) { + _rawOptions[key] = { + ...(_rawOptions[key] ?? {}), ...newOptions, }; return _rawOptions; @@ -162,7 +209,7 @@ class GoogleMapController { final newOptions = _mergeRawOptions(optionsUpdate); _setOptions(_rawOptionsToGmapsOptions(newOptions)); - _setTrafficLayer(_isTrafficLayerEnabled(newOptions)); + _setTrafficLayer(_googleMap, _isTrafficLayerEnabled(newOptions)); } /// Sets new [gmaps.MapOptions] on the wrapped map. @@ -170,11 +217,11 @@ class GoogleMapController { _googleMap?.options = options; } - /// Attaches/detaches a Traffic Layer on the current googleMap. - void _setTrafficLayer(bool attach) { + /// Attaches/detaches a Traffic Layer on the passed `map` if `attach` is true/false. + void _setTrafficLayer(gmaps.GMap map, bool attach) { if (attach && _trafficLayer == null) { _trafficLayer = gmaps.TrafficLayer(); - _trafficLayer.set('map', _googleMap); + _trafficLayer.set('map', map); } if (!attach && _trafficLayer != null) { _trafficLayer.set('map', null); @@ -193,7 +240,7 @@ class GoogleMapController { /// Returns the [ScreenCoordinate] for a given viewport [LatLng]. Future getScreenCoordinate(LatLng latLng) async { final point = - _googleMap.projection.fromLatLngToPoint(_latlngToGmLatlng(latLng)); + _googleMap.projection.fromLatLngToPoint(_latLngToGmLatLng(latLng)); return ScreenCoordinate(x: point.x, y: point.y); } @@ -202,7 +249,7 @@ class GoogleMapController { final latLng = _googleMap.projection.fromPointToLatLng( gmaps.Point(screenCoordinate.x, screenCoordinate.y), ); - return _gmLatlngToLatlng(latLng); + return _gmLatLngToLatLng(latLng); } /// Applies a `cameraUpdate` to the current viewport. @@ -254,7 +301,7 @@ class GoogleMapController { } /// Returns true if the [InfoWindow] of the marker identified by [MarkerId] is shown. - Future isInfoWindowShown(MarkerId markerId) async { + bool isInfoWindowShown(MarkerId markerId) { return _markersController?.isInfoWindowShown(markerId); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index fae4a8a4135c..3365b8cda4c5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -165,7 +165,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { Future isMarkerInfoWindowShown( MarkerId markerId, { @required int mapId, - }) { + }) async { return _map(mapId).isInfoWindowShown(markerId); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 87682175e6dc..edad62bfec75 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -139,7 +139,7 @@ class MarkersController extends AbstractController { void _onMarkerDragEnd(MarkerId markerId, gmaps.LatLng latLng) { _streamController.add(MarkerDragEndEvent( mapId, - _gmLatlngToLatlng(latLng), + _gmLatLngToLatLng(latLng), markerId, )); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart new file mode 100644 index 000000000000..2154ca9612ef --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart @@ -0,0 +1,509 @@ +import 'dart:async'; + +import 'package:e2e/e2e.dart'; +import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +class _MockCirclesController extends Mock implements CirclesController {} + +class _MockPolygonsController extends Mock implements PolygonsController {} + +class _MockPolylinesController extends Mock implements PolylinesController {} + +class _MockMarkersController extends Mock implements MarkersController {} + +class _MockGMap extends Mock implements gmaps.GMap { + final onClickController = StreamController.broadcast(); + @override + Stream get onClick => onClickController.stream; + + final onRightclickController = StreamController.broadcast(); + @override + Stream get onRightclick => onRightclickController.stream; + + final onBoundsChangedController = StreamController.broadcast(); + @override + Stream get onBoundsChanged => onBoundsChangedController.stream; + + final onIdleController = StreamController.broadcast(); + @override + Stream get onIdle => onIdleController.stream; +} + +/// Test Google Map Controller +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + group('GoogleMapController', () { + final int mapId = 33930; + GoogleMapController controller; + StreamController stream; + + // Creates a controller with the default mapId and stream controller, and any `options` needed. + GoogleMapController _createController({Map options}) { + return GoogleMapController( + mapId: mapId, + streamController: stream, + rawOptions: options ?? {}); + } + + setUp(() { + stream = StreamController.broadcast(); + }); + + group('construct/dispose', () { + setUp(() { + controller = _createController(); + }); + + testWidgets('constructor creates widget', (WidgetTester tester) async { + expect(controller.widget, isNotNull); + expect(controller.widget.viewType, endsWith('$mapId')); + }); + + testWidgets('widget is cached when reused', (WidgetTester tester) async { + final first = controller.widget; + final again = controller.widget; + expect(identical(first, again), isTrue); + }); + + testWidgets('dispose closes the stream and removes the widget', + (WidgetTester tester) async { + controller.dispose(); + expect(stream.isClosed, isTrue); + expect(controller.widget, isNull); + }); + }); + + group('init', () { + _MockCirclesController circles; + _MockMarkersController markers; + _MockPolygonsController polygons; + _MockPolylinesController polylines; + _MockGMap map; + + setUp(() { + circles = _MockCirclesController(); + markers = _MockMarkersController(); + polygons = _MockPolygonsController(); + polylines = _MockPolylinesController(); + map = _MockGMap(); + }); + + testWidgets('listens to map events', (WidgetTester tester) async { + controller = _createController(); + controller.debugSetOverrides( + createMap: (_, __) => map, + circles: circles, + markers: markers, + polygons: polygons, + polylines: polylines, + ); + + expect(map.onClickController.hasListener, isFalse); + expect(map.onRightclickController.hasListener, isFalse); + expect(map.onBoundsChangedController.hasListener, isFalse); + expect(map.onIdleController.hasListener, isFalse); + + controller.init(); + + expect(map.onClickController.hasListener, isTrue); + expect(map.onRightclickController.hasListener, isTrue); + expect(map.onBoundsChangedController.hasListener, isTrue); + expect(map.onIdleController.hasListener, isTrue); + }); + + 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(); + + verify(circles.bindToMap(mapId, map)); + verify(markers.bindToMap(mapId, map)); + verify(polygons.bindToMap(mapId, map)); + verify(polylines.bindToMap(mapId, map)); + }); + + testWidgets('renders initial geometry', (WidgetTester tester) async { + controller = _createController(options: { + 'circlesToAdd': [ + {'circleId': 'circle-1'} + ], + 'markersToAdd': [ + {'markerId': 'marker-1'} + ], + 'polygonsToAdd': [ + {'polygonId': 'polygon-1'} + ], + 'polylinesToAdd': [ + {'polylineId': 'polyline-1'} + ], + }); + controller.debugSetOverrides( + circles: circles, + markers: markers, + polygons: polygons, + polylines: polylines, + ); + + controller.init(); + + final capturedCircles = + verify(circles.addCircles(captureAny)).captured[0] as Set; + final capturedMarkers = + verify(markers.addMarkers(captureAny)).captured[0] as Set; + final capturedPolygons = verify(polygons.addPolygons(captureAny)) + .captured[0] as Set; + final capturedPolylines = verify(polylines.addPolylines(captureAny)) + .captured[0] as Set; + + expect(capturedCircles.first.circleId.value, 'circle-1'); + expect(capturedMarkers.first.markerId.value, 'marker-1'); + expect(capturedPolygons.first.polygonId.value, 'polygon-1'); + expect(capturedPolylines.first.polylineId.value, 'polyline-1'); + }); + + group('Initialization options', () { + gmaps.MapOptions capturedOptions; + setUp(() { + capturedOptions = null; + }); + testWidgets('translates initial options', (WidgetTester tester) async { + controller = _createController(options: { + 'options': { + 'mapType': 2, + 'zoomControlsEnabled': true, + } + }); + controller.debugSetOverrides(createMap: (_, options) { + capturedOptions = options; + return map; + }); + + controller.init(); + + expect(capturedOptions, isNotNull); + expect(capturedOptions.mapTypeId, gmaps.MapTypeId.SATELLITE); + expect(capturedOptions.zoomControl, true); + expect(capturedOptions.gestureHandling, 'auto', + reason: + 'by default the map handles zoom/pan gestures internally'); + }); + + testWidgets('disables gestureHandling with scrollGesturesEnabled false', + (WidgetTester tester) async { + controller = _createController(options: { + 'options': { + 'scrollGesturesEnabled': false, + } + }); + controller.debugSetOverrides(createMap: (_, options) { + capturedOptions = options; + return map; + }); + + controller.init(); + + expect(capturedOptions, isNotNull); + expect(capturedOptions.gestureHandling, 'none', + reason: + 'disabling scroll gestures disables all gesture handling'); + }); + + testWidgets('disables gestureHandling with zoomGesturesEnabled false', + (WidgetTester tester) async { + controller = _createController(options: { + 'options': { + 'zoomGesturesEnabled': false, + } + }); + controller.debugSetOverrides(createMap: (_, options) { + capturedOptions = options; + return map; + }); + + controller.init(); + + expect(capturedOptions, isNotNull); + expect(capturedOptions.gestureHandling, 'none', + reason: + 'disabling scroll gestures disables all gesture handling'); + }); + + testWidgets('does not set initial position if absent', + (WidgetTester tester) async { + controller = _createController(); + controller.debugSetOverrides(createMap: (_, options) { + capturedOptions = options; + return map; + }); + + controller.init(); + + expect(capturedOptions, isNotNull); + expect(capturedOptions.zoom, isNull); + expect(capturedOptions.center, isNull); + }); + + testWidgets('sets initial position when passed', + (WidgetTester tester) async { + controller = _createController(options: { + 'initialCameraPosition': { + 'target': [43.308, -5.6910], + 'zoom': 12, + 'bearing': 0, + 'tilt': 0, + } + }); + controller.debugSetOverrides(createMap: (_, options) { + capturedOptions = options; + return map; + }); + + controller.init(); + + expect(capturedOptions, isNotNull); + expect(capturedOptions.zoom, 12); + expect(capturedOptions.center, isNotNull); + }); + }); + + group('Traffic Layer', () { + testWidgets('by default is disabled', (WidgetTester tester) async { + controller = _createController(); + controller.init(); + expect(controller.trafficLayer, isNull); + }); + + testWidgets('initializes with traffic layer', + (WidgetTester tester) async { + controller = _createController(options: { + 'options': { + 'trafficEnabled': true, + } + }); + controller.debugSetOverrides(createMap: (_, __) => map); + controller.init(); + expect(controller.trafficLayer, isNotNull); + }); + }); + }); + + // These are the methods that are delegated to the gmaps.GMap object, that we can mock... + group('Map control methods', () { + _MockGMap map; + + setUp(() { + map = _MockGMap(); + controller = _createController(); + controller.debugSetOverrides(createMap: (_, __) => map); + controller.init(); + }); + + group('updateRawOptions', () { + testWidgets('can update `options`', (WidgetTester tester) async { + controller.updateRawOptions({ + 'mapType': 2, + }); + final options = verify(map.options = captureAny).captured[0]; + + expect(options.mapTypeId, gmaps.MapTypeId.SATELLITE); + }); + + testWidgets('can turn on/off traffic', (WidgetTester tester) async { + expect(controller.trafficLayer, isNull); + + controller.updateRawOptions({ + 'trafficEnabled': true, + }); + + expect(controller.trafficLayer, isNotNull); + + controller.updateRawOptions({ + 'trafficEnabled': false, + }); + + expect(controller.trafficLayer, isNull); + }); + }); + + group('viewport getters', () { + testWidgets('getVisibleRegion', (WidgetTester tester) async { + await controller.getVisibleRegion(); + + verify(map.bounds); + }); + + testWidgets('getZoomLevel', (WidgetTester tester) async { + when(map.zoom).thenReturn(10); + + await controller.getZoomLevel(); + + verify(map.zoom); + }); + }); + + group('moveCamera', () { + testWidgets('newLatLngZoom', (WidgetTester tester) async { + await (controller + .moveCamera(CameraUpdate.newLatLngZoom(LatLng(19, 26), 12))); + + verify(map.zoom = 12); + final captured = verify(map.panTo(captureAny)).captured[0]; + expect(captured.lat, 19); + expect(captured.lng, 26); + }); + }); + + group('map.projection methods', () { + // These are too much for dart mockito, can't mock: + // map.projection.method() (in Javascript ;) ) + }); + }); + + // 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 mock = _MockCirclesController(); + controller.debugSetOverrides(circles: mock); + + final previous = { + Circle(circleId: CircleId('to-be-updated')), + Circle(circleId: CircleId('to-be-removed')), + }; + + final current = { + Circle(circleId: CircleId('to-be-updated'), visible: false), + Circle(circleId: CircleId('to-be-added')), + }; + + controller.updateCircles(CircleUpdates.from(previous, current)); + + verify(mock.removeCircles({ + CircleId('to-be-removed'), + })); + verify(mock.addCircles({ + Circle(circleId: CircleId('to-be-added')), + })); + verify(mock.changeCircles({ + Circle(circleId: CircleId('to-be-updated'), visible: false), + })); + }); + + testWidgets('updateMarkers', (WidgetTester tester) async { + final mock = _MockMarkersController(); + controller.debugSetOverrides(markers: mock); + + final previous = { + Marker(markerId: MarkerId('to-be-updated')), + Marker(markerId: MarkerId('to-be-removed')), + }; + + final current = { + Marker(markerId: MarkerId('to-be-updated'), visible: false), + Marker(markerId: MarkerId('to-be-added')), + }; + + controller.updateMarkers(MarkerUpdates.from(previous, current)); + + verify(mock.removeMarkers({ + MarkerId('to-be-removed'), + })); + verify(mock.addMarkers({ + Marker(markerId: MarkerId('to-be-added')), + })); + verify(mock.changeMarkers({ + Marker(markerId: MarkerId('to-be-updated'), visible: false), + })); + }); + + testWidgets('updatePolygons', (WidgetTester tester) async { + final mock = _MockPolygonsController(); + controller.debugSetOverrides(polygons: mock); + + final previous = { + Polygon(polygonId: PolygonId('to-be-updated')), + Polygon(polygonId: PolygonId('to-be-removed')), + }; + + final current = { + Polygon(polygonId: PolygonId('to-be-updated'), visible: false), + Polygon(polygonId: PolygonId('to-be-added')), + }; + + controller.updatePolygons(PolygonUpdates.from(previous, current)); + + verify(mock.removePolygons({ + PolygonId('to-be-removed'), + })); + verify(mock.addPolygons({ + Polygon(polygonId: PolygonId('to-be-added')), + })); + verify(mock.changePolygons({ + Polygon(polygonId: PolygonId('to-be-updated'), visible: false), + })); + }); + + testWidgets('updatePolylines', (WidgetTester tester) async { + final mock = _MockPolylinesController(); + controller.debugSetOverrides(polylines: mock); + + final previous = { + Polyline(polylineId: PolylineId('to-be-updated')), + Polyline(polylineId: PolylineId('to-be-removed')), + }; + + final current = { + Polyline(polylineId: PolylineId('to-be-updated'), visible: false), + Polyline(polylineId: PolylineId('to-be-added')), + }; + + controller.updatePolylines(PolylineUpdates.from(previous, current)); + + verify(mock.removePolylines({ + PolylineId('to-be-removed'), + })); + verify(mock.addPolylines({ + Polyline(polylineId: PolylineId('to-be-added')), + })); + verify(mock.changePolylines({ + Polyline(polylineId: PolylineId('to-be-updated'), visible: false), + })); + }); + + testWidgets('infoWindow visibility', (WidgetTester tester) async { + final mock = _MockMarkersController(); + controller.debugSetOverrides(markers: mock); + final markerId = MarkerId('marker-with-infowindow'); + + controller.showInfoWindow(markerId); + + verify(mock.showMarkerInfoWindow(markerId)); + + controller.hideInfoWindow(markerId); + + verify(mock.hideMarkerInfoWindow(markerId)); + + controller.isInfoWindowShown(markerId); + + verify(mock.isInfoWindowShown(markerId)); + }); + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart @@ -0,0 +1,7 @@ +// 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 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); From 5eaeb9eedb69af8f3e0c4dd85df738fa38f5e135 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 18 Aug 2020 14:34:37 -0700 Subject: [PATCH 57/58] Address PR comments. Also: * Add copyright notices to dart files * Add analysis_options.yaml file * Tweak build all script to let it know this package has its own analysis_options file. * Extract some types that were randomly strawn around the code to a types.dart file. --- .../google_maps_flutter_web/README.md | 14 ++-- .../analysis_options.yaml | 10 +++ .../lib/google_maps_flutter_web.dart | 6 ++ .../lib/src/circle.dart | 12 ++-- .../lib/src/circles.dart | 42 +++++------ .../lib/src/convert.dart | 6 +- .../lib/src/google_maps_controller.dart | 39 +++++----- .../lib/src/google_maps_flutter_web.dart | 25 +++++-- .../lib/src/marker.dart | 24 ++++--- .../lib/src/markers.dart | 62 ++++++++-------- .../lib/src/polygon.dart | 12 ++-- .../lib/src/polygons.dart | 51 ++++++------- .../lib/src/polyline.dart | 16 +++-- .../lib/src/polylines.dart | 72 +++++++++---------- .../lib/src/types.dart | 30 ++++++++ .../google_maps_controller_e2e.dart | 4 ++ .../google_maps_controller_e2e_test.dart | 2 +- .../test_driver/google_maps_plugin_e2e.dart | 6 +- .../google_maps_plugin_e2e_test.dart | 2 +- .../test/test_driver/marker_e2e.dart | 4 ++ .../test/test_driver/marker_e2e_test.dart | 2 +- .../test/test_driver/markers_e2e.dart | 4 ++ .../test/test_driver/markers_e2e_test.dart | 2 +- .../test/test_driver/shape_e2e.dart | 4 ++ .../test/test_driver/shape_e2e_test.dart | 2 +- .../test/test_driver/shapes_e2e.dart | 4 ++ .../test/test_driver/shapes_e2e_test.dart | 2 +- script/incremental_build.sh | 1 + 28 files changed, 268 insertions(+), 192 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index 4e43eb88406c..e1c1a5330c56 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -1,12 +1,12 @@ # google_maps_flutter_web -This is an implementation of the Google Maps Flutter plugin for web. +This is an implementation of the [google_maps_flutter](https://pub.dev/packages/google_maps_flutter) plugin for web. Behind the scenes, it uses a14n's [google_maps](https://pub.dev/packages/google_maps) dart JS interop layer. ## Usage ### Depend on the package -This package is not an endorsed implementation of the google_maps_flutter plugin, so you'll need to modify the `pubspec.yaml` file of your app to depend on this package: +This package is not an endorsed implementation of the google_maps_flutter plugin yet, so you'll need to modify the `pubspec.yaml` file of your app to depend on this package: ```yaml dependencies: @@ -41,11 +41,11 @@ The following map options are not available in web, because the map doesn't rota There's no "Map Toolbar" in web, so the `mapToolbarEnabled` option is unused. -There's no "My Location" widget in web (for now), so the following options are ignored: +There's no "My Location" widget in web ([tracking issue](https://github.com/flutter/flutter/issues/64073)), so the following options are ignored, for now: -* `myLocationButtonEnabled` ([seems doable](https://developers.google.com/maps/documentation/javascript/examples/control-custom)) -* `myLocationEnabled` ([seems doable](https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html)) +* `myLocationButtonEnabled` +* `myLocationEnabled` -There's no `defaultMarkerWithHue` in web. If you want colored pins/markers, you should use your own asset images. +There's no `defaultMarkerWithHue` in web. If you need colored pins/markers, you may need to use your own asset images. -Traffic, indoor and building layers are still not available on the web. +Indoor and building layers are still not available on the web. Traffic is. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml new file mode 100644 index 000000000000..443b16551ec9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml @@ -0,0 +1,10 @@ +# This is a temporary file to allow us to unblock the flutter/plugins repo CI. +# It disables some of lints that were disabled inline. Disabling lints inline +# is no longer possible, so this file is required. +# TODO(ditman) https://github.com/flutter/flutter/issues/55000 (clean this up) + +include: ../../../analysis_options.yaml + +analyzer: + errors: + undefined_prefixed_name: ignore diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index 1a315d82d7c4..cf133fb9e533 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + library google_maps_flutter_web; import 'dart:async'; @@ -20,6 +24,8 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:google_maps/google_maps.dart' as gmaps; +import 'src/types.dart'; + part 'src/google_maps_flutter_web.dart'; part 'src/google_maps_controller.dart'; part 'src/circle.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 7450e375622b..59627c6a63ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -1,12 +1,16 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; -/// The CircleController class wraps a Circle and its onTap behavior. +/// The `CircleController` class wraps a [gmaps.Circle] and its onTap behavior. class CircleController { gmaps.Circle _circle; final bool _consumeTapEvents; - /// Creates a CircleController, that wraps a Circle object and its onTap behavior. + /// Creates a `CircleController`, which wraps a [gmaps.Circle] object and its `onTap` behavior. CircleController({ @required gmaps.Circle circle, bool consumeTapEvents = false, @@ -20,11 +24,11 @@ class CircleController { } } - /// Returns the wrapped circle. Only used for testing. + /// Returns the wrapped [gmaps.Circle]. Only used for testing. @visibleForTesting gmaps.Circle get circle => _circle; - /// Returns [true] if this Controller will use its own onTap handler to consume events. + /// Returns `true` if this Controller will use its own `onTap` handler to consume events. bool get consumeTapEvents => _consumeTapEvents; /// Updates the options of the wrapped [gmaps.Circle] object. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index 6313e03e0ca0..c7c33ed1811f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -1,26 +1,30 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; -/// This class manages all the circles associated to any given Google Map Controller. -class CirclesController extends AbstractController { - // A cache of circleIDs to their controllers +/// This class manages all the [CircleController]s associated to a [GoogleMapController]. +class CirclesController extends GeometryController { + // A cache of [CircleController]s indexed by their [CircleId]. final Map _circleIdToController; // The stream over which circles broadcast their events StreamController _streamController; - /// Initialize the cache. The StreamController is shared with the Google Map Controller. + /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. CirclesController({ @required StreamController stream, }) : _streamController = stream, _circleIdToController = Map(); - /// Returns the cache of circles. Test only. + /// Returns the cache of [CircleController]s. Test only. @visibleForTesting Map get circles => _circleIdToController; /// Adds a set of [Circle] objects to the cache. /// - /// (Wraps each Circle into its corresponding [CircleController]) + /// Wraps each [Circle] into its corresponding [CircleController]. void addCircles(Set circlesToAdd) { circlesToAdd?.forEach((circle) { _addCircle(circle); @@ -28,7 +32,10 @@ class CirclesController extends AbstractController { } void _addCircle(Circle circle) { - if (circle == null) return; + if (circle == null) { + return; + } + final populationOptions = _circleOptionsFromCircle(circle); gmaps.Circle gmCircle = gmaps.Circle(populationOptions); gmCircle.map = googleMap; @@ -49,31 +56,24 @@ class CirclesController extends AbstractController { } void _changeCircle(Circle circle) { - if (circle != null) { - final circleController = _circleIdToController[circle.circleId]; - circleController?.update(_circleOptionsFromCircle(circle)); - } + final circleController = _circleIdToController[circle?.circleId]; + circleController?.update(_circleOptionsFromCircle(circle)); } /// Removes a set of [CircleId]s from the cache. void removeCircles(Set circleIdsToRemove) { circleIdsToRemove?.forEach((circleId) { - if (circleId != null) { - final CircleController circleController = - _circleIdToController[circleId]; - if (circleController != null) { - circleController.remove(); - _circleIdToController.remove(circleId); - } - } + final CircleController circleController = _circleIdToController[circleId]; + circleController?.remove(); + _circleIdToController.remove(circleId); }); } // Handles the global onCircleTap function to funnel events from circles into the stream. bool _onCircleTap(CircleId circleId) { - // TODO: Should consumeTapEvents prevent events from being added to the stream? + // Have you ended here on your debugging? Is this wrong? + // Comment here: https://github.com/flutter/flutter/issues/64084 _streamController.add(CircleTapEvent(mapId, circleId)); - // Stop propagation? return _circleIdToController[circleId]?.consumeTapEvents ?? false; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 63cccd87d4e0..2eeaa0202995 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; final _nullLatLng = LatLng(0, 0); @@ -383,8 +387,6 @@ gmaps.MarkerOptions _markerOptionsFromMarker( // already encoded in the iconConfig[1] icon = gmaps.Icon() - // TODO: Move the comment below to analysis-options.yaml - // ignore:undefined_prefixed_name ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); // iconConfig[3] may contain the [width, height] of the image, if passed! diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index a85ae095ed5c..707af828e2c6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; /// Type used when passing an override to the _createMap function. @@ -73,11 +77,11 @@ class GoogleMapController { _polylinesController = PolylinesController(stream: this._streamController); _markersController = MarkersController(stream: this._streamController); - // Register the view factory that will hold the `_div` that will hold the map. - // Note that we need to "leak" the div, so it can be used to build the gmaps.GMap object on init(). + // Register the view factory that will hold the `_div` that holds the map in the DOM. + // The `_div` needs to be created outside of the ViewFactory (and cached!) so we can + // use it to create the [gmaps.GMap] in the `init()` method of this class. _div = DivElement()..id = _getViewType(mapId); - // TODO: Move the comment below to analysis-options.yaml - // ignore:undefined_prefixed_name + ui.platformViewRegistry.registerViewFactory( _getViewType(mapId), (int viewId) => _div, @@ -110,6 +114,12 @@ class GoogleMapController { } /// Initializes the [gmaps.GMap] instance from the stored `rawOptions`. + /// + /// This method actually renders the GMap into the cached `_div`. This is + /// called by the [GoogleMapsPlugin.init] method when appropriate. + /// + /// Failure to call this method would result in the GMap not rendering at all, + /// and most of the public methods on this class no-op'ing. void init() { var options = _rawOptionsToGmapsOptions(_rawOptions); // Initial position can only to be set here! @@ -212,12 +222,12 @@ class GoogleMapController { _setTrafficLayer(_googleMap, _isTrafficLayerEnabled(newOptions)); } - /// Sets new [gmaps.MapOptions] on the wrapped map. + // Sets new [gmaps.MapOptions] on the wrapped map. void _setOptions(gmaps.MapOptions options) { _googleMap?.options = options; } - /// Attaches/detaches a Traffic Layer on the passed `map` if `attach` is true/false. + // Attaches/detaches a Traffic Layer on the passed `map` if `attach` is true/false. void _setTrafficLayer(gmaps.GMap map, bool attach) { if (attach && _trafficLayer == null) { _trafficLayer = gmaps.TrafficLayer(); @@ -318,20 +328,3 @@ class GoogleMapController { _streamController.close(); } } - -/// The base class for all "geometry" controllers. -/// -/// This lets all Geometry controllers be bound to a given mapID and GMap. -abstract class AbstractController { - /// The GMap instance that this controller operates on. - gmaps.GMap googleMap; - - /// The map ID for events. - int mapId; - - /// Binds a mapId and its instance to this controller. - void bindToMap(int mapId, gmaps.GMap googleMap) { - this.mapId = mapId; - this.googleMap = googleMap; - } -} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 3365b8cda4c5..cf549e8e375e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; /// The web implementation of [GoogleMapsFlutterPlatform]. @@ -143,6 +147,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } /// Shows the [InfoWindow] (if any) of the [Marker] identified by `markerId`. + /// + /// See also: + /// * [hideMarkerInfoWindow] to hide the info window. + /// * [isMarkerInfoWindowShown] to check if the info window is visible/hidden. @override Future showMarkerInfoWindow( MarkerId markerId, { @@ -152,6 +160,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } /// Hides the [InfoWindow] (if any) of the [Marker] identified by `markerId`. + /// + /// See also: + /// * [showMarkerInfoWindow] to show the info window. + /// * [isMarkerInfoWindowShown] to check if the info window is shown. @override Future hideMarkerInfoWindow( MarkerId markerId, { @@ -161,6 +173,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } /// Returns true if the [InfoWindow] of the [Marker] identified by `markerId` is shown. + /// + /// See also: + /// * [showMarkerInfoWindow] to show the info window. + /// * [hideMarkerInfoWindow] to hide the info window. @override Future isMarkerInfoWindowShown( MarkerId markerId, { @@ -249,13 +265,8 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { PlatformViewCreatedCallback onPlatformViewCreated) { int mapId = creationParams.remove('_webOnlyMapCreationId'); - if (mapId == null) { - throw PlatformException( - code: 'maps_web_missing_creation_map_id', - message: - 'Pass a `_webOnlyMapCreationId` in creationParams to prevent reloads in web.', - ); - } + assert(mapId != null, + 'buildView needs a `_webOnlyMapCreationId` in its creationParams to prevent widget reloads in web.'); // Bail fast if we've already rendered this mapId... if (_mapById[mapId]?.widget != null) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 1bf8db196494..a067e352732f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -1,8 +1,10 @@ -part of google_maps_flutter_web; +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -typedef LatLngCallback = void Function(gmaps.LatLng latLng); +part of google_maps_flutter_web; -/// This class wraps a [gmaps.Marker], how it handles events, and its associated [gmaps.InfoWindow] widget (optional). +/// The `MarkerController` class wraps a [gmaps.Marker], how it handles events, and its associated (optional) [gmaps.InfoWindow] widget. class MarkerController { gmaps.Marker _marker; @@ -12,7 +14,7 @@ class MarkerController { bool _infoWindowShown = false; - /// Creates a MarkerController, that wraps a Marker object, its onTap/Drag behavior, and its associated InfoWindow. + /// Creates a `MarkerController`, which wraps a [gmaps.Marker] object, its `onTap`/`onDrag` behavior, and its associated [gmaps.InfoWindow]. MarkerController({ @required gmaps.Marker marker, gmaps.InfoWindow infoWindow, @@ -35,16 +37,16 @@ class MarkerController { } } - /// Returns [true] if this Controller will use its own onTap handler to consume events. + /// Returns `true` if this Controller will use its own `onTap` handler to consume events. bool get consumeTapEvents => _consumeTapEvents; - /// Returns [true] if the InfoWindow associated to this marker is being shown. + /// Returns `true` if the [gmaps.InfoWindow] associated to this marker is being shown. bool get infoWindowShown => _infoWindowShown; - /// Returns the marker associated to this controller. + /// Returns the [gmaps.Marker] associated to this controller. gmaps.Marker get marker => _marker; - /// Updates the options of the wrapped [gmaps.Polygon] object. + /// Updates the options of the wrapped [gmaps.Marker] object. void update( gmaps.MarkerOptions options, { String newInfoWindowContent, @@ -55,14 +57,14 @@ class MarkerController { } } - /// Disposes of the currently wrapped Marker. + /// Disposes of the currently wrapped [gmaps.Marker]. void remove() { _marker.visible = false; _marker.map = null; _marker = null; } - /// Hide the associated InfoWindow. + /// Hide the associated [gmaps.InfoWindow]. void hideInfoWindow() { if (_infoWindow != null) { _infoWindow.close(); @@ -70,7 +72,7 @@ class MarkerController { } } - /// Show the associated InfoWindow. + /// Show the associated [gmaps.InfoWindow]. void showInfoWindow() { if (_infoWindow != null) { _infoWindow.open(_marker.map, _marker); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index edad62bfec75..ebb478d20b06 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -1,34 +1,38 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; -/// This class manages a set of [MarkerController]s associated to a Google Map Controller. -class MarkersController extends AbstractController { - // A cache of markerIds to their controllers +/// This class manages a set of [MarkerController]s associated to a [GoogleMapController]. +class MarkersController extends GeometryController { + // A cache of [MarkerController]s indexed by their [MarkerId]. final Map _markerIdToController; // The stream over which markers broadcast their events StreamController _streamController; - /// Initializes the cache. The StreamController is shared with the Google Map Controller. + /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. MarkersController({ @required StreamController stream, }) : _streamController = stream, _markerIdToController = Map(); - /// Returns the cache of markers. Test only. + /// Returns the cache of [MarkerController]s. Test only. @visibleForTesting Map get markers => _markerIdToController; /// Adds a set of [Marker] objects to the cache. /// - /// (Wraps each Marker into its corresponding [MarkerController]) + /// Wraps each [Marker] into its corresponding [MarkerController]. void addMarkers(Set markersToAdd) { - if (markersToAdd != null) { - markersToAdd.forEach(_addMarker); - } + markersToAdd?.forEach(_addMarker); } void _addMarker(Marker marker) { - if (marker == null) return; + if (marker == null) { + return; + } final infoWindowOptions = _infoWindowOptionsFromMarker(marker); gmaps.InfoWindow gmInfoWindow; @@ -50,7 +54,6 @@ class MarkersController extends AbstractController { infoWindow: gmInfoWindow, consumeTapEvents: marker.consumeTapEvents, onTap: () { - // TODO: If has infowindow... this.showMarkerInfoWindow(marker.markerId); _onMarkerTap(marker.markerId); }, @@ -63,16 +66,11 @@ class MarkersController extends AbstractController { /// Updates a set of [Marker] objects with new options. void changeMarkers(Set markersToChange) { - if (markersToChange != null) { - markersToChange.forEach(_changeMarker); - } + markersToChange?.forEach(_changeMarker); } void _changeMarker(Marker marker) { - if (marker == null) { - return; - } - MarkerController markerController = _markerIdToController[marker.markerId]; + MarkerController markerController = _markerIdToController[marker?.markerId]; if (markerController != null) { final markerOptions = _markerOptionsFromMarker( marker, @@ -88,37 +86,36 @@ class MarkersController extends AbstractController { /// Removes a set of [MarkerId]s from the cache. void removeMarkers(Set markerIdsToRemove) { - if (markerIdsToRemove == null) { - return; - } - markerIdsToRemove.forEach(_removeMarker); + markerIdsToRemove?.forEach(_removeMarker); } void _removeMarker(MarkerId markerId) { - if (markerId == null) return; - final MarkerController markerController = _markerIdToController[markerId]; - if (markerController != null) { - markerController.remove(); - _markerIdToController.remove(markerId); - } + markerController?.remove(); + _markerIdToController.remove(markerId); } // InfoWindow... - /// Shows the [InfoWindow] of a Marker. + /// Shows the [InfoWindow] of a [MarkerId]. + /// + /// See also [hideMarkerInfoWindow] and [isInfoWindowShown]. void showMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; markerController?.showInfoWindow(); } - /// Hides the [InfoWindow] of a Marker. + /// Hides the [InfoWindow] of a [MarkerId]. + /// + /// See also [showMarkerInfoWindow] and [isInfoWindowShown]. void hideMarkerInfoWindow(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; markerController?.hideInfoWindow(); } - /// Returns whether or not the [InfoWindow] of a Marker is shown. + /// Returns whether or not the [InfoWindow] of a [MarkerId] is shown. + /// + /// See also [showMarkerInfoWindow] and [hideMarkerInfoWindow]. bool isInfoWindowShown(MarkerId markerId) { MarkerController markerController = _markerIdToController[markerId]; return markerController?.infoWindowShown ?? false; @@ -127,8 +124,9 @@ class MarkersController extends AbstractController { // Handle internal events bool _onMarkerTap(MarkerId markerId) { + // Have you ended here on your debugging? Is this wrong? + // Comment here: https://github.com/flutter/flutter/issues/64084 _streamController.add(MarkerTapEvent(mapId, markerId)); - // Stop propagation? return _markerIdToController[markerId]?.consumeTapEvents ?? false; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index e5cd79b5f020..f4c692d2ee83 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -1,12 +1,16 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; -/// The PolygonController class wraps a Polygon and its onTap behavior. +/// The `PolygonController` class wraps a [gmaps.Polygon] and its `onTap` behavior. class PolygonController { gmaps.Polygon _polygon; final bool _consumeTapEvents; - /// Creates a PolygonController, that wraps a Polygon object and its onTap behavior. + /// Creates a `PolygonController` that wraps a [gmaps.Polygon] object and its `onTap` behavior. PolygonController({ @required gmaps.Polygon polygon, bool consumeTapEvents = false, @@ -20,11 +24,11 @@ class PolygonController { } } - /// Returns the wrapped circle. Only used for testing. + /// Returns the wrapped [gmaps.Polygon]. Only used for testing. @visibleForTesting gmaps.Polygon get polygon => _polygon; - /// Returns [true] if this Controller will use its own onTap handler to consume events. + /// Returns `true` if this Controller will use its own `onTap` handler to consume events. bool get consumeTapEvents => _consumeTapEvents; /// Updates the options of the wrapped [gmaps.Polygon] object. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index a2e52d7933af..5c078ea0aa7a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -1,26 +1,30 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; -/// This class manages all the polygons associated to any given Google Map Controller. -class PolygonsController extends AbstractController { - // A cache of polygonIDs to their controllers +/// This class manages a set of [PolygonController]s associated to a [GoogleMapController]. +class PolygonsController extends GeometryController { + // A cache of [PolygonController]s indexed by their [PolygonId]. final Map _polygonIdToController; // The stream over which polygons broadcast events StreamController _streamController; - /// Initializes the cache. The StreamController is shared with the Google Map Controller. + /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. PolygonsController({ @required StreamController stream, }) : _streamController = stream, _polygonIdToController = Map(); - /// Returns the cache of polygons. Test only. + /// Returns the cache of [PolygonController]s. Test only. @visibleForTesting Map get polygons => _polygonIdToController; /// Adds a set of [Polygon] objects to the cache. /// - /// (Wraps each Polygon into its corresponding [PolygonController]) + /// Wraps each Polygon into its corresponding [PolygonController]. void addPolygons(Set polygonsToAdd) { if (polygonsToAdd != null) { polygonsToAdd.forEach((polygon) { @@ -30,7 +34,10 @@ class PolygonsController extends AbstractController { } void _addPolygon(Polygon polygon) { - if (polygon == null) return; + if (polygon == null) { + return; + } + final populationOptions = _polygonOptionsFromPolygon(googleMap, polygon); gmaps.Polygon gmPolygon = gmaps.Polygon(populationOptions); gmPolygon.map = googleMap; @@ -53,38 +60,26 @@ class PolygonsController extends AbstractController { } void _changePolygon(Polygon polygon) { - if (polygon == null) { - return; - } - PolygonController polygonController = - _polygonIdToController[polygon.polygonId]; - if (polygonController != null) { - polygonController.update(_polygonOptionsFromPolygon(googleMap, polygon)); - } + _polygonIdToController[polygon?.polygonId]; + polygonController?.update(_polygonOptionsFromPolygon(googleMap, polygon)); } /// Removes a set of [PolygonId]s from the cache. void removePolygons(Set polygonIdsToRemove) { - if (polygonIdsToRemove == null) { - return; - } - polygonIdsToRemove.forEach((polygonId) { - if (polygonId != null) { - final PolygonController polygonController = - _polygonIdToController[polygonId]; - if (polygonController != null) { - polygonController.remove(); - _polygonIdToController.remove(polygonId); - } - } + polygonIdsToRemove?.forEach((polygonId) { + final PolygonController polygonController = + _polygonIdToController[polygonId]; + polygonController?.remove(); + _polygonIdToController.remove(polygonId); }); } // Handle internal events bool _onPolygonTap(PolygonId polygonId) { + // Have you ended here on your debugging? Is this wrong? + // Comment here: https://github.com/flutter/flutter/issues/64084 _streamController.add(PolygonTapEvent(mapId, polygonId)); - // Stop propagation? return _polygonIdToController[polygonId]?.consumeTapEvents ?? false; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index 2890028702c3..f8ff2c62191d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -1,12 +1,16 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; -/// The PolygonController class wraps a [gmaps.Polyline] and its onTap behavior. +/// The `PolygonController` class wraps a [gmaps.Polyline] and its `onTap` behavior. class PolylineController { gmaps.Polyline _polyline; final bool _consumeTapEvents; - /// Creates a PolylineController, that wraps a Polyline object and its onTap behavior. + /// Creates a `PolylineController` that wraps a [gmaps.Polyline] object and its `onTap` behavior. PolylineController({ @required gmaps.Polyline polyline, bool consumeTapEvents = false, @@ -20,19 +24,19 @@ class PolylineController { } } - /// Returns the wrapped circle. Only used for testing. + /// Returns the wrapped [gmaps.Polyline]. Only used for testing. @visibleForTesting gmaps.Polyline get line => _polyline; - /// Returns [true] if this Controller will use its own onTap handler to consume events. + /// Returns `true` if this Controller will use its own `onTap` handler to consume events. bool get consumeTapEvents => _consumeTapEvents; - /// Updates the options of the wrapped Polyline object. + /// Updates the options of the wrapped [gmaps.Polyline] object. void update(gmaps.PolylineOptions options) { _polyline.options = options; } - /// Disposes of the currently wrapped Polyline. + /// Disposes of the currently wrapped [gmaps.Polyline]. void remove() { _polyline.visible = false; _polyline.map = null; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index 8076ca2819b2..f24ca4b1bb42 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -1,38 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + part of google_maps_flutter_web; -/// This class manages all the (poly)lines associated to any given Google Map Controller. -class PolylinesController extends AbstractController { - // A cache of polylineIds to their controllers +/// This class manages a set of [PolylinesController]s associated to a [GoogleMapController]. +class PolylinesController extends GeometryController { + // A cache of [PolylineController]s indexed by their [PolylineId]. final Map _polylineIdToController; // The stream over which polylines broadcast their events StreamController _streamController; - /// Initializes the cache. The StreamController is shared with the Google Map Controller. + /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. PolylinesController({ @required StreamController stream, }) : _streamController = stream, _polylineIdToController = Map(); - /// Returns the cache of polylines. Test only. + /// Returns the cache of [PolylineContrller]s. Test only. @visibleForTesting Map get lines => _polylineIdToController; /// Adds a set of [Polyline] objects to the cache. /// - /// (Wraps each line into its corresponding [PolylineController]) + /// Wraps each line into its corresponding [PolylineController]. void addPolylines(Set polylinesToAdd) { - if (polylinesToAdd != null) { - polylinesToAdd.forEach((polyline) { - _addPolyline(polyline); - }); - } + polylinesToAdd?.forEach((polyline) { + _addPolyline(polyline); + }); } void _addPolyline(Polyline polyline) { - if (polyline == null) return; - final populationOptions = _polylineOptionsFromPolyline(googleMap, polyline); - gmaps.Polyline gmPolyline = gmaps.Polyline(populationOptions); + if (polyline == null) { + return; + } + + final polylineOptions = _polylineOptionsFromPolyline(googleMap, polyline); + gmaps.Polyline gmPolyline = gmaps.Polyline(polylineOptions); gmPolyline.map = googleMap; PolylineController controller = PolylineController( polyline: gmPolyline, @@ -45,47 +50,34 @@ class PolylinesController extends AbstractController { /// Updates a set of [Polyline] objects with new options. void changePolylines(Set polylinesToChange) { - if (polylinesToChange != null) { - polylinesToChange.forEach((polylineToChange) { - _changePolyline(polylineToChange); - }); - } + polylinesToChange?.forEach((polylineToChange) { + _changePolyline(polylineToChange); + }); } void _changePolyline(Polyline polyline) { - if (polyline == null) { - return; - } PolylineController polylineController = - _polylineIdToController[polyline.polylineId]; - if (polylineController != null) { - polylineController - .update(_polylineOptionsFromPolyline(googleMap, polyline)); - } + _polylineIdToController[polyline?.polylineId]; + polylineController + ?.update(_polylineOptionsFromPolyline(googleMap, polyline)); } /// Removes a set of [PolylineId]s from the cache. void removePolylines(Set polylineIdsToRemove) { - if (polylineIdsToRemove == null) { - return; - } - polylineIdsToRemove.forEach((polylineId) { - if (polylineId != null) { - final PolylineController polylineController = - _polylineIdToController[polylineId]; - if (polylineController != null) { - polylineController.remove(); - _polylineIdToController.remove(polylineId); - } - } + polylineIdsToRemove?.forEach((polylineId) { + final PolylineController polylineController = + _polylineIdToController[polylineId]; + polylineController?.remove(); + _polylineIdToController.remove(polylineId); }); } // Handle internal events bool _onPolylineTap(PolylineId polylineId) { + // Have you ended here on your debugging? Is this wrong? + // Comment here: https://github.com/flutter/flutter/issues/64084 _streamController.add(PolylineTapEvent(mapId, polylineId)); - // Stop propagation? return _polylineIdToController[polylineId]?.consumeTapEvents ?? false; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart new file mode 100644 index 000000000000..039cc473db5e --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart @@ -0,0 +1,30 @@ +// Copyright 2017 The Chromium 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 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:google_maps/google_maps.dart' as gmaps; + +/// A void function that handles a [gmaps.LatLng] as a parameter. +/// +/// Similar to [ui.VoidCallback], but specific for Marker drag events. +typedef LatLngCallback = void Function(gmaps.LatLng latLng); + +/// The base class for all "geometry" group controllers. +/// +/// This lets all Geometry controllers ([MarkersController], [CirclesController], +/// [PolygonsController], [PolylinesController]) to be bound to a [gmaps.GMap] +/// instance and our internal `mapId` value. +abstract class GeometryController { + /// The GMap instance that this controller operates on. + gmaps.GMap googleMap; + + /// The map ID for events. + int mapId; + + /// Binds a `mapId` and the [gmaps.GMap] instance to this controller. + void bindToMap(int mapId, gmaps.GMap googleMap) { + this.mapId = mapId; + this.googleMap = googleMap; + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart index 2154ca9612ef..fac1956bb9f8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium 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 'package:e2e/e2e.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart index a29203f7dcdd..efa93a120e78 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_e2e_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart index 1f14a9108d2f..80ec5400bdd7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium 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 'package:e2e/e2e.dart'; @@ -69,7 +73,7 @@ void main() { (WidgetTester tester) async { expect( () => plugin.buildView({}, null, onPlatformViewCreated), - throwsException, + throwsAssertionError, reason: '_webOnlyMapCreationId is mandatory to prevent unnecessary reloads in web.', ); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart index a29203f7dcdd..efa93a120e78 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_plugin_e2e_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart index 7e6727b47ba0..7f9d9ba409e0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium 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 'package:e2e/e2e.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart index a29203f7dcdd..efa93a120e78 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/marker_e2e_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart index 11380a1d584d..53c34e9fa6eb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium 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 'package:e2e/e2e.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart index a29203f7dcdd..efa93a120e78 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_e2e_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e.dart index c29707625770..8a0e1d84facc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium 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 'package:e2e/e2e.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart index a29203f7dcdd..efa93a120e78 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shape_e2e_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart index a8a21959a4d6..9ff3ad9455bb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e.dart @@ -1,3 +1,7 @@ +// Copyright 2017 The Chromium 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 'package:e2e/e2e.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart index a29203f7dcdd..efa93a120e78 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/shapes_e2e_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/script/incremental_build.sh b/script/incremental_build.sh index ba8bf938d593..5351926b0051 100755 --- a/script/incremental_build.sh +++ b/script/incremental_build.sh @@ -17,6 +17,7 @@ CUSTOM_ANALYSIS_PLUGINS=( "in_app_purchase" "camera" "video_player/video_player_web" + "google_maps_flutter/google_maps_flutter_web" ) # Comma-separated string of the list above readonly CUSTOM_FLAG=$(IFS=, ; echo "${CUSTOM_ANALYSIS_PLUGINS[*]}") From 7292a65634a69cfec456d636a88c6e2d53ce968b Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 18 Aug 2020 14:46:38 -0700 Subject: [PATCH 58/58] backtick the onTap mention in the CircleController class. --- .../google_maps_flutter_web/lib/src/circle.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 59627c6a63ab..96f9be7aa001 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -4,7 +4,7 @@ part of google_maps_flutter_web; -/// The `CircleController` class wraps a [gmaps.Circle] and its onTap behavior. +/// The `CircleController` class wraps a [gmaps.Circle] and its `onTap` behavior. class CircleController { gmaps.Circle _circle;