diff --git a/lib/pages/collect/collect_controller.dart b/lib/pages/collect/collect_controller.dart index 063654b62..d801a12ca 100644 --- a/lib/pages/collect/collect_controller.dart +++ b/lib/pages/collect/collect_controller.dart @@ -35,6 +35,12 @@ abstract class _CollectController with Store { ObservableList collectibles = ObservableList(); + @observable + String searchText = ''; + + @observable + bool isSearching = false; + void loadCollectibles() { collectibles.clear(); collectibles.addAll(_collectCrudRepository.getAllCollectibles()); diff --git a/lib/pages/collect/collect_controller.g.dart b/lib/pages/collect/collect_controller.g.dart index 4ed5a1c48..983b584cb 100644 --- a/lib/pages/collect/collect_controller.g.dart +++ b/lib/pages/collect/collect_controller.g.dart @@ -25,6 +25,38 @@ mixin _$CollectController on _CollectController, Store { }); } + late final _$searchTextAtom = + Atom(name: '_CollectController.searchText', context: context); + + @override + String get searchText { + _$searchTextAtom.reportRead(); + return super.searchText; + } + + @override + set searchText(String value) { + _$searchTextAtom.reportWrite(value, super.searchText, () { + super.searchText = value; + }); + } + + late final _$isSearchingAtom = + Atom(name: '_CollectController.isSearching', context: context); + + @override + bool get isSearching { + _$isSearchingAtom.reportRead(); + return super.isSearching; + } + + @override + set isSearching(bool value) { + _$isSearchingAtom.reportWrite(value, super.isSearching, () { + super.isSearching = value; + }); + } + late final _$addCollectAsyncAction = AsyncAction('_CollectController.addCollect', context: context); @@ -46,7 +78,9 @@ mixin _$CollectController on _CollectController, Store { @override String toString() { return ''' -collectibles: ${collectibles} +collectibles: ${collectibles}, +searchText: ${searchText}, +isSearching: ${isSearching} '''; } } diff --git a/lib/pages/collect/collect_page.dart b/lib/pages/collect/collect_page.dart index 8aad24a39..4adbbcc42 100644 --- a/lib/pages/collect/collect_page.dart +++ b/lib/pages/collect/collect_page.dart @@ -25,10 +25,13 @@ class CollectPage extends StatefulWidget { class _CollectPageState extends State with SingleTickerProviderStateMixin { final CollectController collectController = Modular.get(); + final TextEditingController searchController = TextEditingController(); late NavigationBarState navigationBarState; TabController? tabController; bool showDelete = false; bool syncCollectiblesing = false; + bool searchBarHovering = false; + late final FocusNode _searchEntryFocusNode = FocusNode(); Future _syncBangumiWithProgress({ required GlobalKey<_FullSyncProgressDialogState> progressDialogKey, @@ -133,6 +136,7 @@ class _CollectPageState extends State } void onBackPressed(BuildContext context) { + collectController.searchText = ''; if (syncCollectiblesing) { return; } @@ -147,6 +151,7 @@ class _CollectPageState extends State @override void initState() { super.initState(); + searchController.text = collectController.searchText; collectController.loadCollectibles(); tabController = TabController(vsync: this, length: tabs.length); navigationBarState = @@ -156,6 +161,7 @@ class _CollectPageState extends State @override void dispose() { tabController?.dispose(); + searchController.dispose(); super.dispose(); } @@ -191,6 +197,16 @@ class _CollectPageState extends State ), title: const Text('追番'), actions: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + transitionBuilder: (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Observer(builder: (context) => Padding( + padding: const EdgeInsets.only(right: 8.0), + child: searchEntry + )), + ), IconButton( onPressed: () { setState(() { @@ -247,8 +263,8 @@ class _CollectPageState extends State : const Icon(Icons.sync_rounded), ), body: Observer(builder: (context) { - return renderBody; - }), + return renderBody; + }), ), ); } @@ -274,6 +290,12 @@ class _CollectPageState extends State collectedBangumiRenderItemList[element.type - 1].add(element); } for (List list in collectedBangumiRenderItemList) { + if (collectController.searchText.isNotEmpty) { + list.removeWhere((item) { + return !item.bangumiItem.nameCn.contains(collectController.searchText) && + !item.bangumiItem.name.contains(collectController.searchText); + }); + } list.sort((a, b) => b.time.millisecondsSinceEpoch .compareTo(a.time.millisecondsSinceEpoch)); } @@ -351,6 +373,102 @@ class _CollectPageState extends State } return gridViewList; } + + Widget get searchEntry { + if (collectController.isSearching) { + return TapRegion( + groupId: 'searchEntryTapRegion', + child: Observer(builder:(context) { + return SizedBox( + height: 48, + width: MediaQuery.sizeOf(context).width * 0.6, + child: SearchBar( + autoFocus: true, + focusNode: _searchEntryFocusNode, + controller: searchController, + hintText: '在收藏中搜索番剧喵~', + hintStyle: WidgetStateProperty.all( + Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + elevation: WidgetStateProperty.all(2.0), + shadowColor: WidgetStateProperty.all( + Theme.of(context).colorScheme.shadow, + ), + surfaceTintColor: WidgetStateProperty.all( + Theme.of(context).colorScheme.surfaceTint, + ), + padding: WidgetStateProperty.all( + const EdgeInsets.symmetric(horizontal: 4.0), + ), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(28.0), + ), + ), + leading: const Padding( + padding: EdgeInsets.only(left: 8.0), + child: Icon(Icons.search_rounded), + ), + trailing: [ + Observer(builder: (context) => searchEntryActionButton), + ], + onChanged: (value) { + collectController.searchText = value; + }, + )); + }), + onTapOutside: (_) { + collectController.isSearching = false; + }, + ); + } else { + IconButton searchButton = IconButton( + onPressed: () { + collectController.isSearching = true; + }, + icon: const Icon(Icons.search_rounded), + ); + + if (collectController.searchText.isNotEmpty) { + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Row( + children: [ + Text( + '搜索: ${collectController.searchText}', + style: Theme.of(context).textTheme.bodyMedium, + ), + searchButton, + ], + ), + ); + } else { + return searchButton; + } + } + } + + Widget get searchEntryActionButton { + if (collectController.searchText.isNotEmpty) { + return IconButton( + padding: const EdgeInsets.all(0), + alignment: Alignment.centerRight, + style: ButtonStyle( + overlayColor: WidgetStateProperty.all(Colors.transparent), + ), + onPressed: () { + searchController.clear(); + collectController.searchText = ''; + _searchEntryFocusNode.requestFocus(); + }, + icon: const Icon(Icons.close_rounded), + ); + } else { + return const SizedBox.shrink(); + } + } } class _FullSyncProgressDialog extends StatefulWidget {