mirror of
https://github.com/immich-app/immich.git
synced 2024-12-24 10:37:28 +02:00
98 lines
3.3 KiB
Dart
98 lines
3.3 KiB
Dart
|
import 'package:flutter/material.dart';
|
||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||
|
import 'package:immich_mobile/modules/map/models/map_event.model.dart';
|
||
|
import 'package:immich_mobile/modules/map/widgets/map_asset_grid.dart';
|
||
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||
|
import 'package:immich_mobile/utils/draggable_scroll_controller.dart';
|
||
|
|
||
|
class MapBottomSheet extends HookConsumerWidget {
|
||
|
final Stream<MapEvent> mapEventStream;
|
||
|
final Function(String)? onGridAssetChanged;
|
||
|
final Function(String)? onZoomToAsset;
|
||
|
final Function()? onZoomToLocation;
|
||
|
final Function(bool, Set<Asset>)? onAssetsSelected;
|
||
|
final ValueNotifier<Set<Asset>> selectedAssets;
|
||
|
|
||
|
const MapBottomSheet({
|
||
|
required this.mapEventStream,
|
||
|
this.onGridAssetChanged,
|
||
|
this.onZoomToAsset,
|
||
|
this.onAssetsSelected,
|
||
|
this.onZoomToLocation,
|
||
|
required this.selectedAssets,
|
||
|
super.key,
|
||
|
});
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||
|
const sheetMinExtent = 0.1;
|
||
|
final sheetController = useDraggableScrollController();
|
||
|
final bottomSheetOffset = useValueNotifier(sheetMinExtent);
|
||
|
final isBottomSheetOpened = useRef(false);
|
||
|
|
||
|
void handleMapEvents(MapEvent event) async {
|
||
|
if (event is MapCloseBottomSheet) {
|
||
|
sheetController.animateTo(
|
||
|
0.1,
|
||
|
duration: const Duration(milliseconds: 200),
|
||
|
curve: Curves.linearToEaseOut,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
useOnStreamChange<MapEvent>(mapEventStream, onData: handleMapEvents);
|
||
|
|
||
|
bool onScrollNotification(DraggableScrollableNotification notification) {
|
||
|
isBottomSheetOpened.value =
|
||
|
notification.extent > (notification.maxExtent * 0.9);
|
||
|
bottomSheetOffset.value = notification.extent;
|
||
|
// do not bubble
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return Stack(
|
||
|
children: [
|
||
|
NotificationListener<DraggableScrollableNotification>(
|
||
|
onNotification: onScrollNotification,
|
||
|
child: DraggableScrollableSheet(
|
||
|
controller: sheetController,
|
||
|
minChildSize: sheetMinExtent,
|
||
|
maxChildSize: 0.5,
|
||
|
initialChildSize: sheetMinExtent,
|
||
|
snap: true,
|
||
|
shouldCloseOnMinExtent: false,
|
||
|
builder: (ctx, scrollController) => MapAssetGrid(
|
||
|
controller: scrollController,
|
||
|
mapEventStream: mapEventStream,
|
||
|
selectedAssets: selectedAssets,
|
||
|
onAssetsSelected: onAssetsSelected,
|
||
|
// Do not bother with the event if the bottom sheet is not user scrolled
|
||
|
onGridAssetChanged: (assetId) => isBottomSheetOpened.value
|
||
|
? onGridAssetChanged?.call(assetId)
|
||
|
: null,
|
||
|
onZoomToAsset: onZoomToAsset,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
ValueListenableBuilder(
|
||
|
valueListenable: bottomSheetOffset,
|
||
|
builder: (ctx, value, child) => Positioned(
|
||
|
right: 0,
|
||
|
bottom: context.height * (value + 0.02),
|
||
|
child: child!,
|
||
|
),
|
||
|
child: ElevatedButton(
|
||
|
onPressed: onZoomToLocation,
|
||
|
style: ElevatedButton.styleFrom(
|
||
|
shape: const CircleBorder(),
|
||
|
),
|
||
|
child: const Icon(Icons.my_location),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
}
|