map: update clusters when idle, fixed cluster entry selection
This commit is contained in:
parent
1f192e58f2
commit
42425f6fcf
5 changed files with 147 additions and 124 deletions
|
@ -70,6 +70,7 @@ class Durations {
|
|||
static const searchDebounceDelay = Duration(milliseconds: 250);
|
||||
static const contentChangeDebounceDelay = Duration(milliseconds: 1000);
|
||||
static const mapScrollDebounceDelay = Duration(milliseconds: 150);
|
||||
static const mapIdleDebounceDelay = Duration(milliseconds: 100);
|
||||
|
||||
// app life
|
||||
static const lastVersionCheckInterval = Duration(days: 7);
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:aves/widgets/common/map/google/map.dart';
|
|||
import 'package:aves/widgets/common/map/leaflet/map.dart';
|
||||
import 'package:aves/widgets/common/map/marker.dart';
|
||||
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:fluster/fluster.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -83,9 +84,12 @@ class _GeoMapState extends State<GeoMap> with TickerProviderStateMixin {
|
|||
final onTap = widget.onMarkerTap;
|
||||
if (onTap == null) return;
|
||||
|
||||
final geoEntries = <GeoEntry>[];
|
||||
final clusterId = geoEntry.clusterId;
|
||||
if (clusterId != null) {
|
||||
Set<AvesEntry> getClusterEntries() {
|
||||
if (clusterId == null) {
|
||||
return {geoEntry.entry!};
|
||||
}
|
||||
|
||||
var points = _defaultMarkerCluster.points(clusterId);
|
||||
if (points.length != geoEntry.pointsSize) {
|
||||
// `Fluster.points()` method does not always return all the points contained in a cluster
|
||||
|
@ -94,11 +98,20 @@ class _GeoMapState extends State<GeoMap> with TickerProviderStateMixin {
|
|||
points = _slowMarkerCluster!.points(clusterId);
|
||||
assert(points.length == geoEntry.pointsSize, 'got ${points.length}/${geoEntry.pointsSize} for geoEntry=$geoEntry');
|
||||
}
|
||||
geoEntries.addAll(points);
|
||||
} else {
|
||||
geoEntries.add(geoEntry);
|
||||
return points.map((geoEntry) => geoEntry.entry!).toSet();
|
||||
}
|
||||
|
||||
AvesEntry? markerEntry;
|
||||
if (clusterId != null) {
|
||||
final uri = geoEntry.childMarkerId;
|
||||
markerEntry = entries.firstWhereOrNull((v) => v.uri == uri);
|
||||
} else {
|
||||
markerEntry = geoEntry.entry;
|
||||
}
|
||||
|
||||
if (markerEntry != null) {
|
||||
onTap(markerEntry, getClusterEntries);
|
||||
}
|
||||
onTap(geoEntries.map((geoEntry) => geoEntry.entry!).toList());
|
||||
}
|
||||
|
||||
return FutureBuilder<bool>(
|
||||
|
@ -110,7 +123,7 @@ class _GeoMapState extends State<GeoMap> with TickerProviderStateMixin {
|
|||
builder: (context, mapStyle, child) {
|
||||
final isGoogleMaps = mapStyle.isGoogleMaps;
|
||||
final progressive = !isGoogleMaps;
|
||||
Widget _buildMarker(MarkerKey key) => ImageMarker(
|
||||
Widget _buildMarkerWidget(MarkerKey key) => ImageMarker(
|
||||
key: key,
|
||||
entry: key.entry,
|
||||
count: key.count,
|
||||
|
@ -127,9 +140,8 @@ class _GeoMapState extends State<GeoMap> with TickerProviderStateMixin {
|
|||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
style: mapStyle,
|
||||
markerBuilder: _buildMarker,
|
||||
markerCluster: _defaultMarkerCluster,
|
||||
markerEntries: entries,
|
||||
markerClusterBuilder: _buildMarkerClusters,
|
||||
markerWidgetBuilder: _buildMarkerWidget,
|
||||
onUserZoomChange: widget.onUserZoomChange,
|
||||
onMarkerTap: _onMarkerTap,
|
||||
)
|
||||
|
@ -140,9 +152,8 @@ class _GeoMapState extends State<GeoMap> with TickerProviderStateMixin {
|
|||
minZoom: 2,
|
||||
maxZoom: 16,
|
||||
style: mapStyle,
|
||||
markerBuilder: _buildMarker,
|
||||
markerCluster: _defaultMarkerCluster,
|
||||
markerEntries: entries,
|
||||
markerClusterBuilder: _buildMarkerClusters,
|
||||
markerWidgetBuilder: _buildMarkerWidget,
|
||||
markerSize: Size(
|
||||
GeoMap.markerImageExtent + ImageMarker.outerBorderWidth * 2,
|
||||
GeoMap.markerImageExtent + ImageMarker.outerBorderWidth * 2 + GeoMap.pointerSize.height,
|
||||
|
@ -232,6 +243,19 @@ class _GeoMapState extends State<GeoMap> with TickerProviderStateMixin {
|
|||
createCluster: (base, lng, lat) => GeoEntry.createCluster(base, lng, lat),
|
||||
);
|
||||
}
|
||||
|
||||
Map<MarkerKey, GeoEntry> _buildMarkerClusters() {
|
||||
final bounds = _boundsNotifier.value;
|
||||
final geoEntries = _defaultMarkerCluster.clusters(bounds.boundingBox, bounds.zoom.round());
|
||||
return Map.fromEntries(geoEntries.map((v) {
|
||||
if (v.isCluster!) {
|
||||
final uri = v.childMarkerId;
|
||||
final entry = entries.firstWhere((v) => v.uri == uri);
|
||||
return MapEntry(MarkerKey(entry, v.pointsSize), v);
|
||||
}
|
||||
return MapEntry(MarkerKey(v.entry!, null), v);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
|
@ -245,6 +269,7 @@ class MarkerKey extends LocalKey with EquatableMixin {
|
|||
const MarkerKey(this.entry, this.count);
|
||||
}
|
||||
|
||||
typedef EntryMarkerBuilder = Widget Function(MarkerKey key);
|
||||
typedef MarkerClusterBuilder = Map<MarkerKey, GeoEntry> Function();
|
||||
typedef MarkerWidgetBuilder = Widget Function(MarkerKey key);
|
||||
typedef UserZoomChangeCallback = void Function(double zoom);
|
||||
typedef MarkerTapCallback = void Function(List<AvesEntry> entries);
|
||||
typedef MarkerTapCallback = void Function(AvesEntry markerEntry, Set<AvesEntry> Function() getClusterEntries);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aves/model/entry.dart';
|
||||
import 'package:aves/model/entry_images.dart';
|
||||
import 'package:aves/model/settings/enums.dart';
|
||||
import 'package:aves/utils/change_notifier.dart';
|
||||
|
@ -12,8 +11,6 @@ import 'package:aves/widgets/common/map/geo_entry.dart';
|
|||
import 'package:aves/widgets/common/map/geo_map.dart';
|
||||
import 'package:aves/widgets/common/map/google/marker_generator.dart';
|
||||
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fluster/fluster.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:latlong2/latlong.dart' as ll;
|
||||
|
@ -24,9 +21,8 @@ class EntryGoogleMap extends StatefulWidget {
|
|||
final bool interactive;
|
||||
final double? minZoom, maxZoom;
|
||||
final EntryMapStyle style;
|
||||
final EntryMarkerBuilder markerBuilder;
|
||||
final Fluster<GeoEntry> markerCluster;
|
||||
final List<AvesEntry> markerEntries;
|
||||
final MarkerClusterBuilder markerClusterBuilder;
|
||||
final MarkerWidgetBuilder markerWidgetBuilder;
|
||||
final UserZoomChangeCallback? onUserZoomChange;
|
||||
final void Function(GeoEntry geoEntry)? onMarkerTap;
|
||||
|
||||
|
@ -38,9 +34,8 @@ class EntryGoogleMap extends StatefulWidget {
|
|||
this.minZoom,
|
||||
this.maxZoom,
|
||||
required this.style,
|
||||
required this.markerBuilder,
|
||||
required this.markerCluster,
|
||||
required this.markerEntries,
|
||||
required this.markerClusterBuilder,
|
||||
required this.markerWidgetBuilder,
|
||||
this.onUserZoomChange,
|
||||
this.onMarkerTap,
|
||||
}) : super(key: key);
|
||||
|
@ -52,6 +47,7 @@ class EntryGoogleMap extends StatefulWidget {
|
|||
class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObserver {
|
||||
GoogleMapController? _googleMapController;
|
||||
final List<StreamSubscription> _subscriptions = [];
|
||||
Map<MarkerKey, GeoEntry> _geoEntryByMarkerKey = {};
|
||||
final Map<MarkerKey, Uint8List> _markerBitmaps = {};
|
||||
final AChangeNotifier _markerBitmapChangeNotifier = AChangeNotifier();
|
||||
|
||||
|
@ -66,28 +62,34 @@ class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObse
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registerWidget(widget);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant EntryGoogleMap oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_unregisterWidget(oldWidget);
|
||||
_registerWidget(widget);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
_googleMapController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _registerWidget(EntryGoogleMap widget) {
|
||||
final avesMapController = widget.controller;
|
||||
if (avesMapController != null) {
|
||||
_subscriptions.add(avesMapController.moveEvents.listen((event) => _moveTo(_toGoogleLatLng(event.latLng))));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant EntryGoogleMap oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
const eq = DeepCollectionEquality();
|
||||
if (!eq.equals(widget.markerEntries, oldWidget.markerEntries)) {
|
||||
_markerBitmaps.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_googleMapController?.dispose();
|
||||
void _unregisterWidget(EntryGoogleMap widget) {
|
||||
_subscriptions
|
||||
..forEach((sub) => sub.cancel())
|
||||
..clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -107,51 +109,35 @@ class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObse
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<ZoomedBounds?>(
|
||||
valueListenable: boundsNotifier,
|
||||
builder: (context, visibleRegion, child) {
|
||||
final allEntries = widget.markerEntries;
|
||||
final geoEntries = visibleRegion != null ? widget.markerCluster.clusters(visibleRegion.boundingBox, visibleRegion.zoom.round()) : <GeoEntry>[];
|
||||
final geoEntryByMarkerKey = Map.fromEntries(geoEntries.map((v) {
|
||||
if (v.isCluster!) {
|
||||
final uri = v.childMarkerId;
|
||||
final entry = allEntries.firstWhere((v) => v.uri == uri);
|
||||
return MapEntry(MarkerKey(entry, v.pointsSize), v);
|
||||
}
|
||||
return MapEntry(MarkerKey(v.entry!, null), v);
|
||||
}));
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
MarkerGeneratorWidget<MarkerKey>(
|
||||
markers: geoEntryByMarkerKey.keys.map(widget.markerBuilder).toList(),
|
||||
isReadyToRender: (key) => key.entry.isThumbnailReady(extent: GeoMap.markerImageExtent),
|
||||
onRendered: (key, bitmap) {
|
||||
_markerBitmaps[key] = bitmap;
|
||||
_markerBitmapChangeNotifier.notifyListeners();
|
||||
},
|
||||
),
|
||||
MapDecorator(
|
||||
interactive: interactive,
|
||||
child: _buildMap(geoEntryByMarkerKey),
|
||||
),
|
||||
MapButtonPanel(
|
||||
boundsNotifier: boundsNotifier,
|
||||
zoomBy: _zoomBy,
|
||||
resetRotation: interactive ? _resetRotation : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
return Stack(
|
||||
children: [
|
||||
MarkerGeneratorWidget<MarkerKey>(
|
||||
markers: _geoEntryByMarkerKey.keys.map(widget.markerWidgetBuilder).toList(),
|
||||
isReadyToRender: (key) => key.entry.isThumbnailReady(extent: GeoMap.markerImageExtent),
|
||||
onRendered: (key, bitmap) {
|
||||
_markerBitmaps[key] = bitmap;
|
||||
_markerBitmapChangeNotifier.notifyListeners();
|
||||
},
|
||||
),
|
||||
MapDecorator(
|
||||
interactive: interactive,
|
||||
child: _buildMap(),
|
||||
),
|
||||
MapButtonPanel(
|
||||
boundsNotifier: boundsNotifier,
|
||||
zoomBy: _zoomBy,
|
||||
resetRotation: interactive ? _resetRotation : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMap(Map<MarkerKey, GeoEntry> geoEntryByMarkerKey) {
|
||||
Widget _buildMap() {
|
||||
return AnimatedBuilder(
|
||||
animation: _markerBitmapChangeNotifier,
|
||||
builder: (context, child) {
|
||||
final markers = <Marker>{};
|
||||
geoEntryByMarkerKey.forEach((markerKey, geoEntry) {
|
||||
_geoEntryByMarkerKey.forEach((markerKey, geoEntry) {
|
||||
final bytes = _markerBitmaps[markerKey];
|
||||
if (bytes != null) {
|
||||
final point = LatLng(geoEntry.latitude!, geoEntry.longitude!);
|
||||
|
@ -195,11 +181,17 @@ class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObse
|
|||
myLocationButtonEnabled: false,
|
||||
markers: markers,
|
||||
onCameraMove: (position) => _updateVisibleRegion(zoom: position.zoom, rotation: -position.bearing),
|
||||
onCameraIdle: _updateClusters,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _updateClusters() {
|
||||
if (!mounted) return;
|
||||
setState(() => _geoEntryByMarkerKey = widget.markerClusterBuilder());
|
||||
}
|
||||
|
||||
Future<void> _updateVisibleRegion({required double zoom, required double rotation}) async {
|
||||
if (!mounted) return;
|
||||
|
||||
|
@ -226,7 +218,6 @@ class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObse
|
|||
final controller = _googleMapController;
|
||||
if (controller == null) return;
|
||||
|
||||
final bounds = boundsNotifier.value;
|
||||
await controller.animateCamera(CameraUpdate.newCameraPosition(CameraPosition(
|
||||
target: _toGoogleLatLng(bounds.center),
|
||||
zoom: bounds.zoom,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:aves/model/entry.dart';
|
||||
import 'package:aves/model/settings/enums.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/utils/debouncer.dart';
|
||||
import 'package:aves/widgets/common/map/buttons.dart';
|
||||
import 'package:aves/widgets/common/map/controller.dart';
|
||||
import 'package:aves/widgets/common/map/decorator.dart';
|
||||
|
@ -11,7 +12,6 @@ import 'package:aves/widgets/common/map/latlng_tween.dart';
|
|||
import 'package:aves/widgets/common/map/leaflet/scale_layer.dart';
|
||||
import 'package:aves/widgets/common/map/leaflet/tile_layers.dart';
|
||||
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
||||
import 'package:fluster/fluster.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
@ -22,9 +22,8 @@ class EntryLeafletMap extends StatefulWidget {
|
|||
final bool interactive;
|
||||
final double minZoom, maxZoom;
|
||||
final EntryMapStyle style;
|
||||
final EntryMarkerBuilder markerBuilder;
|
||||
final Fluster<GeoEntry> markerCluster;
|
||||
final List<AvesEntry> markerEntries;
|
||||
final MarkerClusterBuilder markerClusterBuilder;
|
||||
final MarkerWidgetBuilder markerWidgetBuilder;
|
||||
final Size markerSize;
|
||||
final UserZoomChangeCallback? onUserZoomChange;
|
||||
final void Function(GeoEntry geoEntry)? onMarkerTap;
|
||||
|
@ -37,9 +36,8 @@ class EntryLeafletMap extends StatefulWidget {
|
|||
this.minZoom = 0,
|
||||
this.maxZoom = 22,
|
||||
required this.style,
|
||||
required this.markerBuilder,
|
||||
required this.markerCluster,
|
||||
required this.markerEntries,
|
||||
required this.markerClusterBuilder,
|
||||
required this.markerWidgetBuilder,
|
||||
required this.markerSize,
|
||||
this.onUserZoomChange,
|
||||
this.onMarkerTap,
|
||||
|
@ -52,6 +50,8 @@ class EntryLeafletMap extends StatefulWidget {
|
|||
class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderStateMixin {
|
||||
final MapController _leafletMapController = MapController();
|
||||
final List<StreamSubscription> _subscriptions = [];
|
||||
Map<MarkerKey, GeoEntry> _geoEntryByMarkerKey = {};
|
||||
final Debouncer _debouncer = Debouncer(delay: Durations.mapIdleDebounceDelay);
|
||||
|
||||
ValueNotifier<ZoomedBounds> get boundsNotifier => widget.boundsNotifier;
|
||||
|
||||
|
@ -65,58 +65,59 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registerWidget(widget);
|
||||
WidgetsBinding.instance!.addPostFrameCallback((_) => _updateVisibleRegion());
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant EntryLeafletMap oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_unregisterWidget(oldWidget);
|
||||
_registerWidget(widget);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _registerWidget(EntryLeafletMap widget) {
|
||||
final avesMapController = widget.controller;
|
||||
if (avesMapController != null) {
|
||||
_subscriptions.add(avesMapController.moveEvents.listen((event) => _moveTo(event.latLng)));
|
||||
}
|
||||
_subscriptions.add(_leafletMapController.mapEventStream.listen((event) => _updateVisibleRegion()));
|
||||
WidgetsBinding.instance!.addPostFrameCallback((_) => _updateVisibleRegion());
|
||||
boundsNotifier.addListener(_onBoundsChange);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
void _unregisterWidget(EntryLeafletMap widget) {
|
||||
boundsNotifier.removeListener(_onBoundsChange);
|
||||
_subscriptions
|
||||
..forEach((sub) => sub.cancel())
|
||||
..clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<ZoomedBounds?>(
|
||||
valueListenable: boundsNotifier,
|
||||
builder: (context, visibleRegion, child) {
|
||||
final allEntries = widget.markerEntries;
|
||||
final geoEntries = visibleRegion != null ? widget.markerCluster.clusters(visibleRegion.boundingBox, visibleRegion.zoom.round()) : <GeoEntry>[];
|
||||
final geoEntryByMarkerKey = Map.fromEntries(geoEntries.map((v) {
|
||||
if (v.isCluster!) {
|
||||
final uri = v.childMarkerId;
|
||||
final entry = allEntries.firstWhere((v) => v.uri == uri);
|
||||
return MapEntry(MarkerKey(entry, v.pointsSize), v);
|
||||
}
|
||||
return MapEntry(MarkerKey(v.entry!, null), v);
|
||||
}));
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
MapDecorator(
|
||||
interactive: interactive,
|
||||
child: _buildMap(geoEntryByMarkerKey),
|
||||
),
|
||||
MapButtonPanel(
|
||||
boundsNotifier: boundsNotifier,
|
||||
zoomBy: _zoomBy,
|
||||
resetRotation: interactive ? _resetRotation : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
return Stack(
|
||||
children: [
|
||||
MapDecorator(
|
||||
interactive: interactive,
|
||||
child: _buildMap(),
|
||||
),
|
||||
MapButtonPanel(
|
||||
boundsNotifier: boundsNotifier,
|
||||
zoomBy: _zoomBy,
|
||||
resetRotation: interactive ? _resetRotation : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMap(Map<MarkerKey, GeoEntry> geoEntryByMarkerKey) {
|
||||
Widget _buildMap() {
|
||||
final markerSize = widget.markerSize;
|
||||
final markers = geoEntryByMarkerKey.entries.map((kv) {
|
||||
final markers = _geoEntryByMarkerKey.entries.map((kv) {
|
||||
final markerKey = kv.key;
|
||||
final geoEntry = kv.value;
|
||||
final latLng = LatLng(geoEntry.latitude!, geoEntry.longitude!);
|
||||
|
@ -124,7 +125,7 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
|
|||
point: latLng,
|
||||
builder: (context) => GestureDetector(
|
||||
onTap: () => widget.onMarkerTap?.call(geoEntry),
|
||||
child: widget.markerBuilder(markerKey),
|
||||
child: widget.markerWidgetBuilder(markerKey),
|
||||
),
|
||||
width: markerSize.width,
|
||||
height: markerSize.height,
|
||||
|
@ -173,6 +174,13 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
|
|||
}
|
||||
}
|
||||
|
||||
void _onBoundsChange() => _debouncer(_updateClusters);
|
||||
|
||||
void _updateClusters() {
|
||||
if (!mounted) return;
|
||||
setState(() => _geoEntryByMarkerKey = widget.markerClusterBuilder());
|
||||
}
|
||||
|
||||
void _updateVisibleRegion() {
|
||||
final bounds = _leafletMapController.bounds;
|
||||
if (bounds != null) {
|
||||
|
|
|
@ -74,14 +74,12 @@ class _MapPageState extends State<MapPage> {
|
|||
entries: entries,
|
||||
interactive: true,
|
||||
isAnimatingNotifier: _isAnimatingNotifier,
|
||||
onMarkerTap: (markerEntries) {
|
||||
if (markerEntries.isEmpty) return;
|
||||
final entry = markerEntries.first;
|
||||
final index = entries.indexOf(entry);
|
||||
onMarkerTap: (markerEntry, getClusterEntries) {
|
||||
final index = entries.indexOf(markerEntry);
|
||||
if (_selectedIndexNotifier.value != index) {
|
||||
_selectedIndexNotifier.value = index;
|
||||
} else {
|
||||
_moveToEntry(entry);
|
||||
_moveToEntry(markerEntry);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue