map: theme tweaks, info -> map
This commit is contained in:
parent
d10fa5dc5e
commit
a7f4d89dc7
10 changed files with 174 additions and 107 deletions
|
@ -869,6 +869,8 @@
|
||||||
"@mapAttributionOsmHot": {},
|
"@mapAttributionOsmHot": {},
|
||||||
"mapAttributionStamen": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)",
|
"mapAttributionStamen": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors • Tiles by [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)",
|
||||||
"@mapAttributionStamen": {},
|
"@mapAttributionStamen": {},
|
||||||
|
"openMapTooltip": "View on Map page",
|
||||||
|
"@openMapTooltip": {},
|
||||||
|
|
||||||
"viewerInfoOpenEmbeddedFailureFeedback": "Failed to extract embedded data",
|
"viewerInfoOpenEmbeddedFailureFeedback": "Failed to extract embedded data",
|
||||||
"@viewerInfoOpenEmbeddedFailureFeedback": {},
|
"@viewerInfoOpenEmbeddedFailureFeedback": {},
|
||||||
|
|
|
@ -424,6 +424,7 @@
|
||||||
"mapPointNorthUpTooltip": "북쪽을 위로 가리키기",
|
"mapPointNorthUpTooltip": "북쪽을 위로 가리키기",
|
||||||
"mapAttributionOsmHot": "지도 데이터 © [OpenStreetMap](https://www.openstreetmap.org/copyright) 기여자 • 타일 [HOT](https://www.hotosm.org/) • 호스팅 [OSM France](https://openstreetmap.fr/)",
|
"mapAttributionOsmHot": "지도 데이터 © [OpenStreetMap](https://www.openstreetmap.org/copyright) 기여자 • 타일 [HOT](https://www.hotosm.org/) • 호스팅 [OSM France](https://openstreetmap.fr/)",
|
||||||
"mapAttributionStamen": "지도 데이터 © [OpenStreetMap](https://www.openstreetmap.org/copyright) 기여자 • 타일 [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)",
|
"mapAttributionStamen": "지도 데이터 © [OpenStreetMap](https://www.openstreetmap.org/copyright) 기여자 • 타일 [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)",
|
||||||
|
"openMapTooltip": "지도 페이지에서 보기",
|
||||||
|
|
||||||
"viewerInfoOpenEmbeddedFailureFeedback": "첨부 데이터 추출 오류",
|
"viewerInfoOpenEmbeddedFailureFeedback": "첨부 데이터 추출 오류",
|
||||||
"viewerInfoOpenLinkText": "열기",
|
"viewerInfoOpenLinkText": "열기",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:aves/model/settings/enums.dart';
|
import 'package:aves/model/settings/enums.dart';
|
||||||
import 'package:aves/model/settings/map_style.dart';
|
import 'package:aves/model/settings/map_style.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/services/android_app_service.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
import 'package:aves/services/common/services.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';
|
||||||
|
@ -9,16 +9,17 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||||
import 'package:aves/widgets/common/fx/borders.dart';
|
import 'package:aves/widgets/common/fx/borders.dart';
|
||||||
import 'package:aves/widgets/common/map/compass.dart';
|
import 'package:aves/widgets/common/map/compass.dart';
|
||||||
|
import 'package:aves/widgets/common/map/theme.dart';
|
||||||
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
||||||
|
import 'package:aves/widgets/map/map_page.dart';
|
||||||
import 'package:aves/widgets/viewer/overlay/common.dart';
|
import 'package:aves/widgets/viewer/overlay/common.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class MapButtonPanel extends StatelessWidget {
|
class MapButtonPanel extends StatelessWidget {
|
||||||
final bool showBackButton;
|
|
||||||
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
||||||
final Future<void> Function(double amount)? zoomBy;
|
final Future<void> Function(double amount)? zoomBy;
|
||||||
final VoidCallback? resetRotation;
|
final VoidCallback? resetRotation;
|
||||||
|
@ -27,7 +28,6 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
|
|
||||||
const MapButtonPanel({
|
const MapButtonPanel({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.showBackButton,
|
|
||||||
required this.boundsNotifier,
|
required this.boundsNotifier,
|
||||||
this.zoomBy,
|
this.zoomBy,
|
||||||
this.resetRotation,
|
this.resetRotation,
|
||||||
|
@ -37,6 +37,28 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final iconTheme = IconTheme.of(context);
|
final iconTheme = IconTheme.of(context);
|
||||||
final iconSize = Size.square(iconTheme.size!);
|
final iconSize = Size.square(iconTheme.size!);
|
||||||
|
|
||||||
|
Widget? navigationButton;
|
||||||
|
switch (context.select<MapThemeData, MapNavigationButton>((v) => v.navigationButton)) {
|
||||||
|
case MapNavigationButton.back:
|
||||||
|
navigationButton = MapOverlayButton(
|
||||||
|
icon: const BackButtonIcon(),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MapNavigationButton.map:
|
||||||
|
final collection = context.read<CollectionLens?>();
|
||||||
|
if (collection != null) {
|
||||||
|
navigationButton = MapOverlayButton(
|
||||||
|
icon: const Icon(AIcons.map),
|
||||||
|
onPressed: () => _goToMap(context, collection),
|
||||||
|
tooltip: context.l10n.openMapTooltip,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return Positioned.fill(
|
return Positioned.fill(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
|
@ -53,43 +75,38 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (showBackButton)
|
if (navigationButton != null) ...[
|
||||||
MapOverlayButton(
|
navigationButton,
|
||||||
icon: const BackButtonIcon(),
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
|
||||||
),
|
|
||||||
if (resetRotation != null) ...[
|
|
||||||
const SizedBox(height: padding),
|
const SizedBox(height: padding),
|
||||||
ValueListenableBuilder<ZoomedBounds>(
|
|
||||||
valueListenable: boundsNotifier,
|
|
||||||
builder: (context, bounds, child) {
|
|
||||||
final degrees = bounds.rotation;
|
|
||||||
final opacity = degrees == 0 ? .0 : 1.0;
|
|
||||||
return IgnorePointer(
|
|
||||||
ignoring: opacity == 0,
|
|
||||||
child: AnimatedOpacity(
|
|
||||||
opacity: opacity,
|
|
||||||
duration: Durations.viewerOverlayAnimation,
|
|
||||||
child: MapOverlayButton(
|
|
||||||
icon: Transform(
|
|
||||||
origin: iconSize.center(Offset.zero),
|
|
||||||
transform: Matrix4.rotationZ(degToRadian(degrees)),
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: CompassPainter(
|
|
||||||
color: iconTheme.color!,
|
|
||||||
),
|
|
||||||
size: iconSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () => resetRotation?.call(),
|
|
||||||
tooltip: context.l10n.mapPointNorthUpTooltip,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
|
ValueListenableBuilder<ZoomedBounds>(
|
||||||
|
valueListenable: boundsNotifier,
|
||||||
|
builder: (context, bounds, child) {
|
||||||
|
final degrees = bounds.rotation;
|
||||||
|
final opacity = degrees == 0 ? .0 : 1.0;
|
||||||
|
return IgnorePointer(
|
||||||
|
ignoring: opacity == 0,
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: opacity,
|
||||||
|
duration: Durations.viewerOverlayAnimation,
|
||||||
|
child: MapOverlayButton(
|
||||||
|
icon: Transform(
|
||||||
|
origin: iconSize.center(Offset.zero),
|
||||||
|
transform: Matrix4.rotationZ(degToRadian(degrees)),
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: CompassPainter(
|
||||||
|
color: iconTheme.color!,
|
||||||
|
),
|
||||||
|
size: iconSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () => resetRotation?.call(),
|
||||||
|
tooltip: context.l10n.mapPointNorthUpTooltip,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -98,14 +115,6 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
MapOverlayButton(
|
|
||||||
icon: const Icon(AIcons.openOutside),
|
|
||||||
onPressed: () => AndroidAppService.openMap(boundsNotifier.value.center).then((success) {
|
|
||||||
if (!success) showNoMatchingAppDialog(context);
|
|
||||||
}),
|
|
||||||
tooltip: context.l10n.entryActionOpenMap,
|
|
||||||
),
|
|
||||||
const SizedBox(height: padding),
|
|
||||||
MapOverlayButton(
|
MapOverlayButton(
|
||||||
icon: const Icon(AIcons.layers),
|
icon: const Icon(AIcons.layers),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -161,6 +170,20 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _goToMap(BuildContext context, CollectionLens collection) {
|
||||||
|
final entries = collection.sortedEntries.where((entry) => entry.hasGps).toList();
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
settings: const RouteSettings(name: MapPage.routeName),
|
||||||
|
builder: (context) => MapPage(
|
||||||
|
entries: entries,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapOverlayButton extends StatelessWidget {
|
class MapOverlayButton extends StatelessWidget {
|
||||||
|
@ -177,6 +200,7 @@ class MapOverlayButton extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final visualDensity = context.select<MapThemeData, VisualDensity?>((v) => v.visualDensity);
|
||||||
final blurred = settings.enableOverlayBlurEffect;
|
final blurred = settings.enableOverlayBlurEffect;
|
||||||
return BlurredOval(
|
return BlurredOval(
|
||||||
enabled: blurred,
|
enabled: blurred,
|
||||||
|
@ -190,7 +214,7 @@ class MapOverlayButton extends StatelessWidget {
|
||||||
),
|
),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: visualDensity,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
tooltip: tooltip,
|
tooltip: tooltip,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import 'package:aves/widgets/common/map/theme.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class MapDecorator extends StatelessWidget {
|
class MapDecorator extends StatelessWidget {
|
||||||
final bool interactive;
|
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
|
|
||||||
static const mapBorderRadius = BorderRadius.all(Radius.circular(24)); // to match button circles
|
static const mapBorderRadius = BorderRadius.all(Radius.circular(24)); // to match button circles
|
||||||
|
@ -10,12 +11,12 @@ class MapDecorator extends StatelessWidget {
|
||||||
|
|
||||||
const MapDecorator({
|
const MapDecorator({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.interactive,
|
|
||||||
this.child,
|
this.child,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onScaleStart: interactive
|
onScaleStart: interactive
|
||||||
? null
|
? null
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:aves/widgets/common/map/geo_entry.dart';
|
||||||
import 'package:aves/widgets/common/map/google/map.dart';
|
import 'package:aves/widgets/common/map/google/map.dart';
|
||||||
import 'package:aves/widgets/common/map/leaflet/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/marker.dart';
|
||||||
|
import 'package:aves/widgets/common/map/theme.dart';
|
||||||
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
@ -27,8 +28,6 @@ import 'package:provider/provider.dart';
|
||||||
class GeoMap extends StatefulWidget {
|
class GeoMap extends StatefulWidget {
|
||||||
final AvesMapController? controller;
|
final AvesMapController? controller;
|
||||||
final List<AvesEntry> entries;
|
final List<AvesEntry> entries;
|
||||||
final bool interactive, showBackButton;
|
|
||||||
final double? mapHeight;
|
|
||||||
final ValueNotifier<bool> isAnimatingNotifier;
|
final ValueNotifier<bool> isAnimatingNotifier;
|
||||||
final UserZoomChangeCallback? onUserZoomChange;
|
final UserZoomChangeCallback? onUserZoomChange;
|
||||||
final MarkerTapCallback? onMarkerTap;
|
final MarkerTapCallback? onMarkerTap;
|
||||||
|
@ -40,9 +39,6 @@ class GeoMap extends StatefulWidget {
|
||||||
Key? key,
|
Key? key,
|
||||||
this.controller,
|
this.controller,
|
||||||
required this.entries,
|
required this.entries,
|
||||||
required this.interactive,
|
|
||||||
required this.showBackButton,
|
|
||||||
this.mapHeight,
|
|
||||||
required this.isAnimatingNotifier,
|
required this.isAnimatingNotifier,
|
||||||
this.onUserZoomChange,
|
this.onUserZoomChange,
|
||||||
this.onMarkerTap,
|
this.onMarkerTap,
|
||||||
|
@ -64,12 +60,6 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
|
|
||||||
List<AvesEntry> get entries => widget.entries;
|
List<AvesEntry> get entries => widget.entries;
|
||||||
|
|
||||||
bool get interactive => widget.interactive;
|
|
||||||
|
|
||||||
bool get showBackButton => widget.showBackButton;
|
|
||||||
|
|
||||||
double? get mapHeight => widget.mapHeight;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -139,8 +129,6 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
? EntryGoogleMap(
|
? EntryGoogleMap(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
boundsNotifier: _boundsNotifier,
|
boundsNotifier: _boundsNotifier,
|
||||||
interactive: interactive,
|
|
||||||
showBackButton: showBackButton,
|
|
||||||
minZoom: 0,
|
minZoom: 0,
|
||||||
maxZoom: 20,
|
maxZoom: 20,
|
||||||
style: mapStyle,
|
style: mapStyle,
|
||||||
|
@ -152,8 +140,6 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
: EntryLeafletMap(
|
: EntryLeafletMap(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
boundsNotifier: _boundsNotifier,
|
boundsNotifier: _boundsNotifier,
|
||||||
interactive: interactive,
|
|
||||||
showBackButton: showBackButton,
|
|
||||||
minZoom: 2,
|
minZoom: 2,
|
||||||
maxZoom: 16,
|
maxZoom: 16,
|
||||||
style: mapStyle,
|
style: mapStyle,
|
||||||
|
@ -167,6 +153,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
onMarkerTap: _onMarkerTap,
|
onMarkerTap: _onMarkerTap,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final mapHeight = context.select<MapThemeData, double?>((v) => v.mapHeight);
|
||||||
child = Column(
|
child = Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -192,11 +179,8 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
}
|
}
|
||||||
Widget replacement = Stack(
|
Widget replacement = Stack(
|
||||||
children: [
|
children: [
|
||||||
MapDecorator(
|
const MapDecorator(),
|
||||||
interactive: interactive,
|
|
||||||
),
|
|
||||||
MapButtonPanel(
|
MapButtonPanel(
|
||||||
showBackButton: showBackButton,
|
|
||||||
boundsNotifier: _boundsNotifier,
|
boundsNotifier: _boundsNotifier,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -10,15 +10,16 @@ import 'package:aves/widgets/common/map/decorator.dart';
|
||||||
import 'package:aves/widgets/common/map/geo_entry.dart';
|
import 'package:aves/widgets/common/map/geo_entry.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/google/marker_generator.dart';
|
import 'package:aves/widgets/common/map/google/marker_generator.dart';
|
||||||
|
import 'package:aves/widgets/common/map/theme.dart';
|
||||||
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
import 'package:latlong2/latlong.dart' as ll;
|
import 'package:latlong2/latlong.dart' as ll;
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class EntryGoogleMap extends StatefulWidget {
|
class EntryGoogleMap extends StatefulWidget {
|
||||||
final AvesMapController? controller;
|
final AvesMapController? controller;
|
||||||
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
||||||
final bool interactive, showBackButton;
|
|
||||||
final double? minZoom, maxZoom;
|
final double? minZoom, maxZoom;
|
||||||
final EntryMapStyle style;
|
final EntryMapStyle style;
|
||||||
final MarkerClusterBuilder markerClusterBuilder;
|
final MarkerClusterBuilder markerClusterBuilder;
|
||||||
|
@ -30,8 +31,6 @@ class EntryGoogleMap extends StatefulWidget {
|
||||||
Key? key,
|
Key? key,
|
||||||
this.controller,
|
this.controller,
|
||||||
required this.boundsNotifier,
|
required this.boundsNotifier,
|
||||||
required this.interactive,
|
|
||||||
required this.showBackButton,
|
|
||||||
this.minZoom,
|
this.minZoom,
|
||||||
this.maxZoom,
|
this.maxZoom,
|
||||||
required this.style,
|
required this.style,
|
||||||
|
@ -56,8 +55,6 @@ class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObse
|
||||||
|
|
||||||
ZoomedBounds get bounds => boundsNotifier.value;
|
ZoomedBounds get bounds => boundsNotifier.value;
|
||||||
|
|
||||||
bool get interactive => widget.interactive;
|
|
||||||
|
|
||||||
static const uninitializedLatLng = LatLng(0, 0);
|
static const uninitializedLatLng = LatLng(0, 0);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -123,14 +120,12 @@ class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObse
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MapDecorator(
|
MapDecorator(
|
||||||
interactive: interactive,
|
|
||||||
child: _buildMap(),
|
child: _buildMap(),
|
||||||
),
|
),
|
||||||
MapButtonPanel(
|
MapButtonPanel(
|
||||||
showBackButton: widget.showBackButton,
|
|
||||||
boundsNotifier: boundsNotifier,
|
boundsNotifier: boundsNotifier,
|
||||||
zoomBy: _zoomBy,
|
zoomBy: _zoomBy,
|
||||||
resetRotation: interactive ? _resetRotation : null,
|
resetRotation: _resetRotation,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -155,7 +150,7 @@ class _EntryGoogleMapState extends State<EntryGoogleMap> with WidgetsBindingObse
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final interactive = widget.interactive;
|
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
||||||
return GoogleMap(
|
return GoogleMap(
|
||||||
initialCameraPosition: CameraPosition(
|
initialCameraPosition: CameraPosition(
|
||||||
target: _toGoogleLatLng(bounds.center),
|
target: _toGoogleLatLng(bounds.center),
|
||||||
|
|
|
@ -11,15 +11,16 @@ import 'package:aves/widgets/common/map/geo_map.dart';
|
||||||
import 'package:aves/widgets/common/map/latlng_tween.dart';
|
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/scale_layer.dart';
|
||||||
import 'package:aves/widgets/common/map/leaflet/tile_layers.dart';
|
import 'package:aves/widgets/common/map/leaflet/tile_layers.dart';
|
||||||
|
import 'package:aves/widgets/common/map/theme.dart';
|
||||||
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
import 'package:aves/widgets/common/map/zoomed_bounds.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class EntryLeafletMap extends StatefulWidget {
|
class EntryLeafletMap extends StatefulWidget {
|
||||||
final AvesMapController? controller;
|
final AvesMapController? controller;
|
||||||
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
final ValueNotifier<ZoomedBounds> boundsNotifier;
|
||||||
final bool interactive, showBackButton;
|
|
||||||
final double minZoom, maxZoom;
|
final double minZoom, maxZoom;
|
||||||
final EntryMapStyle style;
|
final EntryMapStyle style;
|
||||||
final MarkerClusterBuilder markerClusterBuilder;
|
final MarkerClusterBuilder markerClusterBuilder;
|
||||||
|
@ -32,8 +33,6 @@ class EntryLeafletMap extends StatefulWidget {
|
||||||
Key? key,
|
Key? key,
|
||||||
this.controller,
|
this.controller,
|
||||||
required this.boundsNotifier,
|
required this.boundsNotifier,
|
||||||
required this.interactive,
|
|
||||||
required this.showBackButton,
|
|
||||||
this.minZoom = 0,
|
this.minZoom = 0,
|
||||||
this.maxZoom = 22,
|
this.maxZoom = 22,
|
||||||
required this.style,
|
required this.style,
|
||||||
|
@ -58,8 +57,6 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
|
||||||
|
|
||||||
ZoomedBounds get bounds => boundsNotifier.value;
|
ZoomedBounds get bounds => boundsNotifier.value;
|
||||||
|
|
||||||
bool get interactive => widget.interactive;
|
|
||||||
|
|
||||||
// duration should match the uncustomizable Google Maps duration
|
// duration should match the uncustomizable Google Maps duration
|
||||||
static const _cameraAnimationDuration = Duration(milliseconds: 600);
|
static const _cameraAnimationDuration = Duration(milliseconds: 600);
|
||||||
|
|
||||||
|
@ -104,14 +101,12 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
MapDecorator(
|
MapDecorator(
|
||||||
interactive: interactive,
|
|
||||||
child: _buildMap(),
|
child: _buildMap(),
|
||||||
),
|
),
|
||||||
MapButtonPanel(
|
MapButtonPanel(
|
||||||
showBackButton: widget.showBackButton,
|
|
||||||
boundsNotifier: boundsNotifier,
|
boundsNotifier: boundsNotifier,
|
||||||
zoomBy: _zoomBy,
|
zoomBy: _zoomBy,
|
||||||
resetRotation: interactive ? _resetRotation : null,
|
resetRotation: _resetRotation,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -135,13 +130,14 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
|
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
||||||
return FlutterMap(
|
return FlutterMap(
|
||||||
options: MapOptions(
|
options: MapOptions(
|
||||||
center: bounds.center,
|
center: bounds.center,
|
||||||
zoom: bounds.zoom,
|
zoom: bounds.zoom,
|
||||||
minZoom: widget.minZoom,
|
minZoom: widget.minZoom,
|
||||||
maxZoom: widget.maxZoom,
|
maxZoom: widget.maxZoom,
|
||||||
interactiveFlags: widget.interactive ? InteractiveFlag.all : InteractiveFlag.none,
|
interactiveFlags: interactive ? InteractiveFlag.all : InteractiveFlag.none,
|
||||||
controller: _leafletMapController,
|
controller: _leafletMapController,
|
||||||
),
|
),
|
||||||
mapController: _leafletMapController,
|
mapController: _leafletMapController,
|
||||||
|
|
53
lib/widgets/common/map/theme.dart
Normal file
53
lib/widgets/common/map/theme.dart
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
enum MapNavigationButton { back, map }
|
||||||
|
|
||||||
|
class MapTheme extends StatelessWidget {
|
||||||
|
final bool interactive;
|
||||||
|
final MapNavigationButton navigationButton;
|
||||||
|
final VisualDensity? visualDensity;
|
||||||
|
final double? mapHeight;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const MapTheme({
|
||||||
|
Key? key,
|
||||||
|
required this.interactive,
|
||||||
|
required this.navigationButton,
|
||||||
|
this.visualDensity,
|
||||||
|
this.mapHeight,
|
||||||
|
required this.child,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProxyProvider<Settings, MapThemeData>(
|
||||||
|
update: (_, settings, __) {
|
||||||
|
return MapThemeData(
|
||||||
|
interactive: interactive,
|
||||||
|
navigationButton: navigationButton,
|
||||||
|
visualDensity: visualDensity,
|
||||||
|
mapHeight: mapHeight,
|
||||||
|
// TODO TLAD use settings?
|
||||||
|
// showLocation: showBackButton ?? settings.showThumbnailLocation,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapThemeData {
|
||||||
|
final bool interactive;
|
||||||
|
final MapNavigationButton navigationButton;
|
||||||
|
final VisualDensity? visualDensity;
|
||||||
|
final double? mapHeight;
|
||||||
|
|
||||||
|
const MapThemeData({
|
||||||
|
required this.interactive,
|
||||||
|
required this.navigationButton,
|
||||||
|
this.visualDensity,
|
||||||
|
this.mapHeight,
|
||||||
|
});
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/utils/debouncer.dart';
|
import 'package:aves/utils/debouncer.dart';
|
||||||
import 'package:aves/widgets/common/map/controller.dart';
|
import 'package:aves/widgets/common/map/controller.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/theme.dart';
|
||||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||||
import 'package:aves/widgets/common/thumbnail/scroller.dart';
|
import 'package:aves/widgets/common/thumbnail/scroller.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -65,20 +66,22 @@ class _MapPageState extends State<MapPage> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GeoMap(
|
child: MapTheme(
|
||||||
controller: _mapController,
|
|
||||||
entries: entries,
|
|
||||||
interactive: true,
|
interactive: true,
|
||||||
showBackButton: true,
|
navigationButton: MapNavigationButton.back,
|
||||||
isAnimatingNotifier: _isAnimatingNotifier,
|
child: GeoMap(
|
||||||
onMarkerTap: (markerEntry, getClusterEntries) {
|
controller: _mapController,
|
||||||
final index = entries.indexOf(markerEntry);
|
entries: entries,
|
||||||
if (_selectedIndexNotifier.value != index) {
|
isAnimatingNotifier: _isAnimatingNotifier,
|
||||||
_selectedIndexNotifier.value = index;
|
onMarkerTap: (markerEntry, getClusterEntries) {
|
||||||
} else {
|
final index = entries.indexOf(markerEntry);
|
||||||
_moveToEntry(markerEntry);
|
if (_selectedIndexNotifier.value != index) {
|
||||||
}
|
_selectedIndexNotifier.value = index;
|
||||||
},
|
} else {
|
||||||
|
_moveToEntry(markerEntry);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
|
|
@ -8,8 +8,10 @@ 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/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.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/theme.dart';
|
||||||
import 'package:aves/widgets/viewer/info/common.dart';
|
import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class LocationSection extends StatefulWidget {
|
class LocationSection extends StatefulWidget {
|
||||||
final CollectionLens? collection;
|
final CollectionLens? collection;
|
||||||
|
@ -82,13 +84,19 @@ class _LocationSectionState extends State<LocationSection> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (widget.showTitle) const SectionRow(icon: AIcons.location),
|
if (widget.showTitle) const SectionRow(icon: AIcons.location),
|
||||||
GeoMap(
|
ChangeNotifierProvider<CollectionLens?>.value(
|
||||||
entries: [entry],
|
value: collection,
|
||||||
interactive: false,
|
child: MapTheme(
|
||||||
showBackButton: false,
|
interactive: false,
|
||||||
mapHeight: 200,
|
navigationButton: MapNavigationButton.map,
|
||||||
isAnimatingNotifier: widget.isScrollingNotifier,
|
visualDensity: VisualDensity.compact,
|
||||||
onUserZoomChange: (zoom) => settings.infoMapZoom = zoom,
|
mapHeight: 200,
|
||||||
|
child: GeoMap(
|
||||||
|
entries: [entry],
|
||||||
|
isAnimatingNotifier: widget.isScrollingNotifier,
|
||||||
|
onUserZoomChange: (zoom) => settings.infoMapZoom = zoom,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
_AddressInfoGroup(entry: entry),
|
_AddressInfoGroup(entry: entry),
|
||||||
if (filters.isNotEmpty)
|
if (filters.isNotEmpty)
|
||||||
|
|
Loading…
Reference in a new issue