feat: 评论

This commit is contained in:
guozhigq
2023-05-15 07:32:39 +08:00
parent 7ae1716f44
commit 351cc0a850
7 changed files with 380 additions and 66 deletions

View File

@ -1,4 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/skeleton/video_reply.dart';
@ -10,11 +13,11 @@ import 'widgets/reply_item.dart';
class VideoReplyPanel extends StatefulWidget {
int oid;
int rpid;
String level;
String? level;
VideoReplyPanel({
this.oid = 0,
this.rpid = 0,
this.level = '',
this.level,
super.key,
});
@ -23,11 +26,16 @@ class VideoReplyPanel extends StatefulWidget {
}
class _VideoReplyPanelState extends State<VideoReplyPanel>
with AutomaticKeepAliveClientMixin {
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
late VideoReplyController _videoReplyController;
late AnimationController fabAnimationCtr;
late AnimationController replyAnimationCtl;
// List<ReplyItemModel>? replyList;
Future? _futureBuilderFuture;
bool _isFabVisible = true;
String replyLevel = '1';
// 添加页面缓存
@override
bool get wantKeepAlive => true;
@ -35,16 +43,28 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
@override
void initState() {
super.initState();
if (widget.level == '2') {
replyLevel = widget.level ?? '1';
if (widget.level != null && widget.level == '2') {
_videoReplyController = Get.put(
VideoReplyController(
widget.oid.toString(), widget.rpid.toString(), '2'),
tag: widget.rpid.toString());
_videoReplyController.rPid = widget.rpid;
} else {
_videoReplyController = Get.put(
VideoReplyController(Get.parameters['aid']!, '', '1'),
tag: Get.arguments['heroTag']);
}
// if(replyLevel != ''){
// _videoReplyController.replyLevel = replyLevel;
// }
print(
'_videoReplyController.replyLevel: ${_videoReplyController.replyLevel}');
fabAnimationCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 300));
replyAnimationCtl = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
_futureBuilderFuture = _videoReplyController.queryReplyList();
_videoReplyController.scrollController.addListener(
@ -56,10 +76,55 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
_videoReplyController.onLoad();
}
}
final ScrollDirection direction =
_videoReplyController.scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
_showFab();
} else if (direction == ScrollDirection.reverse) {
_hideFab();
}
},
);
}
void _showFab() {
if (!_isFabVisible) {
_isFabVisible = true;
fabAnimationCtr.forward();
}
}
void _hideFab() {
if (_isFabVisible) {
_isFabVisible = false;
fabAnimationCtr.reverse();
}
}
void _showReply(source, {ReplyItemModel? replyItem, replyLevel}) async {
// source main 直接回复 floor 楼中楼回复
if (source == 'floor') {
_videoReplyController.currentReplyItem = replyItem;
_videoReplyController.replySource = source;
_videoReplyController.replyLevel = replyLevel ?? '1';
} else {
_videoReplyController.replyLevel = '0';
}
replyAnimationCtl.forward();
await Future.delayed(const Duration(microseconds: 100));
_videoReplyController.wakeUpReply();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
fabAnimationCtr.dispose();
_videoReplyController.scrollController.dispose();
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
@ -68,63 +133,147 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
_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: Obx(() => Text(
_videoReplyController.noMore.value
? '没有更多了'
: '加载中')),
),
);
} else {
return ReplyItem(
replyItem: _videoReplyController.replyList[index],
);
}
},
childCount: _videoReplyController.replyList.length + 1,
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
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: Obx(() => Text(
_videoReplyController.noMore.value
? '没有更多了'
: '加载中')),
),
);
} else {
return ReplyItem(
replyItem: _videoReplyController
.replyList[index],
weakUpReply: (replyItem, replyLevel) =>
_showReply(
'floor',
replyItem: replyItem,
replyLevel: replyLevel,
),
replyLevel: replyLevel);
}
},
childCount:
_videoReplyController.replyList.length + 1,
),
),
);
} else {
// 请求错误
return HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
);
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 5),
);
}
},
)
],
),
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 14,
right: 14,
child: SlideTransition(
position: Tween<Offset>(
// begin: const Offset(0, 2),
// 评论内容为空/不足一屏
begin: const Offset(0, 0),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
curve: Curves.easeInOut,
)),
child: FloatingActionButton(
heroTag: null,
onPressed: () => _showReply('main'),
tooltip: '发表评论',
child: const Icon(Icons.reply),
),
),
),
Obx(
() => Positioned(
bottom: 0,
left: 0,
right: 0,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 2),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: replyAnimationCtl,
curve: Curves.easeInOut,
)),
child: Container(
height: 100 + MediaQuery.of(context).padding.bottom,
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
color: Theme.of(context).colorScheme.surfaceVariant,
child: Padding(
padding: const EdgeInsets.only(left: 14, right: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Visibility(
visible: _videoReplyController.autoFocus.value,
child: const TextField(
autofocus: true,
maxLines: null,
decoration: InputDecoration(
hintText: "友善评论", border: InputBorder.none),
),
),
TextButton(
onPressed: () =>
_videoReplyController.submitReplyAdd(),
child: const Text('发送'),
)
],
),
),
);
} else {
// 请求错误
return HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
);
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 5),
);
}
},
)
],
),
),
),
),
],
),
),
);
}