diff --git a/mobile/lib/modules/memories/ui/memory_epilogue.dart b/mobile/lib/modules/memories/ui/memory_epilogue.dart index 4e32ae6ac5..8dd28637df 100644 --- a/mobile/lib/modules/memories/ui/memory_epilogue.dart +++ b/mobile/lib/modules/memories/ui/memory_epilogue.dart @@ -16,7 +16,7 @@ class _MemoryEpilogueState extends State late final _animationController = AnimationController( vsync: this, duration: const Duration( - seconds: 3, + seconds: 2, ), )..repeat( reverse: true, @@ -29,7 +29,7 @@ class _MemoryEpilogueState extends State super.initState(); _animation = CurvedAnimation( parent: _animationController, - curve: Curves.easeInOut, + curve: Curves.easeIn, ); } @@ -41,74 +41,82 @@ class _MemoryEpilogueState extends State @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - Icons.check_circle_outline_sharp, - color: immichDarkThemePrimaryColor, - size: 64.0, - ), - const SizedBox(height: 16.0), - Text( - 'All caught up', - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Colors.white, + return SafeArea( + child: Stack( + children: [ + Positioned.fill( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.check_circle_outline_sharp, + color: immichDarkThemePrimaryColor, + size: 64.0, + ), + const SizedBox(height: 16.0), + Text( + 'All caught up', + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Colors.white, + ), + ), + const SizedBox(height: 16.0), + Text( + 'Check back tomorrow for more memories', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Colors.white, + ), + ), + const SizedBox(height: 16.0), + TextButton( + onPressed: widget.onStartOver, + child: Text( + 'Start Over', + style: context.textTheme.displayMedium?.copyWith( + color: immichDarkThemePrimaryColor, ), - ), - const SizedBox(height: 16.0), - Text( - 'Check back tomorrow for more memories', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), - ), - const SizedBox(height: 16.0), - TextButton( - onPressed: widget.onStartOver, - child: Text( - 'Start Over', - style: context.textTheme.displayMedium?.copyWith( - color: immichDarkThemePrimaryColor, ), ), - ), - ], + ], + ), ), - ), - Column( - children: [ - SizedBox( - height: 48, - child: AnimatedBuilder( - animation: _animation, - builder: (context, child) { - return Transform.translate( - offset: Offset(0, 5 * _animationController.value), - child: child, - ); - }, - child: const Icon( - size: 32, - Icons.expand_less_sharp, - color: Colors.white, - ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: Column( + children: [ + SizedBox( + height: 48, + child: AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return Transform.translate( + offset: Offset(0, 8 * _animationController.value), + child: child, + ); + }, + child: const Icon( + size: 32, + Icons.expand_less_sharp, + color: Colors.white, + ), + ), + ), + Text( + 'Swipe up to close', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Colors.white, + ), + ), + ], ), ), - Text( - 'Swipe up to close', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), - ), - ], - ), - ], + ), + ], + ), ); } } diff --git a/mobile/lib/modules/memories/ui/memory_progress_indicator.dart b/mobile/lib/modules/memories/ui/memory_progress_indicator.dart new file mode 100644 index 0000000000..bad9ea5f84 --- /dev/null +++ b/mobile/lib/modules/memories/ui/memory_progress_indicator.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +class MemoryProgressIndicator extends StatelessWidget { + /// The number of ticks in the progress indicator + final int ticks; + + /// The current value of the indicator + final double value; + + const MemoryProgressIndicator( + {super.key, required this.ticks, required this.value}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final tickWidth = constraints.maxWidth / ticks; + return Stack( + children: [ + LinearProgressIndicator( + value: value, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate( + ticks, + (i) => Container( + width: tickWidth, + height: 4, + decoration: BoxDecoration( + border: i == 0 + ? null + : Border( + left: BorderSide( + color: Theme.of(context).scaffoldBackgroundColor, + width: 1, + ), + ), + ), + ), + ), + ), + ], + ); + }, + ); + } +} diff --git a/mobile/lib/modules/memories/views/memory_page.dart b/mobile/lib/modules/memories/views/memory_page.dart index e5e157fa6c..4522833179 100644 --- a/mobile/lib/modules/memories/views/memory_page.dart +++ b/mobile/lib/modules/memories/views/memory_page.dart @@ -7,6 +7,7 @@ import 'package:immich_mobile/modules/memories/models/memory.dart'; import 'package:immich_mobile/modules/memories/ui/memory_bottom_info.dart'; import 'package:immich_mobile/modules/memories/ui/memory_card.dart'; import 'package:immich_mobile/modules/memories/ui/memory_epilogue.dart'; +import 'package:immich_mobile/modules/memories/ui/memory_progress_indicator.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart'; import 'package:openapi/api.dart' as api; @@ -208,18 +209,27 @@ 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, - ); - }, + Padding( + padding: const EdgeInsets.only( + left: 24.0, + right: 24.0, + top: 8.0, + bottom: 2.0, + ), + child: 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 MemoryProgressIndicator( + ticks: memories[mIndex].assets.length, + value: (value + 1) / memories[mIndex].assets.length, + ); + }, + ), ), Expanded( child: Stack(