mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	Implementing scroll bar like Google Photos
This commit is contained in:
		| @@ -75,12 +75,14 @@ class ImmichSliverAppBar extends ConsumerWidget { | ||||
|                 onPressed: () async { | ||||
|                   var onPop = await AutoRouter.of(context).push(const BackupControllerRoute()); | ||||
|  | ||||
|                   // Fetch new image | ||||
|                   if (onPop == true) { | ||||
|                     // Remove and force getting new widget again | ||||
|                     if (imageGridGroup.isNotEmpty) { | ||||
|                     // Remove and force getting new widget again if there is not many widget on screen. | ||||
|                     // Otherwise do nothing. | ||||
|                     if (imageGridGroup.isNotEmpty && imageGridGroup.length < 20) { | ||||
|                       print("Get more access"); | ||||
|                       ref.read(assetProvider.notifier).getMoreAsset(); | ||||
|                     } else { | ||||
|                     } else if (imageGridGroup.isEmpty) { | ||||
|                       print("get immich asset"); | ||||
|                       ref.read(assetProvider.notifier).getImmichAssets(); | ||||
|                     } | ||||
|                   } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/rendering.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart'; | ||||
| @@ -8,6 +9,7 @@ import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.da | ||||
| import 'package:immich_mobile/modules/home/ui/image_grid.dart'; | ||||
| import 'package:immich_mobile/modules/home/providers/asset.provider.dart'; | ||||
| import 'package:immich_mobile/shared/providers/backup.provider.dart'; | ||||
| import 'package:visibility_detector/visibility_detector.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
|  | ||||
| class HomePage extends HookConsumerWidget { | ||||
| @@ -18,10 +20,9 @@ class HomePage extends HookConsumerWidget { | ||||
|     final ValueNotifier<bool> _showBackToTopBtn = useState(false); | ||||
|     ScrollController _scrollController = useScrollController(); | ||||
|     List<ImmichAssetGroupByDate> assetGroup = ref.watch(assetProvider); | ||||
|     BackUpState _backupState = ref.watch(backupProvider); | ||||
|     List<Widget> imageGridGroup = []; | ||||
|     List<GlobalKey> monthGroupKey = []; | ||||
|  | ||||
|     final monthInView = useState<String>(""); | ||||
|     _scrollControllerCallback() { | ||||
|       var endOfPage = _scrollController.position.maxScrollExtent; | ||||
|  | ||||
| @@ -34,6 +35,13 @@ class HomePage extends HookConsumerWidget { | ||||
|       } else { | ||||
|         _showBackToTopBtn.value = false; | ||||
|       } | ||||
|  | ||||
|       // Quick Scroll For Jumping to Month | ||||
|       if (_scrollController.position.userScrollDirection == ScrollDirection.forward) { | ||||
|         // Scroll UP | ||||
|       } else if (_scrollController.position.userScrollDirection == ScrollDirection.reverse) { | ||||
|         // SCroll Down | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     useEffect(() { | ||||
| @@ -41,8 +49,63 @@ class HomePage extends HookConsumerWidget { | ||||
|  | ||||
|       _scrollController.addListener(_scrollControllerCallback); | ||||
|  | ||||
|       return () => _scrollController.removeListener(_scrollControllerCallback); | ||||
|     }, [_scrollController, key]); | ||||
|       return () { | ||||
|         debugPrint("Remove scroll listener"); | ||||
|         _scrollController.removeListener(_scrollControllerCallback); | ||||
|       }; | ||||
|     }, []); | ||||
|  | ||||
|     SliverToBoxAdapter _buildMonthGroupTitle(String dateTitle, BuildContext context) { | ||||
|       return SliverToBoxAdapter( | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.only(left: 10.0, top: 32), | ||||
|           child: Text( | ||||
|             DateFormat('MMMM, y').format( | ||||
|               DateTime.parse(dateTitle), | ||||
|             ), | ||||
|             style: TextStyle( | ||||
|               fontSize: 24, | ||||
|               fontWeight: FontWeight.bold, | ||||
|               color: Theme.of(context).primaryColor, | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     SliverToBoxAdapter _buildDateGroupTitle(String dateTitle) { | ||||
|       var currentYear = DateTime.now().year; | ||||
|       var groupYear = DateTime.parse(dateTitle).year; | ||||
|       var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy'; | ||||
|       var dateText = DateFormat(formatDateTemplate).format(DateTime.parse(dateTitle)); | ||||
|       var monthText = DateFormat('MMMM, y').format(DateTime.parse(dateTitle)); | ||||
|       return SliverToBoxAdapter( | ||||
|         child: VisibilityDetector( | ||||
|           key: Key(dateText), | ||||
|           onVisibilityChanged: (visibilityInfo) { | ||||
|             monthInView.value = monthText; | ||||
|           }, | ||||
|           child: Padding( | ||||
|             padding: const EdgeInsets.only(top: 24.0, bottom: 24.0, left: 3.0), | ||||
|             child: Row( | ||||
|               children: [ | ||||
|                 Padding( | ||||
|                   padding: const EdgeInsets.only(left: 8.0, bottom: 5.0, top: 5.0), | ||||
|                   child: Text( | ||||
|                     dateText, | ||||
|                     style: const TextStyle( | ||||
|                       fontSize: 14, | ||||
|                       fontWeight: FontWeight.bold, | ||||
|                       color: Colors.black87, | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     Widget _buildBody() { | ||||
|       if (assetGroup.isNotEmpty) { | ||||
| @@ -56,28 +119,9 @@ class HomePage extends HookConsumerWidget { | ||||
|           int? previousMonth = DateTime.tryParse(lastGroupDate)?.month; | ||||
|  | ||||
|           if ((currentMonth! - previousMonth!) != 0) { | ||||
|             var myKey = GlobalKey(); | ||||
|             monthGroupKey.add(myKey); | ||||
|             // debugPrint("Group Key $myKey"); | ||||
|             var monthTitleText = DateFormat('MMMM, y').format(DateTime.parse(dateTitle)); | ||||
|  | ||||
|             imageGridGroup.add( | ||||
|               SliverToBoxAdapter( | ||||
|                 key: myKey, | ||||
|                 child: Padding( | ||||
|                   padding: const EdgeInsets.only(left: 10.0, top: 32), | ||||
|                   child: Text( | ||||
|                     DateFormat('MMMM, y').format( | ||||
|                       DateTime.parse(dateTitle), | ||||
|                     ), | ||||
|                     style: TextStyle( | ||||
|                       fontSize: 24, | ||||
|                       fontWeight: FontWeight.bold, | ||||
|                       color: Theme.of(context).primaryColor, | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ); | ||||
|             imageGridGroup.add(_buildMonthGroupTitle(monthTitleText, context)); | ||||
|           } | ||||
|  | ||||
|           imageGridGroup.add( | ||||
| @@ -88,39 +132,36 @@ class HomePage extends HookConsumerWidget { | ||||
|  | ||||
|           lastGroupDate = dateTitle; | ||||
|         } | ||||
|       }  | ||||
|       } | ||||
|  | ||||
|        return SafeArea( | ||||
|           child: CustomScrollView( | ||||
|       return SafeArea( | ||||
|         child: Stack(children: [ | ||||
|           RawScrollbar( | ||||
|             minThumbLength: 50, | ||||
|             isAlwaysShown: false, | ||||
|             interactive: true, | ||||
|             controller: _scrollController, | ||||
|             slivers: [ | ||||
|               ImmichSliverAppBar(imageGridGroup: imageGridGroup), | ||||
|               ...imageGridGroup, | ||||
|             ], | ||||
|             thickness: 50, | ||||
|             crossAxisMargin: -20, | ||||
|             mainAxisMargin: 70, | ||||
|             timeToFade: const Duration(seconds: 2), | ||||
|             thumbColor: Colors.blueGrey, | ||||
|             radius: const Radius.circular(30), | ||||
|             child: CustomScrollView( | ||||
|               controller: _scrollController, | ||||
|               slivers: [ | ||||
|                 ImmichSliverAppBar(imageGridGroup: imageGridGroup), | ||||
|                 ...imageGridGroup, | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ); | ||||
|         ]), | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return Scaffold( | ||||
|       drawer: const ProfileDrawer(), | ||||
|       body: _buildBody(), | ||||
|       bottomNavigationBar: BottomAppBar( | ||||
|         child: IconButton( | ||||
|           onPressed: () { | ||||
|             if (monthGroupKey.isNotEmpty) { | ||||
|               var targetContext = monthGroupKey.last.currentContext; | ||||
|               if (targetContext != null) { | ||||
|                 Scrollable.ensureVisible( | ||||
|                   targetContext, | ||||
|                   duration: const Duration(milliseconds: 400), | ||||
|                   curve: Curves.easeInOut, | ||||
|                 ); | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|           icon: const Icon(Icons.ac_unit_outlined), | ||||
|         ), | ||||
|       ), | ||||
|       floatingActionButton: _showBackToTopBtn.value | ||||
|           ? FloatingActionButton.small( | ||||
|               enableFeedback: true, | ||||
| @@ -134,30 +175,4 @@ class HomePage extends HookConsumerWidget { | ||||
|           : null, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   SliverToBoxAdapter _buildDateGroupTitle(String dateTitle) { | ||||
|     var currentYear = DateTime.now().year; | ||||
|     var groupYear = DateTime.parse(dateTitle).year; | ||||
|     var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy'; | ||||
|     return SliverToBoxAdapter( | ||||
|       child: Padding( | ||||
|         padding: const EdgeInsets.only(top: 24.0, bottom: 24.0, left: 3.0), | ||||
|         child: Row( | ||||
|           children: [ | ||||
|             Padding( | ||||
|               padding: const EdgeInsets.only(left: 8.0, bottom: 5.0, top: 5.0), | ||||
|               child: Text( | ||||
|                 DateFormat(formatDateTemplate).format(DateTime.parse(dateTitle)), | ||||
|                 style: const TextStyle( | ||||
|                   fontSize: 14, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                   color: Colors.black87, | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ class LoginForm extends HookConsumerWidget { | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final usernameController = useTextEditingController(text: 'testuser@email.com'); | ||||
|     final passwordController = useTextEditingController(text: 'password'); | ||||
|     final serverEndpointController = useTextEditingController(text: 'http://192.168.1.216'); | ||||
|     final serverEndpointController = useTextEditingController(text: 'http://192.168.1.103:3000'); | ||||
|  | ||||
|     return Center( | ||||
|       child: ConstrainedBox( | ||||
|   | ||||
		Reference in New Issue
	
	Block a user