refa: member fav

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-12-03 12:52:23 +08:00
parent 9c8e5b53e7
commit b7a277a57c
3 changed files with 255 additions and 137 deletions

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/http/loading_state.dart';
@@ -40,7 +41,7 @@ class _MemberFavoriteState extends State<MemberFavorite>
return refreshIndicator(
onRefresh: _controller.onRefresh,
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
physics: _FavScrollPhysics(controller: _controller),
slivers: [
SliverPadding(
padding: EdgeInsets.only(
@@ -55,7 +56,10 @@ class _MemberFavoriteState extends State<MemberFavorite>
);
}
Widget _buildBody(ThemeData theme, LoadingState loadingState) {
Widget _buildBody(
ThemeData theme,
LoadingState<List<SpaceFavData>?> loadingState,
) {
return switch (loadingState) {
Loading() => SliverPadding(
padding: const EdgeInsets.only(top: 7),
@@ -65,19 +69,21 @@ class _MemberFavoriteState extends State<MemberFavorite>
itemCount: 10,
),
),
Success(:var response) =>
(response as List?)?.isNotEmpty == true
Success(:final response) =>
response?.isNotEmpty == true
? SliverMainAxisGroup(
slivers: [
SliverToBoxAdapter(
child: Obx(
() => _buildItem(theme, _controller.first.value, true),
),
_buildItem(
theme,
data: _controller.favState,
isEnd: _controller.favEnd,
isFav: true,
),
SliverToBoxAdapter(
child: Obx(
() => _buildItem(theme, _controller.second.value, false),
),
_buildItem(
theme,
data: _controller.subState,
isEnd: _controller.subEnd,
isFav: false,
),
],
)
@@ -89,78 +95,159 @@ class _MemberFavoriteState extends State<MemberFavorite>
};
}
Theme _buildItem(ThemeData theme, SpaceFavData data, bool isFirst) {
return Theme(
data: theme.copyWith(
dividerColor: Colors.transparent,
),
child: ExpansionTile(
dense: true,
initiallyExpanded: true,
title: Text.rich(
TextSpan(
children: [
TextSpan(
text: data.name,
style: const TextStyle(fontSize: 14),
),
TextSpan(
text: ' ${data.mediaListResponse?.count}',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.outline,
),
),
],
),
),
controlAffinity: ListTileControlAffinity.leading,
children: [
...?data.mediaListResponse?.list?.map(
(item) => SizedBox(
height: 98,
child: MemberFavItem(
item: item,
callback: (res) {
if (res == true) {
_controller
..first.value.mediaListResponse?.list?.remove(item)
..first.refresh();
}
Widget _buildItem(
ThemeData theme, {
required Rx<SpaceFavData> data,
required RxBool isEnd,
required bool isFav,
}) {
return SliverMainAxisGroup(
slivers: [
SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(
child: Material(
color: theme.colorScheme.surface,
child: Builder(
builder: (context) {
return InkWell(
onTap: () {
_controller.setExpand(isFav);
(context as Element).markNeedsBuild();
data.refresh();
},
child: Container(
height: 45,
alignment: .centerLeft,
padding: const .only(left: 12),
child: Text.rich(
TextSpan(
children: [
WidgetSpan(
alignment: .middle,
child: Icon(
_controller.isExpand(isFav)
? Icons.expand_less
: Icons.expand_more,
color: theme.colorScheme.outline,
),
),
TextSpan(
text: ' ${data.value.name}',
style: const TextStyle(fontSize: 14),
),
TextSpan(
text: ' ${data.value.mediaListResponse?.count}',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.outline,
),
),
],
),
),
),
);
},
),
),
bgColor: null,
),
Obx(
() =>
(isFirst
? _controller.firstEnd.value
: _controller.secondEnd.value)
? const SizedBox.shrink()
: _buildLoadMoreItem(theme, isFirst),
),
],
),
),
Obx(() {
final list = data.value.mediaListResponse?.list;
if (!_controller.isExpand(isFav)) {
return const SliverToBoxAdapter();
}
final end = isEnd.value;
if (list != null && list.isNotEmpty) {
return SliverList.builder(
itemCount: list.length + (end ? 0 : 1),
itemBuilder: (context, index) {
if (!end && index == list.length) {
return Obx(
() => isEnd.value
? const SizedBox.shrink()
: _buildLoadMoreItem(theme, isFav),
);
}
final item = list[index];
return SizedBox(
height: 98,
child: MemberFavItem(
item: item,
callback: (res) {
if (res == true) {
_controller.favState
..value.mediaListResponse?.list?.remove(item)
..refresh();
}
},
),
);
},
);
}
return const SliverToBoxAdapter();
}),
],
);
}
ListTile _buildLoadMoreItem(ThemeData theme, bool isFirst) {
return ListTile(
dense: true,
onTap: () {
if (isFirst) {
_controller.userfavFolder();
} else {
_controller.userSubFolder();
}
},
title: Text(
'查看更多内容',
textAlign: TextAlign.center,
style: TextStyle(
color: theme.colorScheme.primary,
Widget _buildLoadMoreItem(ThemeData theme, bool isFav) {
return Padding(
padding: const .only(top: 7),
child: InkWell(
onTap: () {
if (isFav) {
_controller.userFavFolder();
} else {
_controller.userSubFolder();
}
},
child: Container(
height: 40,
alignment: .center,
child: Text(
'查看更多内容',
textAlign: TextAlign.center,
style: TextStyle(color: theme.colorScheme.primary),
),
),
),
);
}
}
class _FavScrollPhysics extends AlwaysScrollableScrollPhysics {
const _FavScrollPhysics({super.parent, required this.controller});
final MemberFavoriteCtr controller;
@override
_FavScrollPhysics applyTo(ScrollPhysics? ancestor) {
return _FavScrollPhysics(
parent: buildParent(ancestor),
controller: controller,
);
}
@override
double adjustPositionForNewDimensions({
required ScrollMetrics oldPosition,
required ScrollMetrics newPosition,
required bool isScrolling,
required double velocity,
}) {
if (controller.flag) {
controller.flag = false;
return 0;
}
return super.adjustPositionForNewDimensions(
oldPosition: oldPosition,
newPosition: newPosition,
isScrolling: isScrolling,
velocity: velocity,
);
}
}