blob: b650b9bcf1c8555849bdcf590c5a5d8ce4281b7b [file] [log] [blame]
// 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.
part of google_maps_flutter_web;
/// 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<MarkerId, MarkerController> _markerIdToController;
// The stream over which markers broadcast their events
StreamController<MapEvent> _streamController;
/// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers.
MarkersController({
required StreamController<MapEvent> stream,
}) : _streamController = stream,
_markerIdToController = Map<MarkerId, MarkerController>();
/// Returns the cache of [MarkerController]s. Test only.
@visibleForTesting
Map<MarkerId, MarkerController> get markers => _markerIdToController;
/// Adds a set of [Marker] objects to the cache.
///
/// Wraps each [Marker] into its corresponding [MarkerController].
void addMarkers(Set<Marker> markersToAdd) {
markersToAdd.forEach(_addMarker);
}
void _addMarker(Marker marker) {
if (marker == null) {
return;
}
final infoWindowOptions = _infoWindowOptionsFromMarker(marker);
gmaps.InfoWindow? gmInfoWindow;
if (infoWindowOptions != null) {
gmInfoWindow = gmaps.InfoWindow(infoWindowOptions);
// Google Maps' JS SDK does not have a click event on the InfoWindow, so
// we make one...
if (infoWindowOptions.content is HtmlElement) {
final content = infoWindowOptions.content as HtmlElement;
content.onClick.listen((_) {
_onInfoWindowTap(marker.markerId);
});
}
}
final currentMarker = _markerIdToController[marker.markerId]?.marker;
final populationOptions = _markerOptionsFromMarker(marker, currentMarker);
gmaps.Marker gmMarker = gmaps.Marker(populationOptions);
gmMarker.map = googleMap;
MarkerController controller = MarkerController(
marker: gmMarker,
infoWindow: gmInfoWindow,
consumeTapEvents: marker.consumeTapEvents,
onTap: () {
this.showMarkerInfoWindow(marker.markerId);
_onMarkerTap(marker.markerId);
},
onDragEnd: (gmaps.LatLng latLng) {
_onMarkerDragEnd(marker.markerId, latLng);
},
);
_markerIdToController[marker.markerId] = controller;
}
/// Updates a set of [Marker] objects with new options.
void changeMarkers(Set<Marker> markersToChange) {
markersToChange.forEach(_changeMarker);
}
void _changeMarker(Marker marker) {
MarkerController? markerController = _markerIdToController[marker.markerId];
if (markerController != null) {
final markerOptions = _markerOptionsFromMarker(
marker,
markerController.marker,
);
final infoWindow = _infoWindowOptionsFromMarker(marker);
markerController.update(
markerOptions,
newInfoWindowContent: infoWindow?.content as HtmlElement?,
);
}
}
/// Removes a set of [MarkerId]s from the cache.
void removeMarkers(Set<MarkerId> markerIdsToRemove) {
markerIdsToRemove.forEach(_removeMarker);
}
void _removeMarker(MarkerId markerId) {
final MarkerController? markerController = _markerIdToController[markerId];
markerController?.remove();
_markerIdToController.remove(markerId);
}
// InfoWindow...
/// Shows the [InfoWindow] of a [MarkerId].
///
/// See also [hideMarkerInfoWindow] and [isInfoWindowShown].
void showMarkerInfoWindow(MarkerId markerId) {
_hideAllMarkerInfoWindow();
MarkerController? markerController = _markerIdToController[markerId];
markerController?.showInfoWindow();
}
/// 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 [MarkerId] is shown.
///
/// See also [showMarkerInfoWindow] and [hideMarkerInfoWindow].
bool isInfoWindowShown(MarkerId markerId) {
MarkerController? markerController = _markerIdToController[markerId];
return markerController?.infoWindowShown ?? false;
}
// 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));
return _markerIdToController[markerId]?.consumeTapEvents ?? false;
}
void _onInfoWindowTap(MarkerId markerId) {
_streamController.add(InfoWindowTapEvent(mapId, markerId));
}
void _onMarkerDragEnd(MarkerId markerId, gmaps.LatLng latLng) {
_streamController.add(MarkerDragEndEvent(
mapId,
_gmLatLngToLatLng(latLng),
markerId,
));
}
void _hideAllMarkerInfoWindow() {
_markerIdToController.values
.where((controller) =>
controller == null ? false : controller.infoWindowShown)
.forEach((controller) => controller.hideInfoWindow());
}
}