From 0c36eb1f8bc1df34b986d3669be1538a3560e742 Mon Sep 17 00:00:00 2001 From: Marty Fuhry Date: Wed, 31 Jan 2024 09:51:30 -0500 Subject: [PATCH] Uses linear bar and fits the card better for memories --- .../lib/modules/memories/ui/memory_card.dart | 57 ++++++------- .../modules/memories/views/memory_page.dart | 85 ++++++++++++++----- 2 files changed, 89 insertions(+), 53 deletions(-) diff --git a/mobile/lib/modules/memories/ui/memory_card.dart b/mobile/lib/modules/memories/ui/memory_card.dart index d9ccaed39f..ebb9b47e73 100644 --- a/mobile/lib/modules/memories/ui/memory_card.dart +++ b/mobile/lib/modules/memories/ui/memory_card.dart @@ -12,18 +12,14 @@ import 'package:openapi/api.dart'; class MemoryCard extends StatelessWidget { final Asset asset; final void Function() onTap; - final void Function() onClose; final String title; - final String? rightCornerText; final bool showTitle; const MemoryCard({ required this.asset, required this.onTap, - required this.onClose, required this.title, required this.showTitle, - this.rightCornerText, super.key, }); @@ -65,34 +61,31 @@ class MemoryCard extends StatelessWidget { ), GestureDetector( onTap: onTap, - child: ImmichImage( - asset, - fit: BoxFit.fitWidth, - height: double.infinity, - width: double.infinity, - type: ThumbnailFormat.JPEG, - preferredLocalAssetSize: 2048, - ), - ), - Positioned( - top: 2.0, - left: 2.0, - child: IconButton( - onPressed: onClose, - icon: const Icon(Icons.close_rounded), - color: Colors.grey[400], - ), - ), - Positioned( - right: 18.0, - top: 18.0, - child: Text( - rightCornerText ?? "", - style: TextStyle( - color: Colors.grey[200], - fontSize: 12.0, - fontWeight: FontWeight.bold, - ), + child: LayoutBuilder( + builder: (context, constraints) { + // Determine the fit using the aspect ratio + BoxFit fit = BoxFit.fitWidth; + if (asset.width != null && asset.height != null) { + final aspectRatio = asset.height! / asset.width!; + final phoneAspectRatio = + constraints.maxWidth / constraints.maxHeight; + // Look for a 25% difference in either direction + if (phoneAspectRatio * .75 < aspectRatio && + phoneAspectRatio * 1.25 > aspectRatio) { + // Cover to look nice if we have nearly the same aspect ratio + fit = BoxFit.cover; + } + } + + return ImmichImage( + asset, + fit: fit, + height: double.infinity, + width: double.infinity, + type: ThumbnailFormat.JPEG, + preferredLocalAssetSize: 2048, + ); + }, ), ), if (showTitle) diff --git a/mobile/lib/modules/memories/views/memory_page.dart b/mobile/lib/modules/memories/views/memory_page.dart index 7669e8ba25..e70fdcca15 100644 --- a/mobile/lib/modules/memories/views/memory_page.dart +++ b/mobile/lib/modules/memories/views/memory_page.dart @@ -16,23 +16,33 @@ class MemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const MemoryPage({ + MemoryPage({ required this.memories, required this.memoryIndex, super.key, }); + /// The list of all of the asset page controllers + late final memoryAssetPageControllers = + List.generate(memories.length, (i) => PageController()); + + /// The main vertically scrolling page controller with each list of memories + late final memoryPageController = PageController(initialPage: memoryIndex); + @override Widget build(BuildContext context, WidgetRef ref) { - final memoryPageController = usePageController(initialPage: memoryIndex); - final memoryAssetPageController = usePageController(); final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); + final currentMemoryIndex = useState(0); final assetProgress = useState( "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", ); const bgColor = Colors.black; + // The Page Controller that scrolls horizontally with all of the assets + PageController currentMemoryAssetPageController = + memoryAssetPageControllers[currentMemoryIndex.value]; + useEffect(() { // Memories is an immersive activity SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); @@ -49,7 +59,7 @@ class MemoryPage extends HookConsumerWidget { toNextAsset(int currentAssetIndex) { if (currentAssetIndex + 1 < currentMemory.value.assets.length) { // Go to the next asset - memoryAssetPageController.nextPage( + currentMemoryAssetPageController.nextPage( curve: Curves.easeInOut, duration: const Duration(milliseconds: 500), ); @@ -175,6 +185,7 @@ class MemoryPage extends HookConsumerWidget { onPageChanged: (pageNumber) { HapticFeedback.mediumImpact(); if (pageNumber < memories.length) { + currentMemoryIndex.value = pageNumber; currentMemory.value = memories[pageNumber]; } @@ -197,33 +208,65 @@ class MemoryPage extends HookConsumerWidget { // Build horizontal page return Column( children: [ + AnimatedBuilder( + animation: currentMemoryAssetPageController, + builder: (context, child) { + double value = 0.0; + if (currentMemoryAssetPageController.hasClients) { + // We can only access [page] if this has clients + value = currentMemoryAssetPageController.page ?? 0; + } + return LinearProgressIndicator( + value: (value + 1) / memories[mIndex].assets.length, + ); + }, + ), Expanded( child: PageView.builder( physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), - controller: memoryAssetPageController, + controller: memoryAssetPageControllers[mIndex], onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, itemCount: memories[mIndex].assets.length, itemBuilder: (context, index) { final asset = memories[mIndex].assets[index]; - return Container( - color: Colors.black, - child: MemoryCard( - asset: asset, - onTap: () => toNextAsset(index), - onClose: () { - // auto_route doesn't invoke pop scope, so - // turn off full screen mode here - // https://github.com/Milad-Akarie/auto_route_library/issues/1799 - context.popRoute(); - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - }, - rightCornerText: assetProgress.value, - title: memories[mIndex].title, - showTitle: index == 0, - ), + return Stack( + children: [ + Container( + color: Colors.black, + child: MemoryCard( + asset: asset, + onTap: () => toNextAsset(index), + title: memories[mIndex].title, + showTitle: index == 0, + ), + ), + Positioned( + top: 8, + left: 8, + child: MaterialButton( + minWidth: 0, + onPressed: () { + // auto_route doesn't invoke pop scope, so + // turn off full screen mode here + // https://github.com/Milad-Akarie/auto_route_library/issues/1799 + context.popRoute(); + SystemChrome.setEnabledSystemUIMode( + SystemUiMode.edgeToEdge, + ); + }, + shape: const CircleBorder(), + color: Colors.white.withOpacity(0.2), + elevation: 0, + child: const Icon( + Icons.close_rounded, + color: Colors.white, + ), + ), + ), + ], ); }, ),