mod: 评论刷新、翻页
This commit is contained in:
@ -1,22 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/reply.dart';
|
||||
import 'package:pilipala/models/video/reply/data.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
|
||||
class VideoReplyController extends GetxController {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
// 视频aid
|
||||
String aid = Get.parameters['aid']!;
|
||||
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
|
||||
// 当前页
|
||||
int currentPage = 0;
|
||||
bool isLoadingMore = false;
|
||||
bool noMore = false;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryReplyList();
|
||||
}
|
||||
|
||||
Future queryReplyList() async {
|
||||
var res = await ReplyHttp.replyList(oid: aid, pageNum: 1, type: 1);
|
||||
Future queryReplyList({type = 'init'}) async {
|
||||
isLoadingMore = true;
|
||||
var res =
|
||||
await ReplyHttp.replyList(oid: aid, pageNum: currentPage + 1, type: 1);
|
||||
if (res['status']) {
|
||||
res['data'] = ReplyData.fromJson(res['data']);
|
||||
if (res['data'].replies.isNotEmpty) {
|
||||
currentPage = currentPage + 1;
|
||||
noMore = false;
|
||||
} else {
|
||||
if (currentPage == 0) {
|
||||
} else {
|
||||
noMore = true;
|
||||
}
|
||||
}
|
||||
if (type == 'init') {
|
||||
List<ReplyItemModel> replies = res['data'].replies;
|
||||
// 添加置顶回复
|
||||
if (res['data'].upper.top != null) {
|
||||
bool flag = false;
|
||||
for (var i = 0; i < res['data'].topReplies.length; i++) {
|
||||
if (res['data'].topReplies[i].rpid == res['data'].upper.top.rpid) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
replies.insert(0, res['data'].upper.top);
|
||||
}
|
||||
}
|
||||
replies.insertAll(0, res['data'].topReplies);
|
||||
res['data'].replies = replies;
|
||||
replyList.value = res['data'].replies!;
|
||||
} else {
|
||||
replyList.addAll(res['data'].replies!);
|
||||
res['data'].replies.addAll(replyList);
|
||||
}
|
||||
}
|
||||
isLoadingMore = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
// 上拉加载
|
||||
Future onLoad() async {
|
||||
queryReplyList(type: 'onLoad');
|
||||
}
|
||||
}
|
||||
|
@ -17,64 +17,95 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
final VideoReplyController _videoReplyController =
|
||||
Get.put(VideoReplyController(), tag: Get.arguments['heroTag']);
|
||||
|
||||
// List<ReplyItemModel>? replyList;
|
||||
Future? _futureBuilderFuture;
|
||||
// 添加页面缓存
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: _videoReplyController.queryReplyList(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data['status']) {
|
||||
List<ReplyItemModel> replies = snapshot.data['data'].replies;
|
||||
// 添加置顶回复
|
||||
if (snapshot.data['data'].upper.top != null) {
|
||||
bool flag = false;
|
||||
for (var i = 0;
|
||||
i < snapshot.data['data'].topReplies.length;
|
||||
i++) {
|
||||
if (snapshot.data['data'].topReplies[i].rpid ==
|
||||
snapshot.data['data'].upper.top.rpid) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
replies.insert(0, snapshot.data['data'].upper.top);
|
||||
}
|
||||
}
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
replies.insertAll(0, snapshot.data['data'].topReplies);
|
||||
// 请求成功
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
if (index == replies.length) {
|
||||
return SizedBox(height: MediaQuery.of(context).padding.bottom);
|
||||
} else {
|
||||
return ReplyItem(
|
||||
replyItem: replies[index],
|
||||
isUp:
|
||||
replies[index].mid == snapshot.data['data'].upper.mid);
|
||||
}
|
||||
}, childCount: replies.length + 1));
|
||||
} else {
|
||||
// 请求错误
|
||||
return HttpError(
|
||||
errMsg: snapshot.data['msg'],
|
||||
fn: () => setState(() {}),
|
||||
);
|
||||
_futureBuilderFuture = _videoReplyController.queryReplyList();
|
||||
_videoReplyController.scrollController.addListener(
|
||||
() {
|
||||
if (_videoReplyController.scrollController.position.pixels >=
|
||||
_videoReplyController.scrollController.position.maxScrollExtent -
|
||||
300) {
|
||||
if (!_videoReplyController.isLoadingMore) {
|
||||
_videoReplyController.onLoad();
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return const VideoCardHSkeleton();
|
||||
}, childCount: 5),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
setState(() {});
|
||||
_videoReplyController.currentPage = 0;
|
||||
return await _videoReplyController.queryReplyList();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
controller: _videoReplyController.scrollController,
|
||||
key: const PageStorageKey<String>('评论'),
|
||||
slivers: <Widget>[
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map data = snapshot.data as Map;
|
||||
if (data['status']) {
|
||||
// 请求成功
|
||||
return Obx(
|
||||
() => SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == _videoReplyController.replyList.length) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(
|
||||
bottom:
|
||||
MediaQuery.of(context).padding.bottom),
|
||||
height:
|
||||
MediaQuery.of(context).padding.bottom + 60,
|
||||
child: Center(
|
||||
child: Text(_videoReplyController.noMore
|
||||
? '没有更多了'
|
||||
: '加载中'),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return ReplyItem(
|
||||
replyItem: _videoReplyController.replyList[index],
|
||||
);
|
||||
}
|
||||
},
|
||||
childCount: _videoReplyController.replyList.length + 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 请求错误
|
||||
return HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => setState(() {}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return const VideoCardHSkeleton();
|
||||
}, childCount: 5),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,8 @@ import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class ReplyItem extends StatelessWidget {
|
||||
ReplyItem({super.key, this.replyItem, required this.isUp});
|
||||
ReplyItem({super.key, this.replyItem});
|
||||
ReplyItemModel? replyItem;
|
||||
bool isUp = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -45,51 +44,46 @@ class ReplyItem extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 头像、昵称
|
||||
Row(
|
||||
// 两端对齐
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
// onTap: () =>
|
||||
// Get.toNamed('/member/${reply.userName}', parameters: {
|
||||
// 'memberAvatar': reply.avatar,
|
||||
// 'heroTag': reply.userName + heroTag,
|
||||
// }),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
lfAvtar(context),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
GestureDetector(
|
||||
// onTap: () =>
|
||||
// Get.toNamed('/member/${reply.userName}', parameters: {
|
||||
// 'memberAvatar': reply.avatar,
|
||||
// 'heroTag': reply.userName + heroTag,
|
||||
// }),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
lfAvtar(context),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
replyItem!.member!.uname!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(
|
||||
color: replyItem!.isUp!
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Image.asset(
|
||||
'assets/images/lv/lv${replyItem!.member!.level}.png',
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
replyItem!.member!.uname!,
|
||||
style: TextStyle(
|
||||
color: replyItem!.isUp!
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Image.asset(
|
||||
'assets/images/lv/lv${replyItem!.member!.level}.png',
|
||||
height: 11,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
if (replyItem!.isUp!) UpTag()
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
// title
|
||||
Container(
|
||||
@ -102,6 +96,8 @@ class ReplyItem extends StatelessWidget {
|
||||
style: const TextStyle(height: 1.65),
|
||||
TextSpan(
|
||||
children: [
|
||||
if (replyItem!.isTop!)
|
||||
WidgetSpan(child: UpTag(tagText: '置顶')),
|
||||
buildContent(context, replyItem!.content!),
|
||||
],
|
||||
),
|
||||
@ -136,26 +132,9 @@ class ReplyItem extends StatelessWidget {
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
if (replyItem!.isTop!) ...[
|
||||
Text(
|
||||
' • 置顶',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (replyControl!.isUpTop!) ...[
|
||||
Text(
|
||||
' • 超赞',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
// const SizedBox(width: 4),
|
||||
],
|
||||
const Spacer(),
|
||||
if (replyControl!.isUpTop!)
|
||||
Icon(Icons.favorite, color: Colors.red[400], size: 18),
|
||||
SizedBox(
|
||||
height: 35,
|
||||
child: TextButton(
|
||||
@ -202,7 +181,7 @@ class ReplyItemRow extends StatelessWidget {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(left: 42, right: 4, top: 0),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.7),
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
animationDuration: Duration.zero,
|
||||
@ -245,21 +224,15 @@ class ReplyItemRow extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(8, index == 0 ? 8 : 4, 8, 4),
|
||||
child: Text.rich(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
overflow: extraRow == 1
|
||||
? TextOverflow.ellipsis
|
||||
: TextOverflow.visible,
|
||||
maxLines: extraRow == 1 ? 2 : null,
|
||||
TextSpan(
|
||||
children: [
|
||||
if (replies![index].isUp)
|
||||
TextSpan(
|
||||
text: 'UP • ',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
WidgetSpan(
|
||||
child: UpTag(),
|
||||
),
|
||||
TextSpan(
|
||||
text: replies![index].member.uname + ' ',
|
||||
@ -417,3 +390,31 @@ InlineSpan buildContent(BuildContext context, content) {
|
||||
// spanChilds.add(TextSpan(text: matchMember));
|
||||
return TextSpan(children: spanChilds);
|
||||
}
|
||||
|
||||
class UpTag extends StatelessWidget {
|
||||
String? tagText;
|
||||
UpTag({super.key, this.tagText = 'UP'});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: tagText == 'UP' ? 28 : 38,
|
||||
height: tagText == 'UP' ? 17 : 19,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
border: Border.all(color: Theme.of(context).colorScheme.primary)),
|
||||
margin: const EdgeInsets.only(right: 4),
|
||||
// padding: const EdgeInsets.symmetric(vertical: 0.5, horizontal: 4),
|
||||
child: Center(
|
||||
child: Text(
|
||||
tagText!,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user