tv: stat donut legend focus, map captioned buttons, viewer button focus
This commit is contained in:
parent
4d226e6e38
commit
bcced35e66
25 changed files with 219 additions and 110 deletions
|
@ -25,6 +25,7 @@ class MediaSessionHandler(private val context: Context, private val mediaCommand
|
||||||
|
|
||||||
private var session: MediaSessionCompat? = null
|
private var session: MediaSessionCompat? = null
|
||||||
private var wasPlaying = false
|
private var wasPlaying = false
|
||||||
|
private var isNoisyAudioReceiverRegistered = false
|
||||||
private val noisyAudioReceiver = object : BroadcastReceiver() {
|
private val noisyAudioReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
if (intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) {
|
if (intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) {
|
||||||
|
@ -34,7 +35,10 @@ class MediaSessionHandler(private val context: Context, private val mediaCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dispose() {
|
fun dispose() {
|
||||||
|
if (isNoisyAudioReceiverRegistered) {
|
||||||
context.unregisterReceiver(noisyAudioReceiver)
|
context.unregisterReceiver(noisyAudioReceiver)
|
||||||
|
isNoisyAudioReceiverRegistered = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
@ -110,8 +114,10 @@ class MediaSessionHandler(private val context: Context, private val mediaCommand
|
||||||
val isPlaying = state == PlaybackStateCompat.STATE_PLAYING
|
val isPlaying = state == PlaybackStateCompat.STATE_PLAYING
|
||||||
if (!wasPlaying && isPlaying) {
|
if (!wasPlaying && isPlaying) {
|
||||||
context.registerReceiver(noisyAudioReceiver, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY))
|
context.registerReceiver(noisyAudioReceiver, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY))
|
||||||
|
isNoisyAudioReceiverRegistered = true
|
||||||
} else if (wasPlaying && !isPlaying) {
|
} else if (wasPlaying && !isPlaying) {
|
||||||
context.unregisterReceiver(noisyAudioReceiver)
|
context.unregisterReceiver(noisyAudioReceiver)
|
||||||
|
isNoisyAudioReceiverRegistered = false
|
||||||
}
|
}
|
||||||
wasPlaying = isPlaying
|
wasPlaying = isPlaying
|
||||||
}
|
}
|
||||||
|
|
35
lib/model/actions/map_actions.dart
Normal file
35
lib/model/actions/map_actions.dart
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
enum MapAction {
|
||||||
|
selectStyle,
|
||||||
|
zoomIn,
|
||||||
|
zoomOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ExtraMapAction on MapAction {
|
||||||
|
String getText(BuildContext context) {
|
||||||
|
switch (this) {
|
||||||
|
case MapAction.selectStyle:
|
||||||
|
return context.l10n.mapStyleTooltip;
|
||||||
|
case MapAction.zoomIn:
|
||||||
|
return context.l10n.mapZoomInTooltip;
|
||||||
|
case MapAction.zoomOut:
|
||||||
|
return context.l10n.mapZoomOutTooltip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getIcon() => Icon(_getIconData());
|
||||||
|
|
||||||
|
IconData _getIconData() {
|
||||||
|
switch (this) {
|
||||||
|
case MapAction.selectStyle:
|
||||||
|
return AIcons.layers;
|
||||||
|
case MapAction.zoomIn:
|
||||||
|
return AIcons.zoomIn;
|
||||||
|
case MapAction.zoomOut:
|
||||||
|
return AIcons.zoomOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,8 +27,6 @@ class Device {
|
||||||
|
|
||||||
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
||||||
|
|
||||||
bool get isReadOnly => _isTelevision;
|
|
||||||
|
|
||||||
bool get isTelevision => _isTelevision;
|
bool get isTelevision => _isTelevision;
|
||||||
|
|
||||||
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
|
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'dart:io';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:aves/geo/countries.dart';
|
import 'package:aves/geo/countries.dart';
|
||||||
import 'package:aves/model/device.dart';
|
|
||||||
import 'package:aves/model/entry_cache.dart';
|
import 'package:aves/model/entry_cache.dart';
|
||||||
import 'package:aves/model/entry_dirs.dart';
|
import 'package:aves/model/entry_dirs.dart';
|
||||||
import 'package:aves/model/favourites.dart';
|
import 'package:aves/model/favourites.dart';
|
||||||
|
@ -12,6 +11,7 @@ import 'package:aves/model/metadata/address.dart';
|
||||||
import 'package:aves/model/metadata/catalog.dart';
|
import 'package:aves/model/metadata/catalog.dart';
|
||||||
import 'package:aves/model/metadata/trash.dart';
|
import 'package:aves/model/metadata/trash.dart';
|
||||||
import 'package:aves/model/multipage.dart';
|
import 'package:aves/model/multipage.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/model/source/trash.dart';
|
import 'package:aves/model/source/trash.dart';
|
||||||
import 'package:aves/model/video/metadata.dart';
|
import 'package:aves/model/video/metadata.dart';
|
||||||
import 'package:aves/ref/mime_types.dart';
|
import 'package:aves/ref/mime_types.dart';
|
||||||
|
@ -281,7 +281,7 @@ class AvesEntry {
|
||||||
|
|
||||||
bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains);
|
bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains);
|
||||||
|
|
||||||
bool get canEdit => !device.isReadOnly && path != null && !trashed && isMediaStoreContent;
|
bool get canEdit => !settings.isReadOnly && path != null && !trashed && isMediaStoreContent;
|
||||||
|
|
||||||
bool get canEditDate => canEdit && (canEditExif || canEditXmp);
|
bool get canEditDate => canEdit && (canEditExif || canEditXmp);
|
||||||
|
|
||||||
|
|
|
@ -403,6 +403,8 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
bool get useTvLayout => device.isTelevision || forceTvLayout;
|
bool get useTvLayout => device.isTelevision || forceTvLayout;
|
||||||
|
|
||||||
|
bool get isReadOnly => useTvLayout;
|
||||||
|
|
||||||
// navigation
|
// navigation
|
||||||
|
|
||||||
bool get mustBackTwiceToExit => getBool(mustBackTwiceToExitKey) ?? SettingsDefaults.mustBackTwiceToExit;
|
bool get mustBackTwiceToExit => getBool(mustBackTwiceToExitKey) ?? SettingsDefaults.mustBackTwiceToExit;
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:aves/app_mode.dart';
|
import 'package:aves/app_mode.dart';
|
||||||
import 'package:aves/model/actions/entry_set_actions.dart';
|
import 'package:aves/model/actions/entry_set_actions.dart';
|
||||||
import 'package:aves/model/device.dart';
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/filters/query.dart';
|
import 'package:aves/model/filters/query.dart';
|
||||||
|
@ -392,7 +391,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
...(isSelecting ? selectionMenuActions : browsingMenuActions).where(isVisible).map(
|
...(isSelecting ? selectionMenuActions : browsingMenuActions).where(isVisible).map(
|
||||||
(action) => _toMenuItem(action, enabled: canApply(action), selection: selection),
|
(action) => _toMenuItem(action, enabled: canApply(action), selection: selection),
|
||||||
),
|
),
|
||||||
if (isSelecting && !device.isReadOnly && appMode == AppMode.main && !isTrash)
|
if (isSelecting && !settings.isReadOnly && appMode == AppMode.main && !isTrash)
|
||||||
PopupMenuItem<EntrySetAction>(
|
PopupMenuItem<EntrySetAction>(
|
||||||
enabled: hasSelection,
|
enabled: hasSelection,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
|
|
@ -55,7 +55,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
required int selectedItemCount,
|
required int selectedItemCount,
|
||||||
required bool isTrash,
|
required bool isTrash,
|
||||||
}) {
|
}) {
|
||||||
final canWrite = !device.isReadOnly;
|
final canWrite = !settings.isReadOnly;
|
||||||
final isMain = appMode == AppMode.main;
|
final isMain = appMode == AppMode.main;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
// general
|
// general
|
||||||
|
|
|
@ -34,17 +34,16 @@ class SectionHeader<T> extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = _buildContent(context);
|
Widget child = _buildContent(context);
|
||||||
if (settings.useTvLayout) {
|
if (settings.useTvLayout) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final primaryColor = Theme.of(context).colorScheme.primary;
|
||||||
child = Material(
|
child = Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: InkResponse(
|
child: InkResponse(
|
||||||
onTap: _onTap(context),
|
onTap: _onTap(context),
|
||||||
onHover: (_) {},
|
containedInkWell: true,
|
||||||
highlightShape: BoxShape.rectangle,
|
highlightShape: BoxShape.rectangle,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(123)),
|
borderRadius: const BorderRadius.all(Radius.circular(123)),
|
||||||
containedInkWell: true,
|
hoverColor: primaryColor.withOpacity(0.04),
|
||||||
splashColor: colors.primary.withOpacity(0.12),
|
splashColor: primaryColor.withOpacity(0.12),
|
||||||
hoverColor: colors.primary.withOpacity(0.04),
|
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -73,7 +73,7 @@ class _OverlayButtonState extends State<OverlayButton> {
|
||||||
builder: (context, focused, child) {
|
builder: (context, focused, child) {
|
||||||
final border = AvesBorder.border(
|
final border = AvesBorder.border(
|
||||||
context,
|
context,
|
||||||
width: AvesBorder.curvedBorderWidth * (focused ? 2 : 1),
|
width: AvesBorder.curvedBorderWidth * (focused ? 3 : 1),
|
||||||
);
|
);
|
||||||
return borderRadius != null
|
return borderRadius != null
|
||||||
? BlurredRRect(
|
? BlurredRRect(
|
||||||
|
|
|
@ -1,28 +1,27 @@
|
||||||
import 'package:aves/model/settings/enums/map_style.dart';
|
import 'package:aves/model/actions/map_actions.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/map/buttons/button.dart';
|
import 'package:aves/widgets/common/map/buttons/button.dart';
|
||||||
import 'package:aves/widgets/common/map/buttons/coordinate_filter.dart';
|
import 'package:aves/widgets/common/map/buttons/coordinate_filter.dart';
|
||||||
import 'package:aves/widgets/common/map/compass.dart';
|
import 'package:aves/widgets/common/map/compass.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/common/map/map_action_delegate.dart';
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class MapButtonPanel extends StatelessWidget {
|
class MapButtonPanel extends StatelessWidget {
|
||||||
|
final AvesMapController? controller;
|
||||||
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
||||||
final Future<void> Function(double amount)? zoomBy;
|
|
||||||
final void Function(BuildContext context)? openMapPage;
|
final void Function(BuildContext context)? openMapPage;
|
||||||
final VoidCallback? resetRotation;
|
final VoidCallback? resetRotation;
|
||||||
|
|
||||||
const MapButtonPanel({
|
const MapButtonPanel({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.controller,
|
||||||
required this.boundsNotifier,
|
required this.boundsNotifier,
|
||||||
this.zoomBy,
|
|
||||||
this.openMapPage,
|
this.openMapPage,
|
||||||
this.resetRotation,
|
this.resetRotation,
|
||||||
});
|
});
|
||||||
|
@ -123,21 +122,8 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
: const Spacer(),
|
: const Spacer(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: padding),
|
padding: EdgeInsets.only(top: padding),
|
||||||
child: MapOverlayButton(
|
|
||||||
// key is expected by test driver
|
// key is expected by test driver
|
||||||
buttonKey: const Key('map-menu-layers'),
|
child: _buildButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')),
|
||||||
icon: const Icon(AIcons.layers),
|
|
||||||
onPressed: () => showSelectionDialog<EntryMapStyle>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AvesSelectionDialog<EntryMapStyle?>(
|
|
||||||
initialValue: settings.mapStyle,
|
|
||||||
options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))),
|
|
||||||
title: context.l10n.mapStyleDialogTitle,
|
|
||||||
),
|
|
||||||
onSelection: (v) => settings.mapStyle = v,
|
|
||||||
),
|
|
||||||
tooltip: context.l10n.mapStyleTooltip,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -148,17 +134,9 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
MapOverlayButton(
|
_buildButton(context, MapAction.zoomIn),
|
||||||
icon: const Icon(AIcons.zoomIn),
|
|
||||||
onPressed: zoomBy != null ? () => zoomBy?.call(1) : null,
|
|
||||||
tooltip: context.l10n.mapZoomInTooltip,
|
|
||||||
),
|
|
||||||
SizedBox(height: padding),
|
SizedBox(height: padding),
|
||||||
MapOverlayButton(
|
_buildButton(context, MapAction.zoomOut),
|
||||||
icon: const Icon(AIcons.zoomOut),
|
|
||||||
onPressed: zoomBy != null ? () => zoomBy?.call(-1) : null,
|
|
||||||
tooltip: context.l10n.mapZoomOutTooltip,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -168,4 +146,11 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildButton(BuildContext context, MapAction action, {Key? buttonKey}) => MapOverlayButton(
|
||||||
|
buttonKey: buttonKey,
|
||||||
|
icon: action.getIcon(),
|
||||||
|
onPressed: () => MapActionDelegate(controller).onActionSelected(context, action),
|
||||||
|
tooltip: action.getText(context),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
);
|
);
|
||||||
bool _isMarkerImageReady(MarkerKey<AvesEntry> key) => key.entry.isThumbnailReady(extent: MapThemeData.markerImageExtent);
|
bool _isMarkerImageReady(MarkerKey<AvesEntry> key) => key.entry.isThumbnailReady(extent: MapThemeData.markerImageExtent);
|
||||||
|
|
||||||
|
final controller = widget.controller;
|
||||||
Widget child = const SizedBox();
|
Widget child = const SizedBox();
|
||||||
if (mapStyle != null) {
|
if (mapStyle != null) {
|
||||||
switch (mapStyle) {
|
switch (mapStyle) {
|
||||||
|
@ -153,7 +154,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
case EntryMapStyle.hmsNormal:
|
case EntryMapStyle.hmsNormal:
|
||||||
case EntryMapStyle.hmsTerrain:
|
case EntryMapStyle.hmsTerrain:
|
||||||
child = mobileServices.buildMap<AvesEntry>(
|
child = mobileServices.buildMap<AvesEntry>(
|
||||||
controller: widget.controller,
|
controller: controller,
|
||||||
clusterListenable: _clusterChangeNotifier,
|
clusterListenable: _clusterChangeNotifier,
|
||||||
boundsNotifier: _boundsNotifier,
|
boundsNotifier: _boundsNotifier,
|
||||||
style: mapStyle,
|
style: mapStyle,
|
||||||
|
@ -175,7 +176,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
case EntryMapStyle.stamenToner:
|
case EntryMapStyle.stamenToner:
|
||||||
case EntryMapStyle.stamenWatercolor:
|
case EntryMapStyle.stamenWatercolor:
|
||||||
child = EntryLeafletMap<AvesEntry>(
|
child = EntryLeafletMap<AvesEntry>(
|
||||||
controller: widget.controller,
|
controller: controller,
|
||||||
clusterListenable: _clusterChangeNotifier,
|
clusterListenable: _clusterChangeNotifier,
|
||||||
boundsNotifier: _boundsNotifier,
|
boundsNotifier: _boundsNotifier,
|
||||||
minZoom: 2,
|
minZoom: 2,
|
||||||
|
@ -260,6 +261,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
children: [
|
children: [
|
||||||
const MapDecorator(),
|
const MapDecorator(),
|
||||||
MapButtonPanel(
|
MapButtonPanel(
|
||||||
|
controller: controller,
|
||||||
boundsNotifier: _boundsNotifier,
|
boundsNotifier: _boundsNotifier,
|
||||||
openMapPage: widget.openMapPage,
|
openMapPage: widget.openMapPage,
|
||||||
),
|
),
|
||||||
|
@ -485,14 +487,13 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
|
|
||||||
Widget _decorateMap(BuildContext context, Widget? child) => MapDecorator(child: child);
|
Widget _decorateMap(BuildContext context, Widget? child) => MapDecorator(child: child);
|
||||||
|
|
||||||
Widget _buildButtonPanel(
|
Widget _buildButtonPanel(VoidCallback resetRotation) {
|
||||||
Future<void> Function(double amount) zoomBy,
|
if (settings.useTvLayout) return const SizedBox();
|
||||||
VoidCallback resetRotation,
|
return MapButtonPanel(
|
||||||
) =>
|
controller: widget.controller,
|
||||||
MapButtonPanel(
|
|
||||||
boundsNotifier: _boundsNotifier,
|
boundsNotifier: _boundsNotifier,
|
||||||
zoomBy: zoomBy,
|
|
||||||
openMapPage: widget.openMapPage,
|
openMapPage: widget.openMapPage,
|
||||||
resetRotation: resetRotation,
|
resetRotation: resetRotation,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -95,6 +95,7 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
||||||
final avesMapController = widget.controller;
|
final avesMapController = widget.controller;
|
||||||
if (avesMapController != null) {
|
if (avesMapController != null) {
|
||||||
_subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(event.latLng)));
|
_subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(event.latLng)));
|
||||||
|
_subscriptions.add(avesMapController.zoomCommands.listen((event) => _zoomBy(event.delta)));
|
||||||
}
|
}
|
||||||
_subscriptions.add(_leafletMapController.mapEventStream.listen((event) => _updateVisibleRegion()));
|
_subscriptions.add(_leafletMapController.mapEventStream.listen((event) => _updateVisibleRegion()));
|
||||||
widget.clusterListenable.addListener(_updateMarkers);
|
widget.clusterListenable.addListener(_updateMarkers);
|
||||||
|
@ -114,7 +115,7 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
widget.decoratorBuilder(context, _buildMap()),
|
widget.decoratorBuilder(context, _buildMap()),
|
||||||
widget.buttonPanelBuilder(_zoomBy, _resetRotation),
|
widget.buttonPanelBuilder(_resetRotation),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
37
lib/widgets/common/map/map_action_delegate.dart
Normal file
37
lib/widgets/common/map/map_action_delegate.dart
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import 'package:aves/model/actions/map_actions.dart';
|
||||||
|
import 'package:aves/model/settings/enums/map_style.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/services/common/services.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
||||||
|
import 'package:aves_map/aves_map.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class MapActionDelegate {
|
||||||
|
final AvesMapController? controller;
|
||||||
|
|
||||||
|
const MapActionDelegate(this.controller);
|
||||||
|
|
||||||
|
void onActionSelected(BuildContext context, MapAction action) {
|
||||||
|
switch (action) {
|
||||||
|
case MapAction.selectStyle:
|
||||||
|
showSelectionDialog<EntryMapStyle>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AvesSelectionDialog<EntryMapStyle?>(
|
||||||
|
initialValue: settings.mapStyle,
|
||||||
|
options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))),
|
||||||
|
title: context.l10n.mapStyleDialogTitle,
|
||||||
|
),
|
||||||
|
onSelection: (v) => settings.mapStyle = v,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MapAction.zoomIn:
|
||||||
|
controller?.zoomBy(1);
|
||||||
|
break;
|
||||||
|
case MapAction.zoomOut:
|
||||||
|
controller?.zoomBy(-1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ import 'dart:io';
|
||||||
import 'package:aves/app_mode.dart';
|
import 'package:aves/app_mode.dart';
|
||||||
import 'package:aves/model/actions/chip_set_actions.dart';
|
import 'package:aves/model/actions/chip_set_actions.dart';
|
||||||
import 'package:aves/model/actions/move_type.dart';
|
import 'package:aves/model/actions/move_type.dart';
|
||||||
import 'package:aves/model/device.dart';
|
|
||||||
import 'package:aves/model/filters/album.dart';
|
import 'package:aves/model/filters/album.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/highlight.dart';
|
import 'package:aves/model/highlight.dart';
|
||||||
|
@ -76,10 +75,10 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
||||||
}) {
|
}) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ChipSetAction.createAlbum:
|
case ChipSetAction.createAlbum:
|
||||||
return !device.isReadOnly && appMode == AppMode.main && !isSelecting;
|
return !settings.isReadOnly && appMode == AppMode.main && !isSelecting;
|
||||||
case ChipSetAction.delete:
|
case ChipSetAction.delete:
|
||||||
case ChipSetAction.rename:
|
case ChipSetAction.rename:
|
||||||
return !device.isReadOnly && appMode == AppMode.main && isSelecting;
|
return !settings.isReadOnly && appMode == AppMode.main && isSelecting;
|
||||||
default:
|
default:
|
||||||
return super.isVisible(
|
return super.isVisible(
|
||||||
action,
|
action,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:aves/app_mode.dart';
|
import 'package:aves/app_mode.dart';
|
||||||
|
import 'package:aves/model/actions/map_actions.dart';
|
||||||
import 'package:aves/model/actions/map_cluster_actions.dart';
|
import 'package:aves/model/actions/map_cluster_actions.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/coordinate.dart';
|
import 'package:aves/model/filters/coordinate.dart';
|
||||||
|
@ -16,11 +17,14 @@ import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/utils/debouncer.dart';
|
import 'package:aves/utils/debouncer.dart';
|
||||||
import 'package:aves/widgets/collection/collection_page.dart';
|
import 'package:aves/widgets/collection/collection_page.dart';
|
||||||
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
|
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
|
||||||
|
import 'package:aves/widgets/common/basic/insets.dart';
|
||||||
import 'package:aves/widgets/common/basic/menu.dart';
|
import 'package:aves/widgets/common/basic/menu.dart';
|
||||||
import 'package:aves/widgets/common/behaviour/routes.dart';
|
import 'package:aves/widgets/common/behaviour/routes.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
|
||||||
import 'package:aves/widgets/common/identity/empty.dart';
|
import 'package:aves/widgets/common/identity/empty.dart';
|
||||||
import 'package:aves/widgets/common/map/geo_map.dart';
|
import 'package:aves/widgets/common/map/geo_map.dart';
|
||||||
|
import 'package:aves/widgets/common/map/map_action_delegate.dart';
|
||||||
import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
|
import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
|
||||||
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
|
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
|
||||||
import 'package:aves/widgets/common/thumbnail/scroller.dart';
|
import 'package:aves/widgets/common/thumbnail/scroller.dart';
|
||||||
|
@ -231,7 +235,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMap() {
|
Widget _buildMap() {
|
||||||
return MapTheme(
|
Widget child = MapTheme(
|
||||||
interactive: true,
|
interactive: true,
|
||||||
showCoordinateFilter: true,
|
showCoordinateFilter: true,
|
||||||
navigationButton: MapNavigationButton.back,
|
navigationButton: MapNavigationButton.back,
|
||||||
|
@ -259,6 +263,37 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
||||||
onMarkerLongPress: _onMarkerLongPress,
|
onMarkerLongPress: _onMarkerLongPress,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
if (settings.useTvLayout) {
|
||||||
|
child = DirectionalSafeArea(
|
||||||
|
top: false,
|
||||||
|
end: false,
|
||||||
|
bottom: false,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
MapAction.selectStyle,
|
||||||
|
MapAction.zoomIn,
|
||||||
|
MapAction.zoomOut,
|
||||||
|
]
|
||||||
|
.map((action) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: CaptionedButton(
|
||||||
|
icon: action.getIcon(),
|
||||||
|
caption: action.getText(context),
|
||||||
|
onPressed: () => MapActionDelegate(_mapController).onActionSelected(context, action),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(child: child),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildOverlayController() {
|
Widget _buildOverlayController() {
|
||||||
|
|
|
@ -111,14 +111,22 @@ class _MimeDonutState extends State<MimeDonut> with AutomaticKeepAliveClientMixi
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
final primaryColor = Theme.of(context).colorScheme.primary;
|
||||||
final legend = SizedBox(
|
final legend = SizedBox(
|
||||||
width: dim,
|
width: dim,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: seriesData
|
children: seriesData
|
||||||
.map((d) => GestureDetector(
|
.map((d) => Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: InkResponse(
|
||||||
onTap: () => widget.onFilterSelection(MimeFilter(d.mimeType)),
|
onTap: () => widget.onFilterSelection(MimeFilter(d.mimeType)),
|
||||||
|
containedInkWell: true,
|
||||||
|
highlightShape: BoxShape.rectangle,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(123)),
|
||||||
|
hoverColor: primaryColor.withOpacity(0.04),
|
||||||
|
splashColor: primaryColor.withOpacity(0.12),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
@ -139,8 +147,10 @@ class _MimeDonutState extends State<MimeDonut> with AutomaticKeepAliveClientMixi
|
||||||
color: Theme.of(context).textTheme.bodySmall!.color,
|
color: Theme.of(context).textTheme.bodySmall!.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -281,7 +281,7 @@ class _StatsPageState extends State<StatsPage> {
|
||||||
style: Constants.knownTitleTextStyle,
|
style: Constants.knownTitleTextStyle,
|
||||||
);
|
);
|
||||||
if (settings.useTvLayout) {
|
if (settings.useTvLayout) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final primaryColor = Theme.of(context).colorScheme.primary;
|
||||||
header = Container(
|
header = Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
@ -289,12 +289,11 @@ class _StatsPageState extends State<StatsPage> {
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: InkResponse(
|
child: InkResponse(
|
||||||
onTap: onHeaderPressed,
|
onTap: onHeaderPressed,
|
||||||
onHover: (_) {},
|
containedInkWell: true,
|
||||||
highlightShape: BoxShape.rectangle,
|
highlightShape: BoxShape.rectangle,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(123)),
|
borderRadius: const BorderRadius.all(Radius.circular(123)),
|
||||||
containedInkWell: true,
|
hoverColor: primaryColor.withOpacity(0.04),
|
||||||
splashColor: colors.primary.withOpacity(0.12),
|
splashColor: primaryColor.withOpacity(0.12),
|
||||||
hoverColor: colors.primary.withOpacity(0.04),
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: header,
|
child: header,
|
||||||
|
|
|
@ -67,7 +67,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final targetEntry = EntryActions.pageActions.contains(action) ? pageEntry : mainEntry;
|
final targetEntry = EntryActions.pageActions.contains(action) ? pageEntry : mainEntry;
|
||||||
final canWrite = !device.isReadOnly;
|
final canWrite = !settings.isReadOnly;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case EntryAction.toggleFavourite:
|
case EntryAction.toggleFavourite:
|
||||||
return collection != null;
|
return collection != null;
|
||||||
|
|
|
@ -3,12 +3,12 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:aves/model/actions/entry_actions.dart';
|
import 'package:aves/model/actions/entry_actions.dart';
|
||||||
import 'package:aves/model/actions/events.dart';
|
import 'package:aves/model/actions/events.dart';
|
||||||
import 'package:aves/model/device.dart';
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/entry_info.dart';
|
import 'package:aves/model/entry_info.dart';
|
||||||
import 'package:aves/model/entry_metadata_edition.dart';
|
import 'package:aves/model/entry_metadata_edition.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/geotiff.dart';
|
import 'package:aves/model/geotiff.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
import 'package:aves/ref/mime_types.dart';
|
import 'package:aves/ref/mime_types.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
|
@ -30,7 +30,7 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi
|
||||||
Stream<ActionEvent<EntryAction>> get eventStream => _eventStreamController.stream;
|
Stream<ActionEvent<EntryAction>> get eventStream => _eventStreamController.stream;
|
||||||
|
|
||||||
bool isVisible(AvesEntry targetEntry, EntryAction action) {
|
bool isVisible(AvesEntry targetEntry, EntryAction action) {
|
||||||
final canWrite = !device.isReadOnly;
|
final canWrite = !settings.isReadOnly;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
// general
|
// general
|
||||||
case EntryAction.editDate:
|
case EntryAction.editDate:
|
||||||
|
|
|
@ -135,6 +135,7 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_registerWidget(widget);
|
_registerWidget(widget);
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => _requestFocus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -162,11 +163,10 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
|
||||||
parent: animationController,
|
parent: animationController,
|
||||||
curve: Curves.easeOutQuad,
|
curve: Curves.easeOutQuad,
|
||||||
);
|
);
|
||||||
animationController.addStatusListener(_onAnimationStatusChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _unregisterWidget(_BottomOverlayContent widget) {
|
void _unregisterWidget(_BottomOverlayContent widget) {
|
||||||
widget.animationController.removeStatusListener(_onAnimationStatusChanged);
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -266,11 +266,7 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAnimationStatusChanged(AnimationStatus status) {
|
void _requestFocus() => _buttonRowFocusScopeNode.children.firstOrNull?.requestFocus();
|
||||||
if (status == AnimationStatus.completed) {
|
|
||||||
_buttonRowFocusScopeNode.children.firstOrNull?.requestFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtraBottomOverlay extends StatelessWidget {
|
class ExtraBottomOverlay extends StatelessWidget {
|
||||||
|
|
|
@ -36,6 +36,7 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_registerWidget(widget);
|
_registerWidget(widget);
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => _requestFocus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -53,17 +54,15 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerWidget(SlideshowButtons widget) {
|
void _registerWidget(SlideshowButtons widget) {
|
||||||
final animationController = widget.animationController;
|
|
||||||
_buttonScale = CurvedAnimation(
|
_buttonScale = CurvedAnimation(
|
||||||
parent: animationController,
|
parent: widget.animationController,
|
||||||
// a little bounce at the top
|
// a little bounce at the top
|
||||||
curve: Curves.easeOutBack,
|
curve: Curves.easeOutBack,
|
||||||
);
|
);
|
||||||
animationController.addStatusListener(_onAnimationStatusChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _unregisterWidget(SlideshowButtons widget) {
|
void _unregisterWidget(SlideshowButtons widget) {
|
||||||
widget.animationController.removeStatusListener(_onAnimationStatusChanged);
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -111,9 +110,5 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
||||||
|
|
||||||
void _onAction(BuildContext context, SlideshowAction action) => SlideshowActionNotification(action).dispatch(context);
|
void _onAction(BuildContext context, SlideshowAction action) => SlideshowActionNotification(action).dispatch(context);
|
||||||
|
|
||||||
void _onAnimationStatusChanged(AnimationStatus status) {
|
void _requestFocus() => _buttonRowFocusScopeNode.children.firstOrNull?.requestFocus();
|
||||||
if (status == AnimationStatus.completed) {
|
|
||||||
_buttonRowFocusScopeNode.children.firstOrNull?.requestFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ class AvesMapController {
|
||||||
|
|
||||||
Stream<MapControllerMoveEvent> get moveCommands => _events.where((event) => event is MapControllerMoveEvent).cast<MapControllerMoveEvent>();
|
Stream<MapControllerMoveEvent> get moveCommands => _events.where((event) => event is MapControllerMoveEvent).cast<MapControllerMoveEvent>();
|
||||||
|
|
||||||
|
Stream<MapControllerZoomEvent> get zoomCommands => _events.where((event) => event is MapControllerZoomEvent).cast<MapControllerZoomEvent>();
|
||||||
|
|
||||||
Stream<MapIdleUpdate> get idleUpdates => _events.where((event) => event is MapIdleUpdate).cast<MapIdleUpdate>();
|
Stream<MapIdleUpdate> get idleUpdates => _events.where((event) => event is MapIdleUpdate).cast<MapIdleUpdate>();
|
||||||
|
|
||||||
Stream<MapMarkerLocationChangeEvent> get markerLocationChanges => _events.where((event) => event is MapMarkerLocationChangeEvent).cast<MapMarkerLocationChangeEvent>();
|
Stream<MapMarkerLocationChangeEvent> get markerLocationChanges => _events.where((event) => event is MapMarkerLocationChangeEvent).cast<MapMarkerLocationChangeEvent>();
|
||||||
|
@ -20,6 +22,8 @@ class AvesMapController {
|
||||||
|
|
||||||
void moveTo(LatLng latLng) => _streamController.add(MapControllerMoveEvent(latLng));
|
void moveTo(LatLng latLng) => _streamController.add(MapControllerMoveEvent(latLng));
|
||||||
|
|
||||||
|
void zoomBy(double delta) => _streamController.add(MapControllerZoomEvent(delta));
|
||||||
|
|
||||||
void notifyIdle(ZoomedBounds bounds) => _streamController.add(MapIdleUpdate(bounds));
|
void notifyIdle(ZoomedBounds bounds) => _streamController.add(MapIdleUpdate(bounds));
|
||||||
|
|
||||||
void notifyMarkerLocationChange() => _streamController.add(MapMarkerLocationChangeEvent());
|
void notifyMarkerLocationChange() => _streamController.add(MapMarkerLocationChangeEvent());
|
||||||
|
@ -31,6 +35,12 @@ class MapControllerMoveEvent {
|
||||||
MapControllerMoveEvent(this.latLng);
|
MapControllerMoveEvent(this.latLng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MapControllerZoomEvent {
|
||||||
|
final double delta;
|
||||||
|
|
||||||
|
MapControllerZoomEvent(this.delta);
|
||||||
|
}
|
||||||
|
|
||||||
class MapIdleUpdate {
|
class MapIdleUpdate {
|
||||||
final ZoomedBounds bounds;
|
final ZoomedBounds bounds;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:aves_map/src/marker/key.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
typedef ButtonPanelBuilder = Widget Function(Future<void> Function(double amount) zoomBy, VoidCallback resetRotation);
|
typedef ButtonPanelBuilder = Widget Function(VoidCallback resetRotation);
|
||||||
typedef MarkerClusterBuilder<T> = Map<MarkerKey<T>, GeoEntry<T>> Function();
|
typedef MarkerClusterBuilder<T> = Map<MarkerKey<T>, GeoEntry<T>> Function();
|
||||||
typedef MarkerWidgetBuilder<T> = Widget Function(MarkerKey<T> key);
|
typedef MarkerWidgetBuilder<T> = Widget Function(MarkerKey<T> key);
|
||||||
typedef MarkerImageReadyChecker<T> = bool Function(MarkerKey<T> key);
|
typedef MarkerImageReadyChecker<T> = bool Function(MarkerKey<T> key);
|
||||||
|
|
|
@ -95,6 +95,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
|
||||||
final avesMapController = widget.controller;
|
final avesMapController = widget.controller;
|
||||||
if (avesMapController != null) {
|
if (avesMapController != null) {
|
||||||
_subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(_toServiceLatLng(event.latLng))));
|
_subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(_toServiceLatLng(event.latLng))));
|
||||||
|
_subscriptions.add(avesMapController.zoomCommands.listen((event) => _zoomBy(event.delta)));
|
||||||
}
|
}
|
||||||
widget.clusterListenable.addListener(_updateMarkers);
|
widget.clusterListenable.addListener(_updateMarkers);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +140,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
widget.decoratorBuilder(context, _buildMap()),
|
widget.decoratorBuilder(context, _buildMap()),
|
||||||
widget.buttonPanelBuilder(_zoomBy, _resetRotation),
|
widget.buttonPanelBuilder(_resetRotation),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ class _EntryHmsMapState<T> extends State<EntryHmsMap<T>> {
|
||||||
final avesMapController = widget.controller;
|
final avesMapController = widget.controller;
|
||||||
if (avesMapController != null) {
|
if (avesMapController != null) {
|
||||||
_subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(_toServiceLatLng(event.latLng))));
|
_subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(_toServiceLatLng(event.latLng))));
|
||||||
|
_subscriptions.add(avesMapController.zoomCommands.listen((event) => _zoomBy(event.delta)));
|
||||||
}
|
}
|
||||||
widget.clusterListenable.addListener(_updateMarkers);
|
widget.clusterListenable.addListener(_updateMarkers);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +119,7 @@ class _EntryHmsMapState<T> extends State<EntryHmsMap<T>> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
widget.decoratorBuilder(context, _buildMap()),
|
widget.decoratorBuilder(context, _buildMap()),
|
||||||
widget.buttonPanelBuilder(_zoomBy, _resetRotation),
|
widget.buttonPanelBuilder(_resetRotation),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue