opt: search page

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-04-17 10:32:42 +08:00
parent 5959288491
commit a99fc8fa72
5 changed files with 184 additions and 197 deletions

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/widgets/loading_widget.dart';
import 'package:PiliPlus/common/widgets/http_error.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/common_search_controller.dart';
import 'package:flutter/material.dart';
@@ -48,19 +48,25 @@ abstract class CommonSearchPageState<S extends CommonSearchPage, R, T>
onSubmitted: (value) => controller.onRefresh(),
),
),
body: Obx(() => _buildBody(controller.loadingState.value)),
body: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController,
slivers: [
Obx(() => _buildBody(controller.loadingState.value)),
],
),
);
}
Widget _buildBody(LoadingState<List<T>?> loadingState) {
return switch (loadingState) {
Loading() => errorWidget(),
Loading() => HttpError(),
Success() => loadingState.response?.isNotEmpty == true
? buildList(loadingState.response!)
: errorWidget(
: HttpError(
callback: controller.onReload,
),
Error() => errorWidget(
Error() => HttpError(
errMsg: loadingState.errMsg,
callback: controller.onReload,
),

View File

@@ -26,56 +26,50 @@ class _FavSearchPageState extends CommonSearchPageState<FavSearchPage,
@override
Widget buildList(List<FavDetailItemData> list) {
return CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController,
slivers: [
SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 80,
),
sliver: SliverGrid(
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
delegate: SliverChildBuilderDelegate(
childCount: list.length,
(context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
final item = list[index];
return FavVideoCardH(
videoItem: item,
onDelFav: controller.isOwner == true
? () {
controller.onCancelFav(
index,
item.id!,
item.type,
);
}
: null,
onViewFav: () {
PageUtils.toVideoPage(
'bvid=${item.bvid}&cid=${item.cid}',
arguments: {
'videoItem': item,
'heroTag': Utils.makeHeroTag(item.bvid),
'sourceType': 'fav',
'mediaId': controller.mediaId,
'oid': item.id,
'favTitle': controller.title,
'count': controller.count,
'desc': true,
'isContinuePlaying': true,
},
);
return SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 80,
),
sliver: SliverGrid(
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
delegate: SliverChildBuilderDelegate(
childCount: list.length,
(context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
final item = list[index];
return FavVideoCardH(
videoItem: item,
onDelFav: controller.isOwner == true
? () {
controller.onCancelFav(
index,
item.id!,
item.type,
);
}
: null,
onViewFav: () {
PageUtils.toVideoPage(
'bvid=${item.bvid}&cid=${item.cid}',
arguments: {
'videoItem': item,
'heroTag': Utils.makeHeroTag(item.bvid),
'sourceType': 'fav',
'mediaId': controller.mediaId,
'oid': item.id,
'favTitle': controller.title,
'count': controller.count,
'desc': true,
'isContinuePlaying': true,
},
);
},
),
),
);
},
),
],
),
);
}
}

View File

@@ -24,18 +24,19 @@ class _FollowSearchPageState extends CommonSearchPageState<FollowSearchPage,
@override
Widget buildList(List<FollowItemModel> list) {
return ListView.builder(
return SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 80,
),
controller: controller.scrollController,
itemCount: list.length,
itemBuilder: ((context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
return FollowItem(item: list[index]);
}),
sliver: SliverList.builder(
itemCount: list.length,
itemBuilder: ((context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
return FollowItem(item: list[index]);
}),
),
);
}
}

View File

@@ -25,36 +25,30 @@ class _HistorySearchPageState
@override
Widget buildList(List<HisListItem> list) {
return CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController,
slivers: [
SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 80,
),
sliver: SliverGrid(
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
delegate: SliverChildBuilderDelegate(
childCount: list.length,
(context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
final item = list[index];
return HistoryItem(
videoItem: item,
ctr: controller,
onChoose: null,
onDelete: (kid, business) {
controller.onDelHistory(index, kid, business);
},
);
return SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 80,
),
sliver: SliverGrid(
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
delegate: SliverChildBuilderDelegate(
childCount: list.length,
(context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
final item = list[index];
return HistoryItem(
videoItem: item,
ctr: controller,
onChoose: null,
onDelete: (kid, business) {
controller.onDelHistory(index, kid, business);
},
),
),
);
},
),
],
),
);
}
}

View File

@@ -28,125 +28,117 @@ class _LaterSearchPageState
@override
Widget buildList(List<HotVideoItemModel> list) {
return CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController,
slivers: [
SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 80,
),
sliver: SliverGrid(
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
delegate: SliverChildBuilderDelegate(
childCount: list.length,
(context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
final item = list[index];
return Stack(
children: [
VideoCardH(
videoItem: item,
source: 'later',
onViewLater: (cid) {
PageUtils.toVideoPage(
'bvid=${item.bvid}&cid=$cid',
arguments: {
'videoItem': item,
'oid': item.aid,
'heroTag': Utils.makeHeroTag(item.bvid),
'sourceType': 'watchLater',
'count': controller.count,
'favTitle': '稍后再看',
'mediaId': controller.mid,
'desc': false,
'isContinuePlaying': index != 0,
},
);
return SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 80,
),
sliver: SliverGrid(
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
delegate: SliverChildBuilderDelegate(
childCount: list.length,
(context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
final item = list[index];
return Stack(
children: [
VideoCardH(
videoItem: item,
source: 'later',
onViewLater: (cid) {
PageUtils.toVideoPage(
'bvid=${item.bvid}&cid=$cid',
arguments: {
'videoItem': item,
'oid': item.aid,
'heroTag': Utils.makeHeroTag(item.bvid),
'sourceType': 'watchLater',
'count': controller.count,
'favTitle': '稍后再看',
'mediaId': controller.mid,
'desc': false,
'isContinuePlaying': index != 0,
},
),
Positioned(
top: 5,
left: 12,
bottom: 5,
child: IgnorePointer(
child: LayoutBuilder(
builder: (context, constraints) => AnimatedOpacity(
opacity: item.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width: constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.black.withOpacity(0.6),
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding: WidgetStateProperty.all(
EdgeInsets.zero),
backgroundColor:
WidgetStateProperty.resolveWith(
(states) {
return Theme.of(context)
.colorScheme
.surface
.withOpacity(0.8);
},
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color:
Theme.of(context).colorScheme.primary,
),
);
},
),
Positioned(
top: 5,
left: 12,
bottom: 5,
child: IgnorePointer(
child: LayoutBuilder(
builder: (context, constraints) => AnimatedOpacity(
opacity: item.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight * StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.black.withOpacity(0.6),
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding:
WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor:
WidgetStateProperty.resolveWith(
(states) {
return Theme.of(context)
.colorScheme
.surface
.withOpacity(0.8);
},
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color: Theme.of(context).colorScheme.primary,
),
),
),
),
),
),
),
Positioned(
right: 12,
bottom: 0,
child: iconButton(
tooltip: '移除',
context: context,
onPressed: () {
controller.toViewDel(
context,
index,
item.aid,
);
},
icon: Icons.clear,
iconColor:
Theme.of(context).colorScheme.onSurfaceVariant,
bgColor: Colors.transparent,
),
),
],
);
},
),
),
),
),
Positioned(
right: 12,
bottom: 0,
child: iconButton(
tooltip: '移除',
context: context,
onPressed: () {
controller.toViewDel(
context,
index,
item.aid,
);
},
icon: Icons.clear,
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
bgColor: Colors.transparent,
),
),
],
);
},
),
],
),
);
}
}