mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	ok
This commit is contained in:
		| @@ -0,0 +1,107 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:collection/collection.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
|  | ||||
| class SearchPageState { | ||||
|   final String searchTerm; | ||||
|   final bool isSearchEnabled; | ||||
|   final List<String> searchSuggestion; | ||||
|  | ||||
|   SearchPageState({ | ||||
|     required this.searchTerm, | ||||
|     required this.isSearchEnabled, | ||||
|     required this.searchSuggestion, | ||||
|   }); | ||||
|  | ||||
|   SearchPageState copyWith({ | ||||
|     String? searchTerm, | ||||
|     bool? isSearchEnabled, | ||||
|     List<String>? searchSuggestion, | ||||
|   }) { | ||||
|     return SearchPageState( | ||||
|       searchTerm: searchTerm ?? this.searchTerm, | ||||
|       isSearchEnabled: isSearchEnabled ?? this.isSearchEnabled, | ||||
|       searchSuggestion: searchSuggestion ?? this.searchSuggestion, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Map<String, dynamic> toMap() { | ||||
|     return { | ||||
|       'searchTerm': searchTerm, | ||||
|       'isSearchEnabled': isSearchEnabled, | ||||
|       'searchSuggestion': searchSuggestion, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   factory SearchPageState.fromMap(Map<String, dynamic> map) { | ||||
|     return SearchPageState( | ||||
|       searchTerm: map['searchTerm'] ?? '', | ||||
|       isSearchEnabled: map['isSearchEnabled'] ?? false, | ||||
|       searchSuggestion: List<String>.from(map['searchSuggestion']), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   String toJson() => json.encode(toMap()); | ||||
|  | ||||
|   factory SearchPageState.fromJson(String source) => SearchPageState.fromMap(json.decode(source)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => | ||||
|       'SearchPageState(searchTerm: $searchTerm, isSearchEnabled: $isSearchEnabled, searchSuggestion: $searchSuggestion)'; | ||||
|  | ||||
|   @override | ||||
|   bool operator ==(Object other) { | ||||
|     if (identical(this, other)) return true; | ||||
|     final listEquals = const DeepCollectionEquality().equals; | ||||
|  | ||||
|     return other is SearchPageState && | ||||
|         other.searchTerm == searchTerm && | ||||
|         other.isSearchEnabled == isSearchEnabled && | ||||
|         listEquals(other.searchSuggestion, searchSuggestion); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   int get hashCode => searchTerm.hashCode ^ isSearchEnabled.hashCode ^ searchSuggestion.hashCode; | ||||
| } | ||||
|  | ||||
| class SearchPageStateNotifier extends StateNotifier<SearchPageState> { | ||||
|   SearchPageStateNotifier() | ||||
|       : super( | ||||
|           SearchPageState( | ||||
|             searchTerm: "", | ||||
|             isSearchEnabled: false, | ||||
|             searchSuggestion: [], | ||||
|           ), | ||||
|         ); | ||||
|  | ||||
|   void enableSearch() { | ||||
|     state = state.copyWith(isSearchEnabled: true); | ||||
|   } | ||||
|  | ||||
|   void disableSearch() { | ||||
|     state = state.copyWith(isSearchEnabled: false); | ||||
|   } | ||||
|  | ||||
|   void setSearchTerm(String value) { | ||||
|     state = state.copyWith(searchTerm: value); | ||||
|  | ||||
|     _getSearchSuggestion(state.searchTerm); | ||||
|   } | ||||
|  | ||||
|   void _getSearchSuggestion(String searchTerm) { | ||||
|     var searchList = ['January', '01 2022', 'feburary', "February", 'home', '3413']; | ||||
|  | ||||
|     var newList = searchList.where((e) => e.toLowerCase().contains(searchTerm)); | ||||
|  | ||||
|     state = state.copyWith(searchSuggestion: [...newList]); | ||||
|  | ||||
|     if (searchTerm.isEmpty) { | ||||
|       state = state.copyWith(searchSuggestion: []); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| final searchPageStateProvider = StateNotifierProvider<SearchPageStateNotifier, SearchPageState>((ref) { | ||||
|   return SearchPageStateNotifier(); | ||||
| }); | ||||
							
								
								
									
										55
									
								
								mobile/lib/modules/search/ui/search_bar.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								mobile/lib/modules/search/ui/search_bar.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; | ||||
|  | ||||
| class SearchBar extends HookConsumerWidget with PreferredSizeWidget { | ||||
|   SearchBar({Key? key, required this.searchFocusNode}) : super(key: key); | ||||
|   FocusNode searchFocusNode; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final searchTermController = useTextEditingController(text: ""); | ||||
|     final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled; | ||||
|  | ||||
|     return AppBar( | ||||
|       automaticallyImplyLeading: false, | ||||
|       leading: isSearchEnabled | ||||
|           ? IconButton( | ||||
|               onPressed: () { | ||||
|                 searchFocusNode.unfocus(); | ||||
|                 ref.watch(searchPageStateProvider.notifier).disableSearch(); | ||||
|               }, | ||||
|               icon: const Icon(Icons.arrow_back_ios_rounded)) | ||||
|           : const Icon(Icons.search_rounded), | ||||
|       title: TextField( | ||||
|         controller: searchTermController, | ||||
|         focusNode: searchFocusNode, | ||||
|         autofocus: false, | ||||
|         onTap: () { | ||||
|           ref.watch(searchPageStateProvider.notifier).enableSearch(); | ||||
|           searchFocusNode.requestFocus(); | ||||
|         }, | ||||
|         onSubmitted: (searchTerm) { | ||||
|           ref.watch(searchPageStateProvider.notifier).disableSearch(); | ||||
|           searchFocusNode.unfocus(); | ||||
|         }, | ||||
|         onChanged: (value) { | ||||
|           ref.watch(searchPageStateProvider.notifier).setSearchTerm(value); | ||||
|         }, | ||||
|         decoration: const InputDecoration( | ||||
|           hintText: 'Search your photos', | ||||
|           enabledBorder: UnderlineInputBorder( | ||||
|             borderSide: BorderSide(color: Colors.transparent), | ||||
|           ), | ||||
|           focusedBorder: UnderlineInputBorder( | ||||
|             borderSide: BorderSide(color: Colors.transparent), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Size get preferredSize => const Size.fromHeight(kToolbarHeight); | ||||
| } | ||||
							
								
								
									
										35
									
								
								mobile/lib/modules/search/ui/search_suggestion_list.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								mobile/lib/modules/search/ui/search_suggestion_list.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; | ||||
|  | ||||
| class SearchSuggestionList extends ConsumerWidget { | ||||
|   const SearchSuggestionList({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final searchTerm = ref.watch(searchPageStateProvider).searchTerm; | ||||
|     final searchSuggestion = ref.watch(searchPageStateProvider).searchSuggestion; | ||||
|  | ||||
|     return Container( | ||||
|       color: searchTerm.isEmpty ? Colors.black.withOpacity(0.5) : Theme.of(context).scaffoldBackgroundColor, | ||||
|       child: CustomScrollView( | ||||
|         slivers: [ | ||||
|           SliverFillRemaining( | ||||
|             hasScrollBody: true, | ||||
|             child: ListView.builder( | ||||
|               itemBuilder: ((context, index) { | ||||
|                 return ListTile( | ||||
|                   onTap: () { | ||||
|                     print("navigate to this search result: ${searchSuggestion[index]} "); | ||||
|                   }, | ||||
|                   title: Text(searchSuggestion[index]), | ||||
|                 ); | ||||
|               }), | ||||
|               itemCount: searchSuggestion.length, | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										68
									
								
								mobile/lib/modules/search/views/search_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								mobile/lib/modules/search/views/search_page.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; | ||||
| import 'package:immich_mobile/modules/search/ui/search_bar.dart'; | ||||
| import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart'; | ||||
|  | ||||
| // ignore: must_be_immutable | ||||
| class SearchPage extends HookConsumerWidget { | ||||
|   SearchPage({Key? key}) : super(key: key); | ||||
|  | ||||
|   late FocusNode searchFocusNode; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled; | ||||
|  | ||||
|     useEffect(() { | ||||
|       searchFocusNode = FocusNode(); | ||||
|       return () { | ||||
|         searchFocusNode.dispose(); | ||||
|       }; | ||||
|     }, []); | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: SearchBar(searchFocusNode: searchFocusNode), | ||||
|       body: GestureDetector( | ||||
|         onTap: () { | ||||
|           searchFocusNode.unfocus(); | ||||
|           ref.watch(searchPageStateProvider.notifier).disableSearch(); | ||||
|         }, | ||||
|         child: Stack( | ||||
|           children: [ | ||||
|             ListView( | ||||
|               children: [ | ||||
|                 Container( | ||||
|                   height: 300, | ||||
|                   color: Colors.blue, | ||||
|                 ), | ||||
|                 Container( | ||||
|                   height: 300, | ||||
|                   color: Colors.red, | ||||
|                 ), | ||||
|                 Container( | ||||
|                   height: 300, | ||||
|                   color: Colors.green, | ||||
|                 ), | ||||
|                 Container( | ||||
|                   height: 300, | ||||
|                   color: Colors.blue, | ||||
|                 ), | ||||
|                 Container( | ||||
|                   height: 300, | ||||
|                   color: Colors.red, | ||||
|                 ), | ||||
|                 Container( | ||||
|                   height: 300, | ||||
|                   color: Colors.green, | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|             isSearchEnabled ? const SearchSuggestionList() : Container(), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user