mirror of
https://github.com/immich-app/immich.git
synced 2026-02-28 17:49:05 +03:00
The existing implementation for showing asset details uses a bottom sheet, and is not in sync with the preview or scroll intent. Other apps use inline details, which is much cleaner and feels better to use.
120 lines
4.3 KiB
Dart
120 lines
4.3 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
import 'package:immich_mobile/extensions/maplibrecontroller_extensions.dart';
|
|
import 'package:immich_mobile/widgets/map/map_theme_override.dart';
|
|
import 'package:immich_mobile/widgets/map/asset_market_icon.dart';
|
|
import 'package:maplibre_gl/maplibre_gl.dart';
|
|
|
|
/// A non-interactive thumbnail of a map in the given coordinates with optional markers
|
|
///
|
|
/// User can provide either a [assetMarkerRemoteId] to display the asset's thumbnail or set
|
|
/// [showMarkerPin] to true which would display a marker pin instead. If both are provided,
|
|
/// [assetMarkerRemoteId] will take precedence
|
|
class MapThumbnail extends HookConsumerWidget {
|
|
final Function(Point<double>, LatLng)? onTap;
|
|
final LatLng centre;
|
|
final String? assetMarkerRemoteId;
|
|
final String? assetThumbhash;
|
|
final bool showMarkerPin;
|
|
final double zoom;
|
|
final double height;
|
|
final double width;
|
|
final ThemeMode? themeMode;
|
|
final bool showAttribution;
|
|
final MapCreatedCallback? onCreated;
|
|
|
|
const MapThumbnail({
|
|
super.key,
|
|
required this.centre,
|
|
this.height = 100,
|
|
this.width = 100,
|
|
this.onTap,
|
|
this.zoom = 8,
|
|
this.assetMarkerRemoteId,
|
|
this.assetThumbhash,
|
|
this.showMarkerPin = false,
|
|
this.themeMode,
|
|
this.showAttribution = true,
|
|
this.onCreated,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final controller = useRef<MapLibreMapController?>(null);
|
|
final styleLoaded = useState(false);
|
|
|
|
Future<void> onMapCreated(MapLibreMapController mapController) async {
|
|
controller.value = mapController;
|
|
styleLoaded.value = false;
|
|
onCreated?.call(mapController);
|
|
}
|
|
|
|
Future<void> onStyleLoaded() async {
|
|
try {
|
|
if (showMarkerPin && controller.value != null) {
|
|
await controller.value?.addMarkerAtLatLng(centre);
|
|
}
|
|
} finally {
|
|
// Calling methods on the controller after it is disposed will throw an error
|
|
// We do not have a way to check if the controller is disposed for now
|
|
// https://github.com/maplibre/flutter-maplibre-gl/issues/192
|
|
}
|
|
styleLoaded.value = true;
|
|
}
|
|
|
|
return MapThemeOverride(
|
|
themeMode: themeMode,
|
|
mapBuilder: (style) => AnimatedContainer(
|
|
duration: Durations.medium2,
|
|
curve: Curves.easeOut,
|
|
foregroundDecoration: BoxDecoration(
|
|
color: context.colorScheme.inverseSurface.withAlpha(styleLoaded.value ? 0 : 200),
|
|
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
|
),
|
|
height: height,
|
|
width: width,
|
|
child: ClipRRect(
|
|
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
|
child: Stack(
|
|
alignment: AlignmentGeometry.topCenter,
|
|
children: [
|
|
style.widgetWhen(
|
|
onData: (style) => MapLibreMap(
|
|
initialCameraPosition: CameraPosition(target: centre, zoom: zoom),
|
|
styleString: style,
|
|
onMapCreated: onMapCreated,
|
|
onStyleLoadedCallback: onStyleLoaded,
|
|
onMapClick: onTap,
|
|
doubleClickZoomEnabled: false,
|
|
dragEnabled: false,
|
|
zoomGesturesEnabled: false,
|
|
tiltGesturesEnabled: false,
|
|
scrollGesturesEnabled: false,
|
|
rotateGesturesEnabled: false,
|
|
myLocationEnabled: false,
|
|
attributionButtonMargins: showAttribution == false ? const Point(-100, 0) : null,
|
|
),
|
|
),
|
|
if (assetMarkerRemoteId != null && assetThumbhash != null)
|
|
Container(
|
|
width: width,
|
|
height: height / 2,
|
|
alignment: Alignment.bottomCenter,
|
|
child: SizedBox.square(
|
|
dimension: height / 2.5,
|
|
child: AssetMarkerIcon(id: assetMarkerRemoteId!, thumbhash: assetThumbhash!),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|