This commit is contained in:
bggRGjQaUbCoE
2024-10-17 10:04:27 +08:00
parent 39b7670134
commit 45be8f558c
3 changed files with 258 additions and 218 deletions

View File

@@ -11,6 +11,7 @@ import 'package:PiliPalaX/pages/member/new/content/member_home/member_home.dart'
import 'package:PiliPalaX/pages/member/new/controller.dart';
import 'package:PiliPalaX/pages/member/new/widget/user_info_card.dart';
import 'package:PiliPalaX/pages/member/view.dart';
import 'package:PiliPalaX/pages/member_dynamics/view.dart';
import 'package:PiliPalaX/utils/utils.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/material.dart';
@@ -56,188 +57,62 @@ class _MemberPageNewState extends State<MemberPageNew>
resizeToAvoidBottomInset: false,
body: Obx(
() => _userController.loadingState.value is Success
? ExtendedNestedScrollView(
controller: _userController.scrollController,
onlyOneScrollInBody: true,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverOverlapAbsorber(
handle: ExtendedNestedScrollView
.sliverOverlapAbsorberHandleFor(context),
sliver: DynamicSliverAppBar(
leading: Padding(
padding: EdgeInsets.only(top: _top),
child: const BackButton(),
? LayoutBuilder(
builder: (_, constraints) {
if (constraints.maxHeight > constraints.maxWidth) {
return ExtendedNestedScrollView(
controller: _userController.scrollController,
onlyOneScrollInBody: true,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverOverlapAbsorber(
handle: ExtendedNestedScrollView
.sliverOverlapAbsorberHandleFor(context),
sliver: _buildAppBar(),
),
];
},
body: _userController.tab2 != null &&
_userController.tab2!.isNotEmpty
? LayoutBuilder(
builder: (context, _) {
return Padding(
padding: EdgeInsets.only(
top: ExtendedNestedScrollView
.sliverOverlapAbsorberHandleFor(
context)
.layoutExtent ??
0,
),
child: _buildBody,
);
},
)
: Center(child: const Text('EMPTY')),
);
} else {
return Row(
children: [
Expanded(
child: CustomScrollView(
slivers: [
_buildAppBar(false),
],
),
),
title: Obx(() =>
_userController.scrollRatio.value == 1 &&
_userController.username != null
? Padding(
padding: EdgeInsets.only(top: _top),
child: Text(_userController.username!),
)
: const SizedBox.shrink()),
pinned: true,
scrolledUnderElevation: 0,
flexibleSpace: _buildUserInfo(
_userController.loadingState.value),
bottom: _userController.tab2 != null &&
_userController.tab2!.isNotEmpty
? PreferredSize(
preferredSize: Size.fromHeight(48),
child: Material(
child: TabBar(
controller: _userController.tabController,
tabs: _userController.tabs,
// onTap: (index) {
// if (!_tabController.indexIsChanging) {
// _returnTopController.setIndex(index);
// }
// },
),
),
)
: null,
actions: [
Padding(
padding: EdgeInsets.only(top: _top),
child: IconButton(
tooltip: '搜索',
onPressed: () => Get.toNamed(
'/memberSearch?mid=$_mid&uname=${_userController.username}'),
icon: const Icon(Icons.search_outlined),
),
Expanded(
child: Column(
children: [
SizedBox(height: _top),
_buildTab,
Expanded(child: _buildBody),
],
),
Padding(
padding: EdgeInsets.only(top: _top),
child: PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (BuildContext context) =>
<PopupMenuEntry>[
if (_userController.ownerMid != _mid) ...[
PopupMenuItem(
onTap: () =>
_userController.blockUser(context),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.block, size: 19),
const SizedBox(width: 10),
Text(_userController.relation.value !=
-1
? '加入黑名单'
: '移除黑名单'),
],
),
)
],
PopupMenuItem(
onTap: () => _userController.shareUser(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.share_outlined,
size: 19),
const SizedBox(width: 10),
Text(_userController.ownerMid != _mid
? '分享UP主'
: '分享我的主页'),
],
),
),
if (_userController.ownerMid != null) ...[
const PopupMenuDivider(),
PopupMenuItem(
onTap: () {
showDialog(
context: context,
builder: (_) => AlertDialog(
clipBehavior: Clip.hardEdge,
contentPadding:
const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
content: ReportPanel(
name: _userController.username,
mid: _mid,
),
),
);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
size: 19,
color: Theme.of(context)
.colorScheme
.error,
),
const SizedBox(width: 10),
Text(
'举报',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.error),
),
],
),
),
],
],
),
),
const SizedBox(width: 4),
],
),
),
];
),
],
);
}
},
body: _userController.tab2 != null &&
_userController.tab2!.isNotEmpty
? LayoutBuilder(
builder: (context, _) {
return Padding(
padding: EdgeInsets.only(
top: ExtendedNestedScrollView
.sliverOverlapAbsorberHandleFor(
context)
.layoutExtent ??
0,
),
child: TabBarView(
controller: _userController.tabController,
children: _userController.tab2!.map((item) {
return switch (item.param!) {
'home' => MemberHome(heroTag: _heroTag),
'dynamic' => MemberDynamic(mid: _mid ?? -1),
'contribute' => Obx(
() => MemberContribute(
heroTag: _heroTag,
initialIndex: _userController
.contributeInitialIndex.value,
mid: _mid ?? -1,
),
),
'bangumi' => MemberBangumi(
heroTag: _heroTag,
mid: _mid ?? -1,
),
'favorite' => MemberFavorite(
heroTag: _heroTag,
mid: _mid ?? -1,
),
_ => Center(child: Text(item.title ?? '')),
};
}).toList(),
),
);
},
)
: Center(child: const Text('EMPTY')),
)
: Center(
child: _buildUserInfo(_userController.loadingState.value),
@@ -247,6 +122,150 @@ class _MemberPageNewState extends State<MemberPageNew>
);
}
Widget get _buildTab => TabBar(
controller: _userController.tabController,
tabs: _userController.tabs,
);
Widget get _buildBody => TabBarView(
controller: _userController.tabController,
children: _userController.tab2!.map((item) {
return switch (item.param!) {
'home' => MemberHome(heroTag: _heroTag),
// 'dynamic' => MemberDynamic(mid: _mid ?? -1),
'dynamic' => MemberDynamicsPage(mid: _mid),
'contribute' => Obx(
() => MemberContribute(
heroTag: _heroTag,
initialIndex: _userController.contributeInitialIndex.value,
mid: _mid ?? -1,
),
),
'bangumi' => MemberBangumi(
heroTag: _heroTag,
mid: _mid ?? -1,
),
'favorite' => MemberFavorite(
heroTag: _heroTag,
mid: _mid ?? -1,
),
_ => Center(child: Text(item.title ?? '')),
};
}).toList(),
);
Widget _buildAppBar([bool needTab = true]) => DynamicSliverAppBar(
leading: Padding(
padding: EdgeInsets.only(top: _top),
child: const BackButton(),
),
title: Obx(() => _userController.scrollRatio.value == 1 &&
_userController.username != null
? Padding(
padding: EdgeInsets.only(top: _top),
child: Text(_userController.username!),
)
: const SizedBox.shrink()),
pinned: true,
scrolledUnderElevation: 0,
flexibleSpace: _buildUserInfo(_userController.loadingState.value),
bottom: needTab &&
_userController.tab2 != null &&
_userController.tab2!.isNotEmpty
? PreferredSize(
preferredSize: Size.fromHeight(48),
child: Material(
child: _buildTab,
),
)
: null,
actions: [
Padding(
padding: EdgeInsets.only(top: _top),
child: IconButton(
tooltip: '搜索',
onPressed: () => Get.toNamed(
'/memberSearch?mid=$_mid&uname=${_userController.username}'),
icon: const Icon(Icons.search_outlined),
),
),
Padding(
padding: EdgeInsets.only(top: _top),
child: PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
if (_userController.ownerMid != _mid) ...[
PopupMenuItem(
onTap: () => _userController.blockUser(context),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.block, size: 19),
const SizedBox(width: 10),
Text(_userController.relation.value != -1
? '加入黑名单'
: '移除黑名单'),
],
),
)
],
PopupMenuItem(
onTap: () => _userController.shareUser(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.share_outlined, size: 19),
const SizedBox(width: 10),
Text(_userController.ownerMid != _mid
? '分享UP主'
: '分享我的主页'),
],
),
),
if (_userController.ownerMid != null) ...[
const PopupMenuDivider(),
PopupMenuItem(
onTap: () {
showDialog(
context: context,
builder: (_) => AlertDialog(
clipBehavior: Clip.hardEdge,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
content: ReportPanel(
name: _userController.username,
mid: _mid,
),
),
);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
size: 19,
color: Theme.of(context).colorScheme.error,
),
const SizedBox(width: 10),
Text(
'举报',
style: TextStyle(
color: Theme.of(context).colorScheme.error),
),
],
),
),
],
],
),
),
const SizedBox(width: 4),
],
);
Widget _errorWidget(msg) {
return CustomScrollView(
shrinkWrap: true,

View File

@@ -4,19 +4,14 @@ import 'package:PiliPalaX/http/member.dart';
import 'package:PiliPalaX/models/dynamics/result.dart';
class MemberDynamicsController extends GetxController {
MemberDynamicsController(this.mid);
final ScrollController scrollController = ScrollController();
late int mid;
int? mid;
String offset = '';
int count = 0;
bool hasMore = true;
RxList<DynamicItemModel> dynamicsList = <DynamicItemModel>[].obs;
@override
void onInit() {
super.onInit();
mid = int.parse(Get.parameters['mid']!);
}
Future getMemberDynamic(type) async {
if (type == 'onRefresh') {
offset = '';

View File

@@ -12,61 +12,75 @@ import '../dynamics/widgets/dynamic_panel.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
class MemberDynamicsPage extends StatefulWidget {
const MemberDynamicsPage({super.key});
const MemberDynamicsPage({super.key, this.mid});
final int? mid;
@override
State<MemberDynamicsPage> createState() => _MemberDynamicsPageState();
}
class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
class _MemberDynamicsPageState extends State<MemberDynamicsPage>
with AutomaticKeepAliveClientMixin {
late MemberDynamicsController _memberDynamicController;
late Future _futureBuilderFuture;
late int mid;
late bool dynamicsWaterfallFlow;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
mid = int.parse(Get.parameters['mid']!);
mid = widget.mid ?? int.parse(Get.parameters['mid']!);
final String heroTag = Utils.makeHeroTag(mid);
_memberDynamicController =
Get.put(MemberDynamicsController(), tag: heroTag);
Get.put(MemberDynamicsController(widget.mid), tag: heroTag);
_futureBuilderFuture =
_memberDynamicController.getMemberDynamic('onRefresh');
_memberDynamicController.scrollController.addListener(
() {
if (_memberDynamicController.scrollController.position.pixels >=
_memberDynamicController.scrollController.position.maxScrollExtent -
200) {
EasyThrottle.throttle(
'member_dynamics', const Duration(milliseconds: 1000), () {
_memberDynamicController.onLoad();
});
}
},
);
// _memberDynamicController.scrollController.addListener(
// () {
// if (_memberDynamicController.scrollController.position.pixels >=
// _memberDynamicController.scrollController.position.maxScrollExtent -
// 200) {
// EasyThrottle.throttle(
// 'member_dynamics', const Duration(milliseconds: 1000), () {
// _memberDynamicController.onLoad();
// });
// }
// },
// );
dynamicsWaterfallFlow = GStorage.setting
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
}
@override
void dispose() {
_memberDynamicController.scrollController.removeListener(() {});
// _memberDynamicController.scrollController.removeListener(() {});
_memberDynamicController.scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
centerTitle: false,
title: Text('Ta的动态', style: Theme.of(context).textTheme.titleMedium),
),
body: CustomScrollView(
super.build(context);
return widget.mid == null
? Scaffold(
appBar: AppBar(
titleSpacing: 0,
centerTitle: false,
title:
Text('Ta的动态', style: Theme.of(context).textTheme.titleMedium),
),
body: _buildBody,
)
: _buildBody;
}
Widget get _buildBody => CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: _memberDynamicController.scrollController,
// controller: _memberDynamicController.scrollController,
slivers: [
FutureBuilder(
future: _futureBuilderFuture,
@@ -89,6 +103,13 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == list.length - 1) {
EasyThrottle.throttle('member_dynamics',
const Duration(milliseconds: 1000),
() {
_memberDynamicController.onLoad();
});
}
return DynamicPanel(item: list[index]);
},
childCount: list.length,
@@ -113,10 +134,17 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
/// show no more item at trailing when children are not full of viewport
/// if children is full of viewport, it's the same as fullCrossAxisExtend
// LastChildLayoutType.foot,
lastChildLayoutTypeBuilder: (index) =>
index == list.length
? LastChildLayoutType.foot
: LastChildLayoutType.none,
lastChildLayoutTypeBuilder: (index) {
if (index == list.length - 1) {
EasyThrottle.throttle('member_dynamics',
const Duration(milliseconds: 1000), () {
_memberDynamicController.onLoad();
});
}
return index == list.length
? LastChildLayoutType.foot
: LastChildLayoutType.none;
},
children: [
for (var i in list) DynamicPanel(item: i),
]);
@@ -139,7 +167,5 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
},
),
],
),
);
}
);
}