opt search panel

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-05-18 17:27:40 +08:00
parent e330359192
commit 0de2603e30
8 changed files with 291 additions and 306 deletions

View File

@@ -2,7 +2,7 @@
enum SearchType { enum SearchType {
// all, // all,
// 视频video // 视频video
video('视频'), video('视频', hasHeader: true),
// 番剧media_bangumi, // 番剧media_bangumi,
media_bangumi('番剧'), media_bangumi('番剧'),
// 影视media_ft // 影视media_ft
@@ -16,14 +16,15 @@ enum SearchType {
// 话题topic // 话题topic
// topic, // topic,
// 用户bili_user // 用户bili_user
bili_user('用户'), bili_user('用户', hasHeader: true),
// 专栏article // 专栏article
article('专栏'); article('专栏', hasHeader: true);
// 相簿photo // 相簿photo
// photo // photo
final bool hasHeader;
final String label; final String label;
const SearchType(this.label); const SearchType(this.label, {this.hasHeader = false});
} }
// 搜索类型为视频、专栏及相簿时 // 搜索类型为视频、专栏及相簿时

View File

@@ -37,69 +37,64 @@ class _SearchAllPanelState
@override @override
Widget buildList(ThemeData theme, List<dynamic> list) { Widget buildList(ThemeData theme, List<dynamic> list) {
return SliverPadding( return SliverWaterfallFlow.extent(
padding: EdgeInsets.only( maxCrossAxisExtent: Grid.smallCardWidth * 2,
bottom: MediaQuery.paddingOf(context).bottom + 80, crossAxisSpacing: StyleString.safeSpace,
), lastChildLayoutTypeBuilder: (index) {
sliver: SliverWaterfallFlow.extent( if (index == list.length - 1) {
maxCrossAxisExtent: Grid.smallCardWidth * 2, controller.onLoadMore();
crossAxisSpacing: StyleString.safeSpace, }
lastChildLayoutTypeBuilder: (index) { return index == list.length
if (index == list.length - 1) { ? LastChildLayoutType.foot
controller.onLoadMore(); : LastChildLayoutType.none;
} },
return index == list.length children: list
? LastChildLayoutType.foot .map(
: LastChildLayoutType.none; (item) => switch (item) {
}, SearchVideoItemModel() => SizedBox(
children: list height: 120,
.map( child: VideoCardH(
(item) => switch (item) { videoItem: item,
SearchVideoItemModel() => SizedBox( showPubdate: true,
height: 120,
child: VideoCardH(
videoItem: item,
showPubdate: true,
),
), ),
List<SearchMBangumiItemModel>() => item.length == 1 ),
? SizedBox( List<SearchMBangumiItemModel>() => item.length == 1
height: 160, ? SizedBox(
child: SearchPgcItem(item: item.first), height: 160,
) child: SearchPgcItem(item: item.first),
: SizedBox( )
height: Grid.smallCardWidth / 2 / 0.75 + : SizedBox(
MediaQuery.textScalerOf(context).scale(60), height: Grid.smallCardWidth / 2 / 0.75 +
child: ListView.builder( MediaQuery.textScalerOf(context).scale(60),
padding: const EdgeInsets.only(bottom: 7), child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.only(bottom: 7),
scrollDirection: Axis.horizontal, physics: const AlwaysScrollableScrollPhysics(),
itemCount: item.length, scrollDirection: Axis.horizontal,
itemBuilder: (context, index) { itemCount: item.length,
return Container( itemBuilder: (context, index) {
width: Grid.smallCardWidth / 2, return Container(
margin: EdgeInsets.only( width: Grid.smallCardWidth / 2,
left: StyleString.safeSpace, margin: EdgeInsets.only(
right: index == item.length - 1 left: StyleString.safeSpace,
? StyleString.safeSpace right: index == item.length - 1
: 0, ? StyleString.safeSpace
), : 0,
child: BangumiCardVSearch(item: item[index]), ),
); 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(), ),
}, _ => const SizedBox.shrink(),
) },
.toList(), )
), .toList(),
); );
} }
} }

View File

@@ -32,77 +32,72 @@ class _SearchArticlePanelState extends CommonSearchPanelState<
); );
@override @override
Widget buildList(ThemeData theme, List<SearchArticleItemModel> list) { Widget buildHeader(ThemeData theme) {
return SliverMainAxisGroup( return SliverPersistentHeader(
slivers: [ pinned: false,
SliverPersistentHeader( floating: true,
pinned: false, delegate: CustomSliverPersistentHeaderDelegate(
floating: true, extent: 40,
delegate: CustomSliverPersistentHeaderDelegate( bgColor: theme.colorScheme.surface,
extent: 40, child: Container(
bgColor: theme.colorScheme.surface, height: 40,
child: Container( padding: const EdgeInsets.only(left: 25, right: 12),
height: 40, child: Row(
padding: const EdgeInsets.only(left: 25, right: 12), children: [
child: Row( Obx(
children: [ () => Text(
Obx( '排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}',
() => Text( maxLines: 1,
'排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}', style: TextStyle(color: theme.colorScheme.outline),
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,
),
),
),
],
), ),
), 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( @override
gridDelegate: Grid.videoCardHDelegate(context), Widget buildList(ThemeData theme, List<SearchArticleItemModel> list) {
delegate: SliverChildBuilderDelegate( return SliverGrid(
(BuildContext context, int index) { gridDelegate: Grid.videoCardHDelegate(context),
if (index == list.length - 1) { delegate: SliverChildBuilderDelegate(
controller.onLoadMore(); (BuildContext context, int index) {
} if (index == list.length - 1) {
return SearchArticleItem(item: list[index]); controller.onLoadMore();
}, }
childCount: list.length, return SearchArticleItem(item: list[index]);
), },
), childCount: list.length,
), ),
],
); );
} }
} }

View File

@@ -34,10 +34,9 @@ class _SearchLivePanelState extends CommonSearchPanelState<SearchLivePanel,
@override @override
Widget buildList(ThemeData theme, List<SearchLiveItemModel> list) { Widget buildList(ThemeData theme, List<SearchLiveItemModel> list) {
return SliverPadding( return SliverPadding(
padding: EdgeInsets.only( padding: const EdgeInsets.only(
left: StyleString.safeSpace, left: StyleString.safeSpace,
right: StyleString.safeSpace, right: StyleString.safeSpace,
bottom: MediaQuery.paddingOf(context).bottom + 80,
), ),
sliver: SliverGrid( sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio( gridDelegate: SliverGridDelegateWithExtentAndRatio(

View File

@@ -32,23 +32,19 @@ class _SearchPgcPanelState extends CommonSearchPanelState<SearchPgcPanel,
@override @override
Widget buildList(ThemeData theme, List<SearchMBangumiItemModel> list) { Widget buildList(ThemeData theme, List<SearchMBangumiItemModel> list) {
return SliverPadding( return SliverGrid(
padding: gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom + 80), maxCrossAxisExtent: Grid.smallCardWidth * 2,
sliver: SliverGrid( mainAxisExtent: 160,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( ),
maxCrossAxisExtent: Grid.smallCardWidth * 2, delegate: SliverChildBuilderDelegate(
mainAxisExtent: 160, (BuildContext context, int index) {
), if (index == list.length - 1) {
delegate: SliverChildBuilderDelegate( controller.onLoadMore();
(BuildContext context, int index) { }
if (index == list.length - 1) { return SearchPgcItem(item: list[index]);
controller.onLoadMore(); },
} childCount: list.length,
return SearchPgcItem(item: list[index]);
},
childCount: list.length,
),
), ),
); );
} }

View File

@@ -32,81 +32,77 @@ class _SearchUserPanelState extends CommonSearchPanelState<SearchUserPanel,
); );
@override @override
Widget buildList(ThemeData theme, List<SearchUserItemModel> list) { Widget buildHeader(ThemeData theme) {
return SliverMainAxisGroup( return SliverPersistentHeader(
slivers: [ pinned: false,
SliverPersistentHeader( floating: true,
pinned: false, delegate: CustomSliverPersistentHeaderDelegate(
floating: true, extent: 40,
delegate: CustomSliverPersistentHeaderDelegate( bgColor: theme.colorScheme.surface,
extent: 40, child: Container(
bgColor: theme.colorScheme.surface, height: 40,
child: Container( padding: const EdgeInsets.only(left: 25, right: 12),
height: 40, child: Row(
padding: const EdgeInsets.only(left: 25, right: 12), children: [
child: Row( Obx(
children: [ () => Text(
Obx( '排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}',
() => Text( maxLines: 1,
'排序: ${controller.orderFiltersList[controller.currentOrderFilterval.value]['label']}', style: TextStyle(color: theme.colorScheme.outline),
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,
),
),
),
],
), ),
), 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( @override
maxCrossAxisExtent: Grid.smallCardWidth * 2, Widget buildList(ThemeData theme, List<SearchUserItemModel> list) {
mainAxisExtent: 66, return SliverGrid(
), gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
delegate: SliverChildBuilderDelegate( maxCrossAxisExtent: Grid.smallCardWidth * 2,
(BuildContext context, int index) { mainAxisExtent: 66,
if (index == list.length - 1) { ),
controller.onLoadMore(); delegate: SliverChildBuilderDelegate(
} (BuildContext context, int index) {
return SearchUserItem( if (index == list.length - 1) {
item: list[index], controller.onLoadMore();
); }
}, return SearchUserItem(
childCount: list.length, item: list[index],
), );
), },
), childCount: list.length,
], ),
); );
} }
} }

View File

@@ -34,93 +34,89 @@ class _SearchVideoPanelState extends CommonSearchPanelState<SearchVideoPanel,
); );
@override @override
Widget buildList(ThemeData theme, List<SearchVideoItemModel> list) { Widget buildHeader(ThemeData theme) {
return SliverMainAxisGroup( return SliverPersistentHeader(
slivers: [ pinned: false,
SliverPersistentHeader( floating: true,
pinned: false, delegate: CustomSliverPersistentHeaderDelegate(
floating: true, extent: 34,
delegate: CustomSliverPersistentHeaderDelegate( bgColor: theme.colorScheme.surface,
extent: 34, child: Container(
bgColor: theme.colorScheme.surface, height: 34,
child: Container( padding: const EdgeInsets.symmetric(horizontal: 12),
height: 34, child: Row(
padding: const EdgeInsets.symmetric(horizontal: 12), children: [
child: Row( Expanded(
children: [ child: SingleChildScrollView(
Expanded( scrollDirection: Axis.horizontal,
child: SingleChildScrollView( child: Wrap(
scrollDirection: Axis.horizontal, children: [
child: Wrap( for (var i in controller.filterList) ...[
children: [ Obx(
for (var i in controller.filterList) ...[ () => SearchText(
Obx( fontSize: 13,
() => SearchText( text: i['label'],
fontSize: 13, bgColor: Colors.transparent,
text: i['label'], textColor:
bgColor: Colors.transparent, controller.selectedType.value == i['type']
textColor: ? theme.colorScheme.primary
controller.selectedType.value == i['type'] : theme.colorScheme.outline,
? theme.colorScheme.primary onTap: (value) async {
: theme.colorScheme.outline, controller.selectedType.value = i['type'];
onTap: (value) async { controller.order.value =
controller.selectedType.value = i['type']; i['type'].toString().split('.').last;
controller.order.value = SmartDialog.showLoading(msg: 'loading');
i['type'].toString().split('.').last; await controller.onReload();
SmartDialog.showLoading(msg: 'loading'); SmartDialog.dismiss();
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), @override
delegate: SliverChildBuilderDelegate( Widget buildList(ThemeData theme, List<SearchVideoItemModel> list) {
(context, index) { return SliverGrid(
if (index == list.length - 1) { gridDelegate: Grid.videoCardHDelegate(context),
controller.onLoadMore(); delegate: SliverChildBuilderDelegate(
} (context, index) {
return VideoCardH( if (index == list.length - 1) {
videoItem: list[index], controller.onLoadMore();
showPubdate: true, }
); return VideoCardH(
}, videoItem: list[index],
childCount: list.length, showPubdate: true,
), );
), },
), childCount: list.length,
], ),
); );
} }
} }

View File

@@ -45,6 +45,7 @@ abstract class CommonSearchPanelState<
controller: controller.scrollController, controller: controller.scrollController,
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
slivers: [ slivers: [
if (widget.searchType.hasHeader) buildHeader(theme),
Obx(() => _buildBody(theme, controller.loadingState.value)), Obx(() => _buildBody(theme, controller.loadingState.value)),
], ],
), ),
@@ -105,7 +106,11 @@ abstract class CommonSearchPanelState<
) )
: _builLoading, : _builLoading,
Success(:var response) => response?.isNotEmpty == true Success(:var response) => response?.isNotEmpty == true
? buildList(theme, response!) ? SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom + 80),
sliver: buildList(theme, response!),
)
: HttpError( : HttpError(
onReload: controller.onReload, onReload: controller.onReload,
), ),
@@ -116,5 +121,7 @@ abstract class CommonSearchPanelState<
}; };
} }
Widget buildHeader(ThemeData theme) => throw UnimplementedError();
Widget buildList(ThemeData theme, List<T> list); Widget buildList(ThemeData theme, List<T> list);
} }