Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix controller + plugin (removes unused fields from controller constr…
…uctor)
  • Loading branch information
ditman committed May 19, 2022
commit 5073b3cbf8921385ce2352e88f1ce0a82f81ad7e
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,43 @@ typedef DebugCreateMapFunction = gmaps.GMap Function(

/// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered.
class GoogleMapController {
/// Initializes the GMap, and the sub-controllers related to it. Wires events.
GoogleMapController({
required int mapId,
required StreamController<MapEvent<Object>> streamController,
required CameraPosition initialCameraPosition,
Set<Marker> markers = const <Marker>{},
Set<Polygon> polygons = const <Polygon>{},
Set<Polyline> polylines = const <Polyline>{},
Set<Circle> circles = const <Circle>{},
Map<String, dynamic> mapOptions = const <String, dynamic>{},
}) : _mapId = mapId,
_streamController = streamController,
_initialCameraPosition = initialCameraPosition,
_markers = markers,
_polygons = polygons,
_polylines = polylines,
_circles = circles,
_rawMapOptions = mapOptions {
_circlesController = CirclesController(stream: _streamController);
_polygonsController = PolygonsController(stream: _streamController);
_polylinesController = PolylinesController(stream: _streamController);
_markersController = MarkersController(stream: _streamController);

// 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)
..style.width = '100%'
..style.height = '100%';

ui.platformViewRegistry.registerViewFactory(
_getViewType(mapId),
(int viewId) => _div,
);
}

// The internal ID of the map. Used to broadcast events, DOM IDs and everything where a unique ID is needed.
final int _mapId;

Expand Down Expand Up @@ -51,14 +88,14 @@ class GoogleMapController {
gmaps.GMap? _googleMap;

// The StreamController used by this controller and the geometry ones.
final StreamController<MapEvent> _streamController;
final StreamController<MapEvent<Object>> _streamController;

/// The StreamController for the events of this Map. Only for integration testing.
@visibleForTesting
StreamController<MapEvent> get stream => _streamController;
StreamController<MapEvent<Object>> get stream => _streamController;

/// The Stream over which this controller broadcasts events.
Stream<MapEvent> get events => _streamController.stream;
Stream<MapEvent<Object>> get events => _streamController.stream;

// Geometry controllers, for different features of the map.
CirclesController? _circlesController;
Expand All @@ -71,46 +108,6 @@ class GoogleMapController {
// Keeps track if the map is moving or not.
bool _mapIsMoving = false;

/// Initializes the GMap, and the sub-controllers related to it. Wires events.
GoogleMapController({
required int mapId,
required StreamController<MapEvent> streamController,
required CameraPosition initialCameraPosition,
Set<Marker> markers = const <Marker>{},
Set<Polygon> polygons = const <Polygon>{},
Set<Polyline> polylines = const <Polyline>{},
Set<Circle> circles = const <Circle>{},
Set<TileOverlay> tileOverlays = const <TileOverlay>{},
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers =
const <Factory<OneSequenceGestureRecognizer>>{},
Map<String, dynamic> mapOptions = const <String, dynamic>{},
}) : _mapId = mapId,
_streamController = streamController,
_initialCameraPosition = initialCameraPosition,
_markers = markers,
_polygons = polygons,
_polylines = polylines,
_circles = circles,
_rawMapOptions = mapOptions {
_circlesController = CirclesController(stream: this._streamController);
_polygonsController = PolygonsController(stream: this._streamController);
_polylinesController = PolylinesController(stream: this._streamController);
_markersController = MarkersController(stream: this._streamController);

// 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)
..style.width = '100%'
..style.height = '100%';

ui.platformViewRegistry.registerViewFactory(
_getViewType(mapId),
(int viewId) => _div,
);
}

/// Overrides certain properties to install mocks defined during testing.
@visibleForTesting
void debugSetOverrides({
Expand Down Expand Up @@ -161,12 +158,12 @@ class GoogleMapController {
/// 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(_rawMapOptions);
gmaps.MapOptions options = _rawOptionsToGmapsOptions(_rawMapOptions);
// Initial position can only to be set here!
options = _applyInitialPosition(_initialCameraPosition, options);

// Create the map...
final map = _createMap(_div, options);
final gmaps.GMap map = _createMap(_div, options);
_googleMap = map;

_attachMapEvents(map);
Expand All @@ -185,34 +182,34 @@ class GoogleMapController {

// Funnels map gmap events into the plugin's stream controller.
void _attachMapEvents(gmaps.GMap map) {
map.onTilesloaded.first.then((event) {
map.onTilesloaded.first.then((void _) {
// Report the map as ready to go the first time the tiles load
_streamController.add(WebMapReadyEvent(_mapId));
});
map.onClick.listen((event) {
map.onClick.listen((gmaps.IconMouseEvent event) {
assert(event.latLng != null);
_streamController.add(
MapTapEvent(_mapId, _gmLatLngToLatLng(event.latLng!)),
MapTapEvent(_mapId, _gmLatLngToLatLng(event.latLng!)) as MapEvent<Object>,
);
});
map.onRightclick.listen((event) {
map.onRightclick.listen((gmaps.MapMouseEvent event) {
assert(event.latLng != null);
_streamController.add(
MapLongPressEvent(_mapId, _gmLatLngToLatLng(event.latLng!)),
MapLongPressEvent(_mapId, _gmLatLngToLatLng(event.latLng!)) as MapEvent<Object>,
);
});
map.onBoundsChanged.listen((event) {
map.onBoundsChanged.listen((void _) {
if (!_mapIsMoving) {
_mapIsMoving = true;
_streamController.add(CameraMoveStartedEvent(_mapId));
_streamController.add(CameraMoveStartedEvent(_mapId) as MapEvent<Object>);
}
_streamController.add(
CameraMoveEvent(_mapId, _gmViewportToCameraPosition(map)),
);
});
map.onIdle.listen((event) {
map.onIdle.listen((void _) {
_mapIsMoving = false;
_streamController.add(CameraIdleEvent(_mapId));
_streamController.add(CameraIdleEvent(_mapId) as MapEvent<Object>);
});
}

Expand Down Expand Up @@ -243,15 +240,15 @@ class GoogleMapController {

// Renders the initial sets of geometry.
void _renderInitialGeometry({
Set<Marker> markers = const {},
Set<Circle> circles = const {},
Set<Polygon> polygons = const {},
Set<Polyline> polylines = const {},
Set<Marker> markers = const <Marker>{},
Set<Circle> circles = const <Circle>{},
Set<Polygon> polygons = const <Polygon>{},
Set<Polyline> polylines = const <Polyline>{},
}) {
assert(
_controllersBoundToMap,
'Geometry controllers must be bound to a map before any geometry can ' +
'be added to them. Ensure _attachGeometryControllers is called first.');
'Geometry controllers must be bound to a map before any geometry can '
'be added to them. Ensure _attachGeometryControllers is called first.');

// The above assert will only succeed if the controllers have been bound to a map
// in the [_attachGeometryControllers] method, which ensures that all these
Expand Down Expand Up @@ -280,13 +277,14 @@ class GoogleMapController {
void updateRawOptions(Map<String, dynamic> optionsUpdate) {
assert(_googleMap != null, 'Cannot update options on a null map.');

final newOptions = _mergeRawOptions(optionsUpdate);
final Map<String, dynamic> newOptions = _mergeRawOptions(optionsUpdate);

_setOptions(_rawOptionsToGmapsOptions(newOptions));
_setTrafficLayer(_googleMap!, _isTrafficLayerEnabled(newOptions));
}

// Sets new [gmaps.MapOptions] on the wrapped map.
// ignore: use_setters_to_change_properties
void _setOptions(gmaps.MapOptions options) {
_googleMap?.options = options;
}
Expand All @@ -309,17 +307,19 @@ class GoogleMapController {
Future<LatLngBounds> getVisibleRegion() async {
assert(_googleMap != null, 'Cannot get the visible region of a null map.');

return _gmLatLngBoundsTolatLngBounds(
await _googleMap!.bounds ?? _nullGmapsLatLngBounds,
);
final gmaps.LatLngBounds bounds =
await Future<gmaps.LatLngBounds?>.value(_googleMap!.bounds)
?? _nullGmapsLatLngBounds;

return _gmLatLngBoundsTolatLngBounds(bounds);
}

/// Returns the [ScreenCoordinate] for a given viewport [LatLng].
Future<ScreenCoordinate> getScreenCoordinate(LatLng latLng) async {
assert(_googleMap != null,
'Cannot get the screen coordinates with a null map.');

final point = toScreenLocation(_googleMap!, _latLngToGmLatLng(latLng));
final gmaps.Point point = toScreenLocation(_googleMap!, _latLngToGmLatLng(latLng));

return ScreenCoordinate(x: point.x!.toInt(), y: point.y!.toInt());
}
Expand Down Expand Up @@ -424,8 +424,8 @@ class GoogleMapController {
}
}

/// An event fired when a [mapId] on web is interactive.
class WebMapReadyEvent extends MapEvent<void> {
/// A MapEvent event fired when a [mapId] on web is interactive.
class WebMapReadyEvent extends MapEvent<Object> {
/// Build a WebMapReady Event for the map represented by `mapId`.
WebMapReadyEvent(int mapId) : super(mapId, null);
WebMapReadyEvent(int mapId) : super(mapId, mapId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,24 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform {
}

// A cache of map controllers by map Id.
Map _mapById = Map<int, GoogleMapController>();
Map<int, GoogleMapController> _mapById = <int, GoogleMapController>{};

/// Allows tests to inject controllers without going through the buildView flow.
@visibleForTesting
// ignore: use_setters_to_change_properties
void debugSetMapById(Map<int, GoogleMapController> mapById) {
_mapById = mapById;
}

// Convenience getter for a stream of events filtered by their mapId.
Stream<MapEvent> _events(int mapId) => _map(mapId).events;
Stream<MapEvent<Object>> _events(int mapId) => _map(mapId).events;

// Convenience getter for a map controller by its mapId.
GoogleMapController _map(int mapId) {
final controller = _mapById[mapId];
final GoogleMapController? controller = _mapById[mapId];
assert(controller != null,
'Maps cannot be retrieved before calling buildView!');
return controller;
return controller!;
}

@override
Expand Down Expand Up @@ -134,7 +135,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform {
String? mapStyle, {
required int mapId,
}) async {
_map(mapId).updateRawOptions({
_map(mapId).updateRawOptions(<String, dynamic>{
'styles': _mapStyles(mapStyle),
});
}
Expand Down Expand Up @@ -303,13 +304,13 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform {
}) {
// Bail fast if we've already rendered this map ID...
if (_mapById[creationId]?.widget != null) {
return _mapById[creationId].widget;
return _mapById[creationId]!.widget!;
}

final StreamController<MapEvent> controller =
StreamController<MapEvent>.broadcast();
final StreamController<MapEvent<Object>> controller =
StreamController<MapEvent<Object>>.broadcast();

final mapController = GoogleMapController(
final GoogleMapController mapController = GoogleMapController(
initialCameraPosition: initialCameraPosition,
mapId: creationId,
streamController: controller,
Expand All @@ -322,7 +323,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform {

_mapById[creationId] = mapController;

mapController.events.whereType<WebMapReadyEvent>().first.then((event) {
mapController.events.whereType<WebMapReadyEvent>().first.then((WebMapReadyEvent event) {
assert(creationId == event.mapId,
'Received WebMapReadyEvent for the wrong map');
// Notify the plugin now that there's a fully initialized controller.
Expand Down