From 6cec14448c683bbb6d558c290ff5e4140413b8e5 Mon Sep 17 00:00:00 2001 From: cuiyouliang <799699717@qq.com> Date: Fri, 22 Aug 2025 18:24:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A8=E8=8D=90=E8=A7=86=E9=A2=91=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E5=92=8C=E7=82=B9=E8=B5=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/api/video_api.dart | 3 + lib/pages/video/components/popup_reply.dart | 46 +- lib/pages/video/module/recommend.dart | 854 +++++++++++++------- 3 files changed, 577 insertions(+), 326 deletions(-) diff --git a/lib/api/video_api.dart b/lib/api/video_api.dart index 7cf0f30..d611143 100644 --- a/lib/api/video_api.dart +++ b/lib/api/video_api.dart @@ -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'; //收到点赞总数 diff --git a/lib/pages/video/components/popup_reply.dart b/lib/pages/video/components/popup_reply.dart index 4409435..d4938cf 100644 --- a/lib/pages/video/components/popup_reply.dart +++ b/lib/pages/video/components/popup_reply.dart @@ -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? onChanged; + final ValueChanged? onSubmitted; // 定义 onSubmitted + final String hintText; @override State createState() => _PopupReplyState(); @@ -31,6 +34,26 @@ class _PopupReplyState extends State { 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 { ), 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 { 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 { RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)) ) ), - onPressed: () { - widget.onChanged!(controller.text); - Get.back(); - }, + onPressed: _handleSubmit, // 使用统一的提交处理 child: Text('发送',), ), ], @@ -105,4 +123,4 @@ class _PopupReplyState extends State { ), ); } -} +} \ No newline at end of file diff --git a/lib/pages/video/module/recommend.dart b/lib/pages/video/module/recommend.dart index 5d889e1..7974a23 100644 --- a/lib/pages/video/module/recommend.dart +++ b/lib/pages/video/module/recommend.dart @@ -45,16 +45,351 @@ class RecommendModule extends StatefulWidget { @override State createState() => _RecommendModuleState(); } +class CommentBottomSheet extends StatefulWidget { + final int videoId; + const CommentBottomSheet({super.key, required this.videoId}); + + @override + _CommentBottomSheetState createState() => _CommentBottomSheetState(); +} + +class _CommentBottomSheetState extends State { + // 评论相关状态(移动到内部) + int commentPage = 1; + final int commentPageSize = 10; + bool isLoadingMoreComments = false; + bool hasMoreComments = true; + List> commentList = []; + int replyingCommentId = 0; + String replyingCommentUser = ''; + + @override + void initState() { + super.initState(); + // 初始化时加载评论 + fetchComments(false); + } + + Future 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> newComments = + List>.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 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( + 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 { VideoModuleController videoModuleController = Get.put(VideoModuleController()); final ChatController chatController = Get.find(); -// class _RecommendModuleState extends State with AutomaticKeepAliveClientMixin { -// @override -// bool get wantKeepAlive => true; -// VideoModuleController videoModuleController = Get.find(); - // 分页内容 int page = 1; final int pageSize = 10; @@ -78,15 +413,17 @@ class _RecommendModuleState extends State { 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> 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 { 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,209 +569,148 @@ class _RecommendModuleState extends State { } } - // 评论弹框 - void handleComment(index) { - //获取评论数据 + // 修改 fetchComments 方法 +Future fetchComments(int videoId, {bool loadMore = false}) async { + // 防止重复加载 + if (isLoadingMoreComments && !loadMore) return; - showModalBottomSheet( - backgroundColor: Colors.white, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(15.0))), - showDragHandle: false, - clipBehavior: Clip.antiAlias, - isScrollControlled: true, // 屏幕最大高度 - constraints: BoxConstraints( - 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'); - }, - ))); - }, - ), - ], - ), - ); - }, + 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> newComments = + List>.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 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 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 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, + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 3 / 4, + ), + context: context, + builder: (context) { + return CommentBottomSheet(videoId: videoId); + }, + ); +} // 分享弹框 void handleShare(index) { @@ -493,46 +765,40 @@ class _RecommendModuleState extends State { ), // 会话列表 - Obx(() { - // 这里过滤掉有分组的会话 - final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList(); - if (filteredList.isEmpty) return SizedBox.shrink(); - return SizedBox( - height: 110, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: filteredList.length, - padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0), - itemBuilder: (context, index) { - return GestureDetector( - // 点击分享 - onTap: () => handlCoverClick(filteredList[index].conversation), - child: Container( - width: 64, - margin: EdgeInsets.symmetric(horizontal: 8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0), - NetworkOrAssetImage( - imageUrl: filteredList[index].faceUrl, - width: 48.0, - height: 48.0, - ), - SizedBox(height: 5), - Text( - '${filteredList[index].conversation.showName}', - style: TextStyle(fontSize: 12.0), - overflow: TextOverflow.ellipsis, - ), - ], - ), + SizedBox( + height: 110, + child: ListView.builder( + scrollDirection: Axis.horizontal, + 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, + margin: EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + NetworkOrAssetImage( + imageUrl: filteredList[index].faceUrl, + width: 48.0, + height: 48.0, + ), + SizedBox(height: 5), + Text( + '${filteredList[index].conversation.showName}', + style: TextStyle(fontSize: 12.0), + overflow: TextOverflow.ellipsis, + ), + ], ), - ); - }, - ), - ); - }), + ), + ); + }, + ), + ), // 取消按钮 SafeArea( @@ -607,7 +873,6 @@ class _RecommendModuleState extends State { @override Widget build(BuildContext context) { - // super.build(context); return Container( color: Colors.black, child: Column( @@ -617,15 +882,12 @@ class _RecommendModuleState extends State { 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 { }); 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 { 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 { ), ), - // 播放/暂停按钮 StreamBuilder( stream: player.stream.playing, builder: (context, playing) { @@ -715,7 +972,6 @@ class _RecommendModuleState extends State { child: Column( spacing: 15.0, children: [ - // 头像 Stack( children: [ SizedBox( @@ -737,7 +993,7 @@ class _RecommendModuleState extends State { ), child: ClipOval( child: NetworkOrAssetImage( - imageUrl: videoList[index]['avatar'], + imageUrl: videoList[index]['commentUserFace'], ), ), ), @@ -770,7 +1026,6 @@ class _RecommendModuleState extends State { ), ], ), - // 点赞 GestureDetector( child: Column( children: [ @@ -787,12 +1042,15 @@ class _RecommendModuleState extends State { ], ), 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 { 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 { handleShare(index); }, ), - //举报 GestureDetector( child: Column( children: [ @@ -861,7 +1103,6 @@ class _RecommendModuleState extends State { ], ), ), - // 底部信息区域 Positioned( bottom: 15.0, left: 10.0, @@ -870,13 +1111,12 @@ class _RecommendModuleState extends State { 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 { ), ], )), - // mini播放进度条 Positioned( bottom: 0.0, left: 6.0, @@ -937,29 +1176,25 @@ class _RecommendModuleState extends State { 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 { ), ), ), - // 播放位置指示器 Positioned( bottom: 100.0, left: 10.0, @@ -998,10 +1232,6 @@ class _RecommendModuleState extends State { ); }, ), - - /// 固定层 - // 红包广告,先不做 - // Ads(), ], ), ), @@ -1009,4 +1239,4 @@ class _RecommendModuleState extends State { ), ); } -} +} \ No newline at end of file