推荐视频评论和点赞
This commit is contained in:
parent
a78acc0f5c
commit
6cec14448c
@ -8,6 +8,9 @@ class VideoApi {
|
||||
// post
|
||||
static const String myPublicList = '/app/vlog/myPublicList'; // 我发布的视频
|
||||
static const String myLikedList = '/app/vlog/myLikedList'; // 我点赞的视频
|
||||
static const String videoCommentList = '/comment/list'; // 视频点赞列表
|
||||
static const String doVideoComment = '/app/comment/publish'; // 发布评论
|
||||
|
||||
|
||||
static const String unlike = '/app/vlog/unlike'; //取消点赞
|
||||
static const String totalLikedCounts = '/app/vlog/totalLikedCounts'; //收到点赞总数
|
||||
|
@ -1,4 +1,3 @@
|
||||
/// 底部评论框
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@ -7,11 +6,15 @@ import 'package:get/get.dart';
|
||||
class PopupReply extends StatefulWidget {
|
||||
const PopupReply({
|
||||
super.key,
|
||||
this.onChanged
|
||||
this.onChanged,
|
||||
this.onSubmitted, // 添加 onSubmitted 参数
|
||||
this.hintText = '说点什么...',
|
||||
});
|
||||
|
||||
// 输入框值改变
|
||||
final ValueChanged? onChanged;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final ValueChanged<String>? onSubmitted; // 定义 onSubmitted
|
||||
final String hintText;
|
||||
|
||||
@override
|
||||
State<PopupReply> createState() => _PopupReplyState();
|
||||
@ -31,6 +34,26 @@ class _PopupReplyState extends State<PopupReply> {
|
||||
controller.dispose();
|
||||
}
|
||||
|
||||
// 处理提交事件
|
||||
void _handleSubmit() {
|
||||
final text = controller.text.trim();
|
||||
if (text.isNotEmpty) {
|
||||
// 优先调用 onSubmitted
|
||||
if (widget.onSubmitted != null) {
|
||||
widget.onSubmitted!(text);
|
||||
} else if (widget.onChanged != null) {
|
||||
// 如果没有 onSubmitted,则使用 onChanged
|
||||
widget.onChanged!(text);
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 onSubmitted 回调(接收 String 参数)
|
||||
void _onSubmitted(String value) {
|
||||
_handleSubmit();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -62,7 +85,7 @@ class _PopupReplyState extends State<PopupReply> {
|
||||
),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: '说点什么...',
|
||||
hintText: widget.hintText,
|
||||
isDense: true,
|
||||
hoverColor: Colors.transparent,
|
||||
contentPadding: EdgeInsets.all(10.0),
|
||||
@ -74,11 +97,9 @@ class _PopupReplyState extends State<PopupReply> {
|
||||
maxLines: null,
|
||||
controller: controller,
|
||||
cursorColor: Color(0xFFFF5000),
|
||||
onEditingComplete: () {
|
||||
widget.onChanged!(controller.text);
|
||||
Get.back();
|
||||
},
|
||||
onChanged: (value) {},
|
||||
onEditingComplete: _handleSubmit, // 使用统一的提交处理
|
||||
onChanged: widget.onChanged, // 直接传递 onChanged
|
||||
onSubmitted: _onSubmitted, // 传递正确的函数签名
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -91,10 +112,7 @@ class _PopupReplyState extends State<PopupReply> {
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0))
|
||||
)
|
||||
),
|
||||
onPressed: () {
|
||||
widget.onChanged!(controller.text);
|
||||
Get.back();
|
||||
},
|
||||
onPressed: _handleSubmit, // 使用统一的提交处理
|
||||
child: Text('发送',),
|
||||
),
|
||||
],
|
||||
|
@ -45,16 +45,351 @@ class RecommendModule extends StatefulWidget {
|
||||
@override
|
||||
State<RecommendModule> createState() => _RecommendModuleState();
|
||||
}
|
||||
class CommentBottomSheet extends StatefulWidget {
|
||||
final int videoId;
|
||||
|
||||
const CommentBottomSheet({super.key, required this.videoId});
|
||||
|
||||
@override
|
||||
_CommentBottomSheetState createState() => _CommentBottomSheetState();
|
||||
}
|
||||
|
||||
class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
||||
// 评论相关状态(移动到内部)
|
||||
int commentPage = 1;
|
||||
final int commentPageSize = 10;
|
||||
bool isLoadingMoreComments = false;
|
||||
bool hasMoreComments = true;
|
||||
List<Map<String, dynamic>> commentList = [];
|
||||
int replyingCommentId = 0;
|
||||
String replyingCommentUser = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 初始化时加载评论
|
||||
fetchComments(false);
|
||||
}
|
||||
|
||||
Future<void> fetchComments(bool loadMore) async {
|
||||
if (isLoadingMoreComments && !loadMore) return;
|
||||
|
||||
setState(() {
|
||||
isLoadingMoreComments = true;
|
||||
});
|
||||
|
||||
if (!loadMore) {
|
||||
commentPage = 1;
|
||||
hasMoreComments = true;
|
||||
commentList.clear();
|
||||
}
|
||||
|
||||
try {
|
||||
final res = await Http.post(VideoApi.videoCommentList, data: {
|
||||
'vlogId': widget.videoId,
|
||||
'current': commentPage,
|
||||
'size': commentPageSize,
|
||||
});
|
||||
|
||||
logger.d('评论接口返回: ${json.encode(res)}');
|
||||
|
||||
if (res['code'] == 200 && res['data'] != null) {
|
||||
final data = res['data'];
|
||||
final List<Map<String, dynamic>> newComments =
|
||||
List<Map<String, dynamic>>.from(data['records'] ?? []);
|
||||
final int total = data['total'] ?? 0;
|
||||
|
||||
setState(() {
|
||||
if (loadMore) {
|
||||
commentList.addAll(newComments);
|
||||
} else {
|
||||
commentList = newComments;
|
||||
}
|
||||
hasMoreComments = commentList.length < total;
|
||||
commentPage++;
|
||||
});
|
||||
|
||||
logger.d('成功加载 ${newComments.length} 条评论,总共 $total 条');
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e('获取评论异常: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoadingMoreComments = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> postComment(String content, {int parentCommentId = 0}) async {
|
||||
try {
|
||||
final res = await Http.post(VideoApi.doVideoComment, data: {
|
||||
'vlogId': widget.videoId,
|
||||
'content': content,
|
||||
'fatherCommentId': parentCommentId,
|
||||
});
|
||||
|
||||
if (res['code'] == 0) {
|
||||
// 评论成功,重新加载评论列表
|
||||
fetchComments(false);
|
||||
MyToast().tip(
|
||||
title: '评论成功',
|
||||
position: 'center',
|
||||
type: 'success',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.i('发布评论失败: $e');
|
||||
MyToast().tip(
|
||||
title: '评论失败',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(15.0, 10.0, 10.0, 5.0),
|
||||
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color(0xFFFAFAFA)))),
|
||||
child: Column(
|
||||
spacing: 10.0,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
/* Expanded(
|
||||
child: Text.rich(TextSpan(children: [
|
||||
TextSpan(
|
||||
text: '大家都在搜: ',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
TextSpan(
|
||||
text: '黑神话-悟空',
|
||||
style: TextStyle(color: const Color(0xFF496D80)),
|
||||
),
|
||||
]))),*/
|
||||
GestureDetector(
|
||||
child: Container(
|
||||
height: 22.0,
|
||||
width: 22.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
child: UnconstrainedBox(child: Icon(Icons.close, color: Colors.black45, size: 14.0))),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'${commentList.length}条评论',
|
||||
style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification scrollInfo) {
|
||||
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
|
||||
!isLoadingMoreComments &&
|
||||
hasMoreComments) {
|
||||
fetchComments(true);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: commentList.isEmpty && !isLoadingMoreComments
|
||||
? Center(
|
||||
child: Text(
|
||||
'暂无评论',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemCount: commentList.length + (hasMoreComments ? 1 : 0),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == commentList.length) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: isLoadingMoreComments
|
||||
? CircularProgressIndicator()
|
||||
: Text('没有更多评论了'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final comment = commentList[index];
|
||||
return ListTile(
|
||||
isThreeLine: true,
|
||||
leading: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: comment['commentUserFace'] ?? 'assets/images/avatar/default.png',
|
||||
width: 30.0,
|
||||
height: 30.0,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
comment['commentUserNickname'] ?? '未知用户',
|
||||
style: TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20.0),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
replyingCommentId = comment['id'];
|
||||
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
Icons.reply,
|
||||
color: Colors.black54,
|
||||
size: 16.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 5.0),
|
||||
child: Text(
|
||||
comment['content'] ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (comment['childCount'] > 0)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
replyingCommentId = comment['id'];
|
||||
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(right: 15.0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Row(children: [
|
||||
Text(
|
||||
'${comment['childCount']}回复',
|
||||
style: TextStyle(fontSize: 12.0),
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 10.0,
|
||||
)
|
||||
]),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${comment['createTime']?.toString().substring(0, 10) ?? ''}',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (replyingCommentId > 0)
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
color: Colors.grey[100],
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'回复 @$replyingCommentUser',
|
||||
style: TextStyle(fontSize: 12.0, color: Colors.blue),
|
||||
),
|
||||
Spacer(),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
replyingCommentId = 0;
|
||||
replyingCommentUser = '';
|
||||
});
|
||||
},
|
||||
child: Icon(Icons.close, size: 16.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(10.0),
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 15.0),
|
||||
Icon(
|
||||
Icons.edit_note,
|
||||
color: Colors.black54,
|
||||
size: 16.0,
|
||||
),
|
||||
SizedBox(width: 5.0),
|
||||
Text(
|
||||
replyingCommentId > 0 ? '回复 @$replyingCommentUser' : '说点什么...',
|
||||
style: TextStyle(color: Colors.black54, fontSize: 14.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(context, FadeRoute(child: PopupReply(
|
||||
hintText: replyingCommentId > 0 ? '回复 @$replyingCommentUser' : '说点什么...',
|
||||
onChanged: (value) {
|
||||
debugPrint('评论内容: $value');
|
||||
},
|
||||
onSubmitted: (value) {
|
||||
if (value.isNotEmpty) {
|
||||
postComment(value, parentCommentId: replyingCommentId);
|
||||
setState(() {
|
||||
replyingCommentId = 0;
|
||||
replyingCommentUser = '';
|
||||
});
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
)));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
class _RecommendModuleState extends State<RecommendModule> {
|
||||
VideoModuleController videoModuleController = Get.put(VideoModuleController());
|
||||
final ChatController chatController = Get.find<ChatController>();
|
||||
|
||||
// class _RecommendModuleState extends State<RecommendModule> with AutomaticKeepAliveClientMixin {
|
||||
// @override
|
||||
// bool get wantKeepAlive => true;
|
||||
// VideoModuleController videoModuleController = Get.find<VideoModuleController>();
|
||||
|
||||
// 分页内容
|
||||
int page = 1;
|
||||
final int pageSize = 10;
|
||||
@ -78,15 +413,17 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
late Duration duration = Duration.zero; // 总时长
|
||||
// 视频数据
|
||||
List videoList = [];
|
||||
// 评论数据
|
||||
List commentList = [
|
||||
{'avatar': 'assets/images/avatar/img01.jpg', 'name': 'Alice', 'desc': '用汗水浇灌希望,让努力铸就辉煌,你付出的每一刻,都是在靠近成功的彼岸。'},
|
||||
{'avatar': 'assets/images/avatar/img02.jpg', 'name': '悟空', 'desc': '黑暗遮不住破晓的曙光,困境困不住奋进的脚步,勇往直前,你定能冲破阴霾。'},
|
||||
{'avatar': 'assets/images/avatar/img03.jpg', 'name': '木棉花', 'desc': '每一次跌倒都是为了下一次更有力地跃起,别放弃~!'},
|
||||
{'avatar': 'assets/images/avatar/img04.jpg', 'name': '狗仔', 'desc': '人生没有白走的路,每一步都算数,那些辛苦的过往,会在未来化作最美的勋章。'},
|
||||
{'avatar': 'assets/images/avatar/img05.jpg', 'name': '向日葵', 'desc': '以梦为马,不负韶华,握紧手中的笔,书写属于自己的热血传奇,让青春绽放光芒。'},
|
||||
{'avatar': 'assets/images/avatar/img06.jpg', 'name': '健身女神', 'desc': '哪怕身处谷底,只要抬头仰望,便能看见漫天繁星,心怀希望,就能找到出路,奔赴美好。'},
|
||||
];
|
||||
|
||||
// 评论相关状态
|
||||
int commentPage = 1;
|
||||
final int commentPageSize = 10;
|
||||
bool isLoadingMoreComments = false;
|
||||
bool hasMoreComments = true;
|
||||
List<Map<String, dynamic>> commentList = [];
|
||||
int currentVideoId = 0;
|
||||
int replyingCommentId = 0; // 正在回复的评论ID
|
||||
String replyingCommentUser = ''; // 正在回复的用户名
|
||||
|
||||
// 分享列表
|
||||
List shareList = [
|
||||
{'icon': 'assets/images/share-wx.png', 'label': '好友'},
|
||||
@ -187,16 +524,12 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
final data = res['data'];
|
||||
logger.d('关注用户的视频列表:$data');
|
||||
if (data == null || (data is List && data.isEmpty)) {
|
||||
// MyDialog.toast('没有更多了', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data['records'] is List) {
|
||||
List videos = data['records'];
|
||||
for (var item in videos) {
|
||||
// print("喜欢:${item['likeCounts']}");
|
||||
// print("评论:${item['commentsCounts']}");
|
||||
// logger.i(item);
|
||||
item['expanded'] = false;
|
||||
}
|
||||
|
||||
@ -236,206 +569,145 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
}
|
||||
}
|
||||
|
||||
// 修改 fetchComments 方法
|
||||
Future<void> fetchComments(int videoId, {bool loadMore = false}) async {
|
||||
// 防止重复加载
|
||||
if (isLoadingMoreComments && !loadMore) return;
|
||||
|
||||
setState(() {
|
||||
isLoadingMoreComments = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final res = await Http.post(VideoApi.videoCommentList, data: {
|
||||
'vlogId': videoId,
|
||||
'current': loadMore ? commentPage : 1,
|
||||
'size': commentPageSize,
|
||||
});
|
||||
|
||||
logger.d('评论接口返回: ${json.encode(res)}');
|
||||
|
||||
if (res['code'] == 200 && res['data'] != null) {
|
||||
final data = res['data'];
|
||||
final List<Map<String, dynamic>> newComments =
|
||||
List<Map<String, dynamic>>.from(data['records'] ?? []);
|
||||
final int total = data['total'] ?? 0;
|
||||
|
||||
setState(() {
|
||||
if (loadMore) {
|
||||
commentList.addAll(newComments);
|
||||
} else {
|
||||
commentList = newComments;
|
||||
}
|
||||
hasMoreComments = commentList.length < total;
|
||||
commentPage = loadMore ? commentPage + 1 : 2; // 下一页页码
|
||||
});
|
||||
|
||||
logger.d('成功加载 ${newComments.length} 条评论,总共 $total 条');
|
||||
} else {
|
||||
logger.e('获取评论失败: ${res['msg']}');
|
||||
MyToast().tip(
|
||||
title: '获取评论失败',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e('获取评论异常: $e');
|
||||
MyToast().tip(
|
||||
title: '网络异常',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoadingMoreComments = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 发布评论
|
||||
Future<void> postComment(String content, {int parentCommentId = 0}) async {
|
||||
try {
|
||||
final res = await Http.post(VideoApi.doVideoComment, data: {
|
||||
'vlogId': currentVideoId,
|
||||
'content': content,
|
||||
'fatherCommentId': parentCommentId,
|
||||
});
|
||||
|
||||
if (res['code'] == 0) {
|
||||
// 评论成功,重新加载评论列表
|
||||
fetchComments(currentVideoId, loadMore: false);
|
||||
MyToast().tip(
|
||||
title: '评论成功',
|
||||
position: 'center',
|
||||
type: 'success',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.i('发布评论失败: $e');
|
||||
MyToast().tip(
|
||||
title: '评论失败',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 取消喜欢视频
|
||||
Future<void> doUnLikeVideo(item) async {
|
||||
logger.d('点击了点赞按钮$item');
|
||||
try {
|
||||
final res = await Http.post(VideoApi.unlike, data:{
|
||||
'vlogId': item['id']
|
||||
});
|
||||
final resCode = res['code'];
|
||||
if (resCode == 200) {
|
||||
setState(() {
|
||||
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logger.i('点击取消喜欢按钮报错: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 点击喜欢视频
|
||||
Future<void> doLikeVideo(item) async {
|
||||
try {
|
||||
final res = await Http.post(VideoApi.like, data:{
|
||||
'vlogId': item['id']
|
||||
});
|
||||
final resCode = res['code'];
|
||||
if (resCode == 200) {
|
||||
setState(() {
|
||||
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
||||
});
|
||||
}
|
||||
logger.i('点赞返回信息----------->: $res');
|
||||
} catch (e) {
|
||||
logger.i('点击喜欢按钮报错: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 评论弹框
|
||||
void handleComment(index) {
|
||||
//获取评论数据
|
||||
final videoIdStr = videoList[index]['id'].toString();
|
||||
final videoId = int.tryParse(videoIdStr) ?? 0;
|
||||
logger.i('点击了评论按钮$videoId');
|
||||
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(15.0))),
|
||||
showDragHandle: false,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
isScrollControlled: true, // 屏幕最大高度
|
||||
isScrollControlled: true,
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * 3 / 4, // 自定义最大高度
|
||||
maxHeight: MediaQuery.of(context).size.height * 3 / 4,
|
||||
),
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(15.0, 10.0, 10.0, 5.0),
|
||||
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color(0xFFFAFAFA)))),
|
||||
child: Column(
|
||||
spacing: 10.0,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text.rich(TextSpan(children: [
|
||||
TextSpan(
|
||||
text: '大家都在搜: ',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
TextSpan(
|
||||
text: '黑神话-悟空',
|
||||
style: TextStyle(color: const Color(0xFF496D80)),
|
||||
),
|
||||
]))),
|
||||
GestureDetector(
|
||||
child: Container(
|
||||
height: 22.0,
|
||||
width: 22.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
child: UnconstrainedBox(child: Icon(Icons.close, color: Colors.black45, size: 14.0))),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'168条评论',
|
||||
style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w600),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: ListView.builder(
|
||||
physics: BouncingScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: commentList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
isThreeLine: true,
|
||||
leading: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
child: Image.asset(
|
||||
'${commentList[index]['avatar']}',
|
||||
width: 30.0,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${commentList[index]['name']}',
|
||||
style: TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.favorite_border_outlined,
|
||||
color: Colors.black54,
|
||||
size: 16.0,
|
||||
),
|
||||
Text(
|
||||
'99',
|
||||
style: TextStyle(color: Colors.black54, fontSize: 12.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
width: 20.0,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.heart_broken_outlined,
|
||||
color: Colors.black54,
|
||||
size: 16.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 5.0),
|
||||
child: Text(
|
||||
'${commentList[index]['desc']}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(right: 15.0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Row(children: [
|
||||
Text(
|
||||
'12回复',
|
||||
style: TextStyle(fontSize: 12.0),
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 10.0,
|
||||
)
|
||||
]),
|
||||
),
|
||||
Text(
|
||||
'01-15 · 浙江',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(10.0),
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 15.0,
|
||||
),
|
||||
Icon(
|
||||
Icons.edit_note,
|
||||
color: Colors.black54,
|
||||
size: 16.0,
|
||||
),
|
||||
SizedBox(
|
||||
width: 5.0,
|
||||
),
|
||||
Text(
|
||||
'说点什么...',
|
||||
style: TextStyle(color: Colors.black54, fontSize: 14.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
navigator?.push(FadeRoute(child: PopupReply(
|
||||
onChanged: (value) {
|
||||
debugPrint('评论内容: $value');
|
||||
},
|
||||
)));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
return CommentBottomSheet(videoId: videoId);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -493,19 +765,15 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
|
||||
// 会话列表
|
||||
Obx(() {
|
||||
// 这里过滤掉有分组的会话
|
||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
||||
if (filteredList.isEmpty) return SizedBox.shrink();
|
||||
return SizedBox(
|
||||
SizedBox(
|
||||
height: 110,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: filteredList.length,
|
||||
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
|
||||
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
||||
itemBuilder: (context, index) {
|
||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
||||
return GestureDetector(
|
||||
// 点击分享
|
||||
onTap: () => handlCoverClick(filteredList[index].conversation),
|
||||
child: Container(
|
||||
width: 64,
|
||||
@ -513,7 +781,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0),
|
||||
NetworkOrAssetImage(
|
||||
imageUrl: filteredList[index].faceUrl,
|
||||
width: 48.0,
|
||||
@ -531,8 +798,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
// 取消按钮
|
||||
SafeArea(
|
||||
@ -607,7 +873,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// super.build(context);
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
child: Column(
|
||||
@ -617,15 +882,12 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
children: [
|
||||
/// 垂直滚动模块
|
||||
PageView.builder(
|
||||
// 自定义滚动行为(支持桌面端滑动、去掉滚动条槽)
|
||||
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: pageController,
|
||||
onPageChanged: (index) async {
|
||||
// 更新当前播放视频索引
|
||||
videoModuleController.updateVideoPlayIndex(index);
|
||||
setState(() {
|
||||
// 重置slider参数
|
||||
sliderValue = 0.0;
|
||||
sliderDraging = false;
|
||||
position = Duration.zero;
|
||||
@ -633,18 +895,17 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
});
|
||||
|
||||
player.stop();
|
||||
// await player.open(Media(videoList[index]['src']));
|
||||
await player.open(Media(videoList[index]['url']));
|
||||
// 如果滚动到列表末尾,且还有更多数据
|
||||
if (index == videoList.length - 2 && !isLoadingMore) {
|
||||
await fetchVideoList(); // 拉取更多
|
||||
await fetchVideoList();
|
||||
}
|
||||
},
|
||||
itemCount: videoList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final videoWidth = videoList[index]['width'] ?? 1;
|
||||
final videoHeight = videoList[index]['height'] ?? 1; // 防止除以0
|
||||
final videoHeight = videoList[index]['height'] ?? 1;
|
||||
final isHorizontal = videoWidth > videoHeight;
|
||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
||||
return Stack(
|
||||
children: [
|
||||
// 视频区域
|
||||
@ -656,17 +917,14 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
child: GestureDetector(
|
||||
child: Stack(
|
||||
children: [
|
||||
// 短视频插件
|
||||
Visibility(
|
||||
visible: videoModuleController.videoPlayIndex.value == index && position > Duration.zero,
|
||||
child: Video(
|
||||
controller: videoController,
|
||||
fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
|
||||
// 无控制条
|
||||
controls: NoVideoControls,
|
||||
),
|
||||
),
|
||||
// 封面图,播放后透明度渐变为0(而不是直接隐藏)
|
||||
AnimatedOpacity(
|
||||
opacity: videoModuleController.videoPlayIndex.value == index && position > Duration(milliseconds: 100) ? 0.0 : 1.0,
|
||||
duration: Duration(milliseconds: 50),
|
||||
@ -678,7 +936,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
),
|
||||
|
||||
// 播放/暂停按钮
|
||||
StreamBuilder(
|
||||
stream: player.stream.playing,
|
||||
builder: (context, playing) {
|
||||
@ -715,7 +972,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
child: Column(
|
||||
spacing: 15.0,
|
||||
children: [
|
||||
// 头像
|
||||
Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
@ -737,7 +993,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: videoList[index]['avatar'],
|
||||
imageUrl: videoList[index]['commentUserFace'],
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -770,7 +1026,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
],
|
||||
),
|
||||
// 点赞
|
||||
GestureDetector(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -787,12 +1042,15 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
videoList[index]['doILikeThisVlog'] = !videoList[index]['doILikeThisVlog'];
|
||||
});
|
||||
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
||||
if(videoList[index]['doILikeThisVlog'] == true){
|
||||
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
||||
doUnLikeVideo(videoList[index]);
|
||||
}else{
|
||||
doLikeVideo(videoList[index]);
|
||||
}
|
||||
},
|
||||
),
|
||||
// 评论
|
||||
GestureDetector(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -812,21 +1070,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
handleComment(index);
|
||||
},
|
||||
),
|
||||
// Column(
|
||||
// children: [
|
||||
// SvgPicture.asset(
|
||||
// 'assets/images/svg/favor.svg',
|
||||
// colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
// height: 40.0,
|
||||
// width: 40.0,
|
||||
// ),
|
||||
// Text(
|
||||
// '${videoList[index]['starNum']}',
|
||||
// style: TextStyle(color: Colors.white, fontSize: 12.0),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// 转发
|
||||
GestureDetector(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -842,7 +1085,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
handleShare(index);
|
||||
},
|
||||
),
|
||||
//举报
|
||||
GestureDetector(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -861,7 +1103,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
],
|
||||
),
|
||||
),
|
||||
// 底部信息区域
|
||||
Positioned(
|
||||
bottom: 15.0,
|
||||
left: 10.0,
|
||||
@ -870,13 +1111,12 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'@${videoList[videoModuleController.videoPlayIndex.value]['nickname'] ?? '未知'}',
|
||||
'@${videoList[videoModuleController.videoPlayIndex.value]['commentUserNickname'] ?? '未知'}',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 16.0),
|
||||
),
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final text = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '未知';
|
||||
// 先用 TextPainter 判断是否超过 3 行
|
||||
final span = TextSpan(
|
||||
text: text,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 14.0),
|
||||
@ -926,7 +1166,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
],
|
||||
)),
|
||||
// mini播放进度条
|
||||
Positioned(
|
||||
bottom: 0.0,
|
||||
left: 6.0,
|
||||
@ -937,29 +1176,25 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
child: SliderTheme(
|
||||
data: SliderThemeData(
|
||||
trackHeight: sliderDraging ? 6.0 : 2.0,
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 4.0), // 调整滑块的大小
|
||||
// trackShape: RectangularSliderTrackShape(), // 使用矩形轨道形状
|
||||
overlayShape: RoundSliderOverlayShape(overlayRadius: 0), // 去掉Slider默认上下边距间隙
|
||||
inactiveTrackColor: Colors.white24, // 设置非活动进度条的颜色
|
||||
activeTrackColor: Colors.white, // 设置活动进度条的颜色
|
||||
thumbColor: Colors.white, // 设置滑块的颜色
|
||||
overlayColor: Colors.transparent, // 设置滑块覆盖层的颜色
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 4.0),
|
||||
overlayShape: RoundSliderOverlayShape(overlayRadius: 0),
|
||||
inactiveTrackColor: Colors.white24,
|
||||
activeTrackColor: Colors.white,
|
||||
thumbColor: Colors.white,
|
||||
overlayColor: Colors.transparent,
|
||||
),
|
||||
child: Slider(
|
||||
value: sliderValue,
|
||||
onChanged: (value) async {
|
||||
// debugPrint('当前视频播放时间$value');
|
||||
setState(() {
|
||||
sliderValue = value;
|
||||
});
|
||||
// 跳转播放时间
|
||||
await player.seek(duration * value.clamp(0.0, 1.0));
|
||||
},
|
||||
onChangeEnd: (value) async {
|
||||
setState(() {
|
||||
sliderDraging = false;
|
||||
});
|
||||
// 继续播放
|
||||
if (!player.state.playing) {
|
||||
await player.play();
|
||||
}
|
||||
@ -974,7 +1209,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// 播放位置指示器
|
||||
Positioned(
|
||||
bottom: 100.0,
|
||||
left: 10.0,
|
||||
@ -998,10 +1232,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
/// 固定层
|
||||
// 红包广告,先不做
|
||||
// Ads(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
Loading…
x
Reference in New Issue
Block a user