From 0de2603e303796a33ee4361d1e104ec51a2d5d5d Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Sun, 18 May 2025 17:27:40 +0800 Subject: [PATCH] opt search panel Signed-off-by: bggRGjQaUbCoE --- lib/models/common/search_type.dart | 9 +- lib/pages/search_panel/all/view.dart | 113 ++++++++-------- lib/pages/search_panel/article/view.dart | 131 +++++++++--------- lib/pages/search_panel/live/view.dart | 3 +- lib/pages/search_panel/pgc/view.dart | 30 ++--- lib/pages/search_panel/user/view.dart | 140 ++++++++++---------- lib/pages/search_panel/video/view.dart | 162 +++++++++++------------ lib/pages/search_panel/view.dart | 9 +- 8 files changed, 291 insertions(+), 306 deletions(-) diff --git a/lib/models/common/search_type.dart b/lib/models/common/search_type.dart index da82db27..51363583 100644 --- a/lib/models/common/search_type.dart +++ b/lib/models/common/search_type.dart @@ -2,7 +2,7 @@ enum SearchType { // all, // 视频:video - video('视频'), + video('视频', hasHeader: true), // 番剧:media_bangumi, media_bangumi('番剧'), // 影视:media_ft @@ -16,14 +16,15 @@ enum SearchType { // 话题:topic // topic, // 用户:bili_user - bili_user('用户'), + bili_user('用户', hasHeader: true), // 专栏:article - article('专栏'); + article('专栏', hasHeader: true); // 相簿:photo // photo + final bool hasHeader; final String label; - const SearchType(this.label); + const SearchType(this.label, {this.hasHeader = false}); } // 搜索类型为视频、专栏及相簿时 diff --git a/lib/pages/search_panel/all/view.dart b/lib/pages/search_panel/all/view.dart index 67d1c03d..4e00496c 100644 --- a/lib/pages/search_panel/all/view.dart +++ b/lib/pages/search_panel/all/view.dart @@ -37,69 +37,64 @@ class _SearchAllPanelState @override Widget buildList(ThemeData theme, List list) { - return SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: SliverWaterfallFlow.extent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - crossAxisSpacing: StyleString.safeSpace, - lastChildLayoutTypeBuilder: (index) { - if (index == list.length - 1) { - controller.onLoadMore(); - } - return index == list.length - ? LastChildLayoutType.foot - : LastChildLayoutType.none; - }, - children: list - .map( - (item) => switch (item) { - SearchVideoItemModel() => SizedBox( - height: 120, - child: VideoCardH( - videoItem: item, - showPubdate: true, - ), + return SliverWaterfallFlow.extent( + maxCrossAxisExtent: Grid.smallCardWidth * 2, + crossAxisSpacing: StyleString.safeSpace, + lastChildLayoutTypeBuilder: (index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + return index == list.length + ? LastChildLayoutType.foot + : LastChildLayoutType.none; + }, + children: list + .map( + (item) => switch (item) { + SearchVideoItemModel() => SizedBox( + height: 120, + child: VideoCardH( + videoItem: item, + showPubdate: true, ), - List() => item.length == 1 - ? SizedBox( - height: 160, - child: SearchPgcItem(item: item.first), - ) - : SizedBox( - height: Grid.smallCardWidth / 2 / 0.75 + - MediaQuery.textScalerOf(context).scale(60), - child: ListView.builder( - padding: const EdgeInsets.only(bottom: 7), - physics: const AlwaysScrollableScrollPhysics(), - scrollDirection: Axis.horizontal, - itemCount: item.length, - itemBuilder: (context, index) { - return Container( - width: Grid.smallCardWidth / 2, - margin: EdgeInsets.only( - left: StyleString.safeSpace, - right: index == item.length - 1 - ? StyleString.safeSpace - : 0, - ), - child: BangumiCardVSearch(item: item[index]), - ); - }, - ), + ), + List() => item.length == 1 + ? SizedBox( + height: 160, + child: SearchPgcItem(item: item.first), + ) + : SizedBox( + height: Grid.smallCardWidth / 2 / 0.75 + + MediaQuery.textScalerOf(context).scale(60), + child: ListView.builder( + padding: const EdgeInsets.only(bottom: 7), + physics: const AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.horizontal, + itemCount: item.length, + itemBuilder: (context, index) { + return Container( + width: Grid.smallCardWidth / 2, + margin: EdgeInsets.only( + left: StyleString.safeSpace, + right: index == item.length - 1 + ? StyleString.safeSpace + : 0, + ), + child: BangumiCardVSearch(item: item[index]), + ); + }, ), - SearchUserItemModel() => Padding( - padding: const EdgeInsets.only(bottom: 5), - child: SearchUserItem( - item: item, ), + SearchUserItemModel() => Padding( + padding: const EdgeInsets.only(bottom: 5), + child: SearchUserItem( + item: item, ), - _ => const SizedBox.shrink(), - }, - ) - .toList(), - ), + ), + _ => const SizedBox.shrink(), + }, + ) + .toList(), ); } } diff --git a/lib/pages/search_panel/article/view.dart b/lib/pages/search_panel/article/view.dart index 821c15ca..3005d63c 100644 --- a/lib/pages/search_panel/article/view.dart +++ b/lib/pages/search_panel/article/view.dart @@ -32,77 +32,72 @@ class _SearchArticlePanelState extends CommonSearchPanelState< ); @override - Widget buildList(ThemeData theme, List list) { - return SliverMainAxisGroup( - slivers: [ - SliverPersistentHeader( - pinned: false, - floating: true, - delegate: CustomSliverPersistentHeaderDelegate( - extent: 40, - bgColor: theme.colorScheme.surface, - child: Container( - height: 40, - padding: const EdgeInsets.only(left: 25, right: 12), - child: Row( - children: [ - Obx( - () => Text( - '排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}', - maxLines: 1, - style: TextStyle(color: theme.colorScheme.outline), - ), - ), - const Spacer(), - Obx( - () => Text( - '分区: ${controller.zoneFiltersList[controller.currentZoneFilterval.value]['label']}', - maxLines: 1, - style: TextStyle(color: theme.colorScheme.outline), - ), - ), - const Spacer(), - SizedBox( - width: 32, - height: 32, - child: IconButton( - tooltip: '筛选', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - controller.onShowFilterDialog(context); - }, - icon: Icon( - Icons.filter_list_outlined, - size: 18, - color: theme.colorScheme.primary, - ), - ), - ), - ], + Widget buildHeader(ThemeData theme) { + return SliverPersistentHeader( + pinned: false, + floating: true, + delegate: CustomSliverPersistentHeaderDelegate( + extent: 40, + bgColor: theme.colorScheme.surface, + child: Container( + height: 40, + padding: const EdgeInsets.only(left: 25, right: 12), + child: Row( + children: [ + Obx( + () => Text( + '排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}', + maxLines: 1, + style: TextStyle(color: theme.colorScheme.outline), + ), ), - ), + const Spacer(), + Obx( + () => Text( + '分区: ${controller.zoneFiltersList[controller.currentZoneFilterval.value]['label']}', + maxLines: 1, + style: TextStyle(color: theme.colorScheme.outline), + ), + ), + const Spacer(), + SizedBox( + width: 32, + height: 32, + child: IconButton( + tooltip: '筛选', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + controller.onShowFilterDialog(context); + }, + icon: Icon( + Icons.filter_list_outlined, + size: 18, + color: theme.colorScheme.primary, + ), + ), + ), + ], ), ), - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: SliverGrid( - gridDelegate: Grid.videoCardHDelegate(context), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (index == list.length - 1) { - controller.onLoadMore(); - } - return SearchArticleItem(item: list[index]); - }, - childCount: list.length, - ), - ), - ), - ], + ), + ); + } + + @override + Widget buildList(ThemeData theme, List list) { + return SliverGrid( + gridDelegate: Grid.videoCardHDelegate(context), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + return SearchArticleItem(item: list[index]); + }, + childCount: list.length, + ), ); } } diff --git a/lib/pages/search_panel/live/view.dart b/lib/pages/search_panel/live/view.dart index 5c4c2b90..393b6755 100644 --- a/lib/pages/search_panel/live/view.dart +++ b/lib/pages/search_panel/live/view.dart @@ -34,10 +34,9 @@ class _SearchLivePanelState extends CommonSearchPanelState list) { return SliverPadding( - padding: EdgeInsets.only( + padding: const EdgeInsets.only( left: StyleString.safeSpace, right: StyleString.safeSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, ), sliver: SliverGrid( gridDelegate: SliverGridDelegateWithExtentAndRatio( diff --git a/lib/pages/search_panel/pgc/view.dart b/lib/pages/search_panel/pgc/view.dart index 7ab1656f..3b3ca81d 100644 --- a/lib/pages/search_panel/pgc/view.dart +++ b/lib/pages/search_panel/pgc/view.dart @@ -32,23 +32,19 @@ class _SearchPgcPanelState extends CommonSearchPanelState list) { - return SliverPadding( - padding: - EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom + 80), - sliver: SliverGrid( - gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - mainAxisExtent: 160, - ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (index == list.length - 1) { - controller.onLoadMore(); - } - return SearchPgcItem(item: list[index]); - }, - childCount: list.length, - ), + return SliverGrid( + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: Grid.smallCardWidth * 2, + mainAxisExtent: 160, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + return SearchPgcItem(item: list[index]); + }, + childCount: list.length, ), ); } diff --git a/lib/pages/search_panel/user/view.dart b/lib/pages/search_panel/user/view.dart index 57924009..83b907a7 100644 --- a/lib/pages/search_panel/user/view.dart +++ b/lib/pages/search_panel/user/view.dart @@ -32,81 +32,77 @@ class _SearchUserPanelState extends CommonSearchPanelState list) { - return SliverMainAxisGroup( - slivers: [ - SliverPersistentHeader( - pinned: false, - floating: true, - delegate: CustomSliverPersistentHeaderDelegate( - extent: 40, - bgColor: theme.colorScheme.surface, - child: Container( - height: 40, - padding: const EdgeInsets.only(left: 25, right: 12), - child: Row( - children: [ - Obx( - () => Text( - '排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}', - maxLines: 1, - style: TextStyle(color: theme.colorScheme.outline), - ), - ), - const Spacer(), - Obx( - () => Text( - '用户类型: ${controller.userTypeFiltersList[controller.currentUserTypeFilterval.value]['label']}', - maxLines: 1, - style: TextStyle(color: theme.colorScheme.outline), - ), - ), - const Spacer(), - SizedBox( - width: 32, - height: 32, - child: IconButton( - tooltip: '筛选', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - controller.onShowFilterDialog(context); - }, - icon: Icon( - Icons.filter_list_outlined, - size: 18, - color: theme.colorScheme.primary, - ), - ), - ), - ], + Widget buildHeader(ThemeData theme) { + return SliverPersistentHeader( + pinned: false, + floating: true, + delegate: CustomSliverPersistentHeaderDelegate( + extent: 40, + bgColor: theme.colorScheme.surface, + child: Container( + height: 40, + padding: const EdgeInsets.only(left: 25, right: 12), + child: Row( + children: [ + Obx( + () => Text( + '排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}', + maxLines: 1, + style: TextStyle(color: theme.colorScheme.outline), + ), ), - ), + const Spacer(), + Obx( + () => Text( + '用户类型: ${controller.userTypeFiltersList[controller.currentUserTypeFilterval.value]['label']}', + maxLines: 1, + style: TextStyle(color: theme.colorScheme.outline), + ), + ), + const Spacer(), + SizedBox( + width: 32, + height: 32, + child: IconButton( + tooltip: '筛选', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + controller.onShowFilterDialog(context); + }, + icon: Icon( + Icons.filter_list_outlined, + size: 18, + color: theme.colorScheme.primary, + ), + ), + ), + ], ), ), - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80), - sliver: SliverGrid( - gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - mainAxisExtent: 66, - ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (index == list.length - 1) { - controller.onLoadMore(); - } - return SearchUserItem( - item: list[index], - ); - }, - childCount: list.length, - ), - ), - ), - ], + ), + ); + } + + @override + Widget buildList(ThemeData theme, List list) { + return SliverGrid( + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: Grid.smallCardWidth * 2, + mainAxisExtent: 66, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + return SearchUserItem( + item: list[index], + ); + }, + childCount: list.length, + ), ); } } diff --git a/lib/pages/search_panel/video/view.dart b/lib/pages/search_panel/video/view.dart index 671cc4ee..8eabce64 100644 --- a/lib/pages/search_panel/video/view.dart +++ b/lib/pages/search_panel/video/view.dart @@ -34,93 +34,89 @@ class _SearchVideoPanelState extends CommonSearchPanelState list) { - return SliverMainAxisGroup( - slivers: [ - SliverPersistentHeader( - pinned: false, - floating: true, - delegate: CustomSliverPersistentHeaderDelegate( - extent: 34, - bgColor: theme.colorScheme.surface, - child: Container( - height: 34, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - children: [ - Expanded( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Wrap( - children: [ - for (var i in controller.filterList) ...[ - Obx( - () => SearchText( - fontSize: 13, - text: i['label'], - bgColor: Colors.transparent, - textColor: - controller.selectedType.value == i['type'] - ? theme.colorScheme.primary - : theme.colorScheme.outline, - onTap: (value) async { - controller.selectedType.value = i['type']; - controller.order.value = - i['type'].toString().split('.').last; - SmartDialog.showLoading(msg: 'loading'); - await controller.onReload(); - SmartDialog.dismiss(); - }, - ), - ), - ] - ], - ), - ), + Widget buildHeader(ThemeData theme) { + return SliverPersistentHeader( + pinned: false, + floating: true, + delegate: CustomSliverPersistentHeaderDelegate( + extent: 34, + bgColor: theme.colorScheme.surface, + child: Container( + height: 34, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Wrap( + children: [ + for (var i in controller.filterList) ...[ + Obx( + () => SearchText( + fontSize: 13, + text: i['label'], + bgColor: Colors.transparent, + textColor: + controller.selectedType.value == i['type'] + ? theme.colorScheme.primary + : theme.colorScheme.outline, + onTap: (value) async { + controller.selectedType.value = i['type']; + controller.order.value = + i['type'].toString().split('.').last; + SmartDialog.showLoading(msg: 'loading'); + await controller.onReload(); + SmartDialog.dismiss(); + }, + ), + ), + ] + ], ), - const VerticalDivider(indent: 7, endIndent: 8), - const SizedBox(width: 3), - SizedBox( - width: 32, - height: 32, - child: IconButton( - tooltip: '筛选', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => controller.onShowFilterDialog(context), - icon: Icon( - Icons.filter_list_outlined, - size: 18, - color: theme.colorScheme.primary, - ), - ), - ), - ], + ), ), - ), + const VerticalDivider(indent: 7, endIndent: 8), + const SizedBox(width: 3), + SizedBox( + width: 32, + height: 32, + child: IconButton( + tooltip: '筛选', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => controller.onShowFilterDialog(context), + icon: Icon( + Icons.filter_list_outlined, + size: 18, + color: theme.colorScheme.primary, + ), + ), + ), + ], ), ), - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80), - sliver: SliverGrid( - gridDelegate: Grid.videoCardHDelegate(context), - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == list.length - 1) { - controller.onLoadMore(); - } - return VideoCardH( - videoItem: list[index], - showPubdate: true, - ); - }, - childCount: list.length, - ), - ), - ), - ], + ), + ); + } + + @override + Widget buildList(ThemeData theme, List list) { + return SliverGrid( + gridDelegate: Grid.videoCardHDelegate(context), + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + return VideoCardH( + videoItem: list[index], + showPubdate: true, + ); + }, + childCount: list.length, + ), ); } } diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index d3378c8a..7947b9c3 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -45,6 +45,7 @@ abstract class CommonSearchPanelState< controller: controller.scrollController, physics: const AlwaysScrollableScrollPhysics(), slivers: [ + if (widget.searchType.hasHeader) buildHeader(theme), Obx(() => _buildBody(theme, controller.loadingState.value)), ], ), @@ -105,7 +106,11 @@ abstract class CommonSearchPanelState< ) : _builLoading, Success(:var response) => response?.isNotEmpty == true - ? buildList(theme, response!) + ? SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80), + sliver: buildList(theme, response!), + ) : HttpError( onReload: controller.onReload, ), @@ -116,5 +121,7 @@ abstract class CommonSearchPanelState< }; } + Widget buildHeader(ThemeData theme) => throw UnimplementedError(); + Widget buildList(ThemeData theme, List list); }