mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	Implemented multi select interaction (#13)
This commit is contained in:
		| @@ -15,8 +15,11 @@ This project is under heavy development, there will be continous functions, feat | ||||
| # Features | ||||
|  | ||||
| [x] Upload assets(videos/images) | ||||
|  | ||||
| [x] View assets | ||||
|  | ||||
| [x] Quick navigation with drag scroll bar | ||||
|  | ||||
| [x] Auto Backup | ||||
|  | ||||
| # Development | ||||
|   | ||||
							
								
								
									
										66
									
								
								mobile/lib/modules/home/models/home_page_state.model.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								mobile/lib/modules/home/models/home_page_state.model.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:collection/collection.dart'; | ||||
|  | ||||
| import 'package:immich_mobile/shared/models/immich_asset.model.dart'; | ||||
|  | ||||
| class HomePageState { | ||||
|   final bool isMultiSelectEnable; | ||||
|   final Set<ImmichAsset> selectedItems; | ||||
|   final Set<String> selectedDateGroup; | ||||
|   HomePageState({ | ||||
|     required this.isMultiSelectEnable, | ||||
|     required this.selectedItems, | ||||
|     required this.selectedDateGroup, | ||||
|   }); | ||||
|  | ||||
|   HomePageState copyWith({ | ||||
|     bool? isMultiSelectEnable, | ||||
|     Set<ImmichAsset>? selectedItems, | ||||
|     Set<String>? selectedDateGroup, | ||||
|   }) { | ||||
|     return HomePageState( | ||||
|       isMultiSelectEnable: isMultiSelectEnable ?? this.isMultiSelectEnable, | ||||
|       selectedItems: selectedItems ?? this.selectedItems, | ||||
|       selectedDateGroup: selectedDateGroup ?? this.selectedDateGroup, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Map<String, dynamic> toMap() { | ||||
|     return { | ||||
|       'isMultiSelectEnable': isMultiSelectEnable, | ||||
|       'selectedItems': selectedItems.map((x) => x.toMap()).toList(), | ||||
|       'selectedDateGroup': selectedDateGroup.toList(), | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   factory HomePageState.fromMap(Map<String, dynamic> map) { | ||||
|     return HomePageState( | ||||
|       isMultiSelectEnable: map['isMultiSelectEnable'] ?? false, | ||||
|       selectedItems: Set<ImmichAsset>.from(map['selectedItems']?.map((x) => ImmichAsset.fromMap(x))), | ||||
|       selectedDateGroup: Set<String>.from(map['selectedDateGroup']), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   String toJson() => json.encode(toMap()); | ||||
|  | ||||
|   factory HomePageState.fromJson(String source) => HomePageState.fromMap(json.decode(source)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => | ||||
|       'HomePageState(isMultiSelectEnable: $isMultiSelectEnable, selectedItems: $selectedItems, selectedDateGroup: $selectedDateGroup)'; | ||||
|  | ||||
|   @override | ||||
|   bool operator ==(Object other) { | ||||
|     if (identical(this, other)) return true; | ||||
|     final setEquals = const DeepCollectionEquality().equals; | ||||
|  | ||||
|     return other is HomePageState && | ||||
|         other.isMultiSelectEnable == isMultiSelectEnable && | ||||
|         setEquals(other.selectedItems, selectedItems) && | ||||
|         setEquals(other.selectedDateGroup, selectedDateGroup); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   int get hashCode => isMultiSelectEnable.hashCode ^ selectedItems.hashCode ^ selectedDateGroup.hashCode; | ||||
| } | ||||
| @@ -0,0 +1,63 @@ | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/home/models/home_page_state.model.dart'; | ||||
| import 'package:immich_mobile/shared/models/immich_asset.model.dart'; | ||||
|  | ||||
| class HomePageStateNotifier extends StateNotifier<HomePageState> { | ||||
|   HomePageStateNotifier() | ||||
|       : super( | ||||
|           HomePageState( | ||||
|             isMultiSelectEnable: false, | ||||
|             selectedItems: {}, | ||||
|             selectedDateGroup: {}, | ||||
|           ), | ||||
|         ); | ||||
|  | ||||
|   void addSelectedDateGroup(String dateGroupTitle) { | ||||
|     state = state.copyWith(selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle}); | ||||
|   } | ||||
|  | ||||
|   void removeSelectedDateGroup(String dateGroupTitle) { | ||||
|     var currentDateGroup = state.selectedDateGroup; | ||||
|  | ||||
|     currentDateGroup.removeWhere((e) => e == dateGroupTitle); | ||||
|  | ||||
|     state = state.copyWith(selectedDateGroup: currentDateGroup); | ||||
|   } | ||||
|  | ||||
|   void enableMultiSelect(Set<ImmichAsset> selectedItems) { | ||||
|     state = state.copyWith(isMultiSelectEnable: true, selectedItems: selectedItems); | ||||
|   } | ||||
|  | ||||
|   void disableMultiSelect() { | ||||
|     state = state.copyWith(isMultiSelectEnable: false, selectedItems: {}, selectedDateGroup: {}); | ||||
|   } | ||||
|  | ||||
|   void addSingleSelectedItem(ImmichAsset asset) { | ||||
|     state = state.copyWith(selectedItems: {...state.selectedItems, asset}); | ||||
|   } | ||||
|  | ||||
|   void addMultipleSelectedItems(List<ImmichAsset> assets) { | ||||
|     state = state.copyWith(selectedItems: {...state.selectedItems, ...assets}); | ||||
|   } | ||||
|  | ||||
|   void removeSingleSelectedItem(ImmichAsset asset) { | ||||
|     Set<ImmichAsset> currentList = state.selectedItems; | ||||
|  | ||||
|     currentList.removeWhere((e) => e.id == asset.id); | ||||
|  | ||||
|     state = state.copyWith(selectedItems: currentList); | ||||
|   } | ||||
|  | ||||
|   void removeMultipleSelectedItem(List<ImmichAsset> assets) { | ||||
|     Set<ImmichAsset> currentList = state.selectedItems; | ||||
|  | ||||
|     for (ImmichAsset asset in assets) { | ||||
|       currentList.removeWhere((e) => e.id == asset.id); | ||||
|     } | ||||
|  | ||||
|     state = state.copyWith(selectedItems: currentList); | ||||
|   } | ||||
| } | ||||
|  | ||||
| final homePageStateProvider = | ||||
|     StateNotifierProvider<HomePageStateNotifier, HomePageState>(((ref) => HomePageStateNotifier())); | ||||
							
								
								
									
										75
									
								
								mobile/lib/modules/home/ui/daily_title_text.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								mobile/lib/modules/home/ui/daily_title_text.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; | ||||
| import 'package:immich_mobile/shared/models/immich_asset.model.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
|  | ||||
| class DailyTitleText extends ConsumerWidget { | ||||
|   const DailyTitleText({ | ||||
|     Key? key, | ||||
|     required this.isoDate, | ||||
|     required this.assetGroup, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   final String isoDate; | ||||
|   final List<ImmichAsset> assetGroup; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     var currentYear = DateTime.now().year; | ||||
|     var groupYear = DateTime.parse(isoDate).year; | ||||
|     var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy'; | ||||
|     var dateText = DateFormat(formatDateTemplate).format(DateTime.parse(isoDate)); | ||||
|     var isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable; | ||||
|     var selectedDateGroup = ref.watch(homePageStateProvider).selectedDateGroup; | ||||
|     var selectedItems = ref.watch(homePageStateProvider).selectedItems; | ||||
|  | ||||
|     return SliverToBoxAdapter( | ||||
|       child: Padding( | ||||
|         padding: const EdgeInsets.only(top: 29.0, bottom: 29.0, left: 12.0, right: 12.0), | ||||
|         child: Row( | ||||
|           children: [ | ||||
|             Text( | ||||
|               dateText, | ||||
|               style: const TextStyle( | ||||
|                 fontSize: 14, | ||||
|                 fontWeight: FontWeight.bold, | ||||
|                 color: Colors.black87, | ||||
|               ), | ||||
|             ), | ||||
|             const Spacer(), | ||||
|             GestureDetector( | ||||
|               onTap: () { | ||||
|                 if (isMultiSelectEnable && | ||||
|                     selectedDateGroup.contains(dateText) && | ||||
|                     selectedDateGroup.length == 1 && | ||||
|                     selectedItems.length == assetGroup.length) { | ||||
|                   ref.watch(homePageStateProvider.notifier).disableMultiSelect(); | ||||
|                 } else if (isMultiSelectEnable && | ||||
|                     selectedDateGroup.contains(dateText) && | ||||
|                     selectedItems.length != assetGroup.length) { | ||||
|                   ref.watch(homePageStateProvider.notifier).removeSelectedDateGroup(dateText); | ||||
|                   ref.watch(homePageStateProvider.notifier).removeMultipleSelectedItem(assetGroup); | ||||
|                 } else if (isMultiSelectEnable && | ||||
|                     selectedDateGroup.contains(dateText) && | ||||
|                     selectedDateGroup.length > 1) { | ||||
|                   ref.watch(homePageStateProvider.notifier).removeSelectedDateGroup(dateText); | ||||
|                   ref.watch(homePageStateProvider.notifier).removeMultipleSelectedItem(assetGroup); | ||||
|                 } else if (isMultiSelectEnable && !selectedDateGroup.contains(dateText)) { | ||||
|                   ref.watch(homePageStateProvider.notifier).addSelectedDateGroup(dateText); | ||||
|                   ref.watch(homePageStateProvider.notifier).addMultipleSelectedItems(assetGroup); | ||||
|                 } else { | ||||
|                   ref.watch(homePageStateProvider.notifier).enableMultiSelect(assetGroup.toSet()); | ||||
|                   ref.watch(homePageStateProvider.notifier).addSelectedDateGroup(dateText); | ||||
|                 } | ||||
|               }, | ||||
|               child: isMultiSelectEnable && selectedDateGroup.contains(dateText) | ||||
|                   ? const Icon(Icons.check_circle_rounded) | ||||
|                   : const Icon(Icons.check_circle_outline_rounded), | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										30
									
								
								mobile/lib/modules/home/ui/monthly_title_text.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								mobile/lib/modules/home/ui/monthly_title_text.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
|  | ||||
| class MonthlyTitleText extends StatelessWidget { | ||||
|   const MonthlyTitleText({ | ||||
|     Key? key, | ||||
|     required this.isoDate, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   final String isoDate; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     var monthTitleText = DateFormat('MMMM y').format(DateTime.parse(isoDate)); | ||||
|  | ||||
|     return SliverToBoxAdapter( | ||||
|       child: Padding( | ||||
|         padding: const EdgeInsets.only(left: 12.0, top: 32), | ||||
|         child: Text( | ||||
|           monthTitleText, | ||||
|           style: TextStyle( | ||||
|             fontSize: 26, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: Theme.of(context).primaryColor, | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,26 +1,54 @@ | ||||
| import 'package:auto_route/auto_route.dart'; | ||||
| import 'package:cached_network_image/cached_network_image.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/hive_box.dart'; | ||||
| import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; | ||||
| import 'package:immich_mobile/shared/models/immich_asset.model.dart'; | ||||
| import 'package:immich_mobile/routing/router.dart'; | ||||
|  | ||||
| class ThumbnailImage extends HookWidget { | ||||
| class ThumbnailImage extends HookConsumerWidget { | ||||
|   final ImmichAsset asset; | ||||
|  | ||||
|   const ThumbnailImage({Key? key, required this.asset}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final cacheKey = useState(1); | ||||
|  | ||||
|     var box = Hive.box(userInfoBox); | ||||
|     var thumbnailRequestUrl = | ||||
|         '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true'; | ||||
|  | ||||
|     var selectedAsset = ref.watch(homePageStateProvider).selectedItems; | ||||
|     var isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable; | ||||
|  | ||||
|     Widget _buildSelectionIcon(ImmichAsset asset) { | ||||
|       if (selectedAsset.contains(asset)) { | ||||
|         return Icon( | ||||
|           Icons.check_circle, | ||||
|           color: Theme.of(context).primaryColor, | ||||
|         ); | ||||
|       } else { | ||||
|         return const Icon( | ||||
|           Icons.circle_outlined, | ||||
|           color: Colors.white, | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return GestureDetector( | ||||
|       onTap: () { | ||||
|         if (isMultiSelectEnable && selectedAsset.contains(asset) && selectedAsset.length == 1) { | ||||
|           ref.watch(homePageStateProvider.notifier).disableMultiSelect(); | ||||
|         } else if (isMultiSelectEnable && selectedAsset.contains(asset) && selectedAsset.length > 1) { | ||||
|           ref.watch(homePageStateProvider.notifier).removeSingleSelectedItem(asset); | ||||
|         } else if (isMultiSelectEnable && !selectedAsset.contains(asset)) { | ||||
|           ref.watch(homePageStateProvider.notifier).addSingleSelectedItem(asset); | ||||
|         } else { | ||||
|           if (asset.type == 'IMAGE') { | ||||
|             AutoRouter.of(context).push( | ||||
|               ImageViewerRoute( | ||||
| @@ -39,10 +67,23 @@ class ThumbnailImage extends HookWidget { | ||||
|               ), | ||||
|             ); | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       onLongPress: () { | ||||
|         // Enable multi selecte function | ||||
|         ref.watch(homePageStateProvider.notifier).enableMultiSelect({asset}); | ||||
|         HapticFeedback.heavyImpact(); | ||||
|       }, | ||||
|       onLongPress: () {}, | ||||
|       child: Hero( | ||||
|         tag: asset.id, | ||||
|         child: Stack( | ||||
|           children: [ | ||||
|             Container( | ||||
|               decoration: BoxDecoration( | ||||
|                 border: isMultiSelectEnable && selectedAsset.contains(asset) | ||||
|                     ? Border.all(color: Theme.of(context).primaryColorLight, width: 10) | ||||
|                     : const Border(), | ||||
|               ), | ||||
|               child: CachedNetworkImage( | ||||
|                 cacheKey: "${asset.id}-${cacheKey.value}", | ||||
|                 width: 300, | ||||
| @@ -63,6 +104,20 @@ class ThumbnailImage extends HookWidget { | ||||
|                 }, | ||||
|               ), | ||||
|             ), | ||||
|             Container( | ||||
|               child: isMultiSelectEnable | ||||
|                   ? Padding( | ||||
|                       padding: const EdgeInsets.all(3.0), | ||||
|                       child: Align( | ||||
|                         alignment: Alignment.topLeft, | ||||
|                         child: _buildSelectionIcon(asset), | ||||
|                       ), | ||||
|                     ) | ||||
|                   : Container(), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,24 +1,23 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/daily_title_text.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/draggable_scrollbar.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/image_grid.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/monthly_title_text.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/profile_drawer.dart'; | ||||
| import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.dart'; | ||||
| import 'package:immich_mobile/modules/home/providers/asset.provider.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
|  | ||||
| class HomePage extends HookConsumerWidget { | ||||
|   const HomePage({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final ValueNotifier<bool> _showBackToTopBtn = useState(false); | ||||
|     ScrollController _scrollController = useScrollController(); | ||||
|  | ||||
|     List<ImmichAssetGroupByDate> assetGroup = ref.watch(assetProvider); | ||||
|     List<Widget> imageGridGroup = []; | ||||
|     List<ImmichAssetGroupByDate> _assetGroup = ref.watch(assetProvider); | ||||
|     List<Widget> _imageGridGroup = []; | ||||
|  | ||||
|     _scrollControllerCallback() { | ||||
|       var endOfPage = _scrollController.position.maxScrollExtent; | ||||
| @@ -26,12 +25,6 @@ class HomePage extends HookConsumerWidget { | ||||
|       if (_scrollController.offset >= endOfPage - (endOfPage * 0.1) && !_scrollController.position.outOfRange) { | ||||
|         ref.read(assetProvider.notifier).getOlderAsset(); | ||||
|       } | ||||
|  | ||||
|       if (_scrollController.offset >= 400) { | ||||
|         _showBackToTopBtn.value = true; | ||||
|       } else { | ||||
|         _showBackToTopBtn.value = false; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     useEffect(() { | ||||
| @@ -49,18 +42,18 @@ class HomePage extends HookConsumerWidget { | ||||
|       // Remove and force getting new widget again if there is not many widget on screen. | ||||
|       // Otherwise do nothing. | ||||
|  | ||||
|       if (imageGridGroup.isNotEmpty && imageGridGroup.length < 20) { | ||||
|       if (_imageGridGroup.isNotEmpty && _imageGridGroup.length < 20) { | ||||
|         ref.read(assetProvider.notifier).getOlderAsset(); | ||||
|       } else if (imageGridGroup.isEmpty) { | ||||
|       } else if (_imageGridGroup.isEmpty) { | ||||
|         ref.read(assetProvider.notifier).getImmichAssets(); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     Widget _buildBody() { | ||||
|       if (assetGroup.isNotEmpty) { | ||||
|         String lastGroupDate = assetGroup[0].date; | ||||
|       if (_assetGroup.isNotEmpty) { | ||||
|         String lastGroupDate = _assetGroup[0].date; | ||||
|  | ||||
|         for (var group in assetGroup) { | ||||
|         for (var group in _assetGroup) { | ||||
|           var dateTitle = group.date; | ||||
|           var assetGroup = group.assets; | ||||
|  | ||||
| @@ -71,19 +64,19 @@ class HomePage extends HookConsumerWidget { | ||||
|  | ||||
|           if (currentMonth != null && previousMonth != null) { | ||||
|             if ((currentMonth - previousMonth) != 0) { | ||||
|               imageGridGroup.add( | ||||
|               _imageGridGroup.add( | ||||
|                 MonthlyTitleText(isoDate: dateTitle), | ||||
|               ); | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           // Add Daily Title Group | ||||
|           imageGridGroup.add( | ||||
|             DailyTitleText(isoDate: dateTitle), | ||||
|           _imageGridGroup.add( | ||||
|             DailyTitleText(isoDate: dateTitle, assetGroup: assetGroup), | ||||
|           ); | ||||
|  | ||||
|           // Add Image Group | ||||
|           imageGridGroup.add( | ||||
|           _imageGridGroup.add( | ||||
|             ImageGrid(assetGroup: assetGroup), | ||||
|           ); | ||||
|           // | ||||
| @@ -100,10 +93,10 @@ class HomePage extends HookConsumerWidget { | ||||
|             controller: _scrollController, | ||||
|             slivers: [ | ||||
|               ImmichSliverAppBar( | ||||
|                 imageGridGroup: imageGridGroup, | ||||
|                 imageGridGroup: _imageGridGroup, | ||||
|                 onPopBack: onPopBackFromBackupPage, | ||||
|               ), | ||||
|               ...imageGridGroup, | ||||
|               ..._imageGridGroup, | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
| @@ -116,69 +109,3 @@ class HomePage extends HookConsumerWidget { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class MonthlyTitleText extends StatelessWidget { | ||||
|   const MonthlyTitleText({ | ||||
|     Key? key, | ||||
|     required this.isoDate, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   final String isoDate; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     var monthTitleText = DateFormat('MMMM, y').format(DateTime.parse(isoDate)); | ||||
|  | ||||
|     return SliverToBoxAdapter( | ||||
|       child: Padding( | ||||
|         padding: const EdgeInsets.only(left: 10.0, top: 32), | ||||
|         child: Text( | ||||
|           monthTitleText, | ||||
|           style: TextStyle( | ||||
|             fontSize: 24, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: Theme.of(context).primaryColor, | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class DailyTitleText extends StatelessWidget { | ||||
|   const DailyTitleText({ | ||||
|     Key? key, | ||||
|     required this.isoDate, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   final String isoDate; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     var currentYear = DateTime.now().year; | ||||
|     var groupYear = DateTime.parse(isoDate).year; | ||||
|     var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy'; | ||||
|     var dateText = DateFormat(formatDateTemplate).format(DateTime.parse(isoDate)); | ||||
|  | ||||
|     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( | ||||
|                 dateText, | ||||
|                 style: const TextStyle( | ||||
|                   fontSize: 14, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                   color: Colors.black87, | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user