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 mapEventStream; final Function(String)? onGridAssetChanged; final Function(String)? onZoomToAsset; final Function()? onZoomToLocation; final Function(bool, Set)? onAssetsSelected; final ValueNotifier> 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(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( 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), ), ), ], ); } }