From a78acc0f5c2cd7e21c74967f2340066df2a24bd1 Mon Sep 17 00:00:00 2001 From: cuiyouliang <799699717@qq.com> Date: Thu, 21 Aug 2025 17:50:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=B3=E6=B3=A8=E9=A6=96=E9=A1=B5=E8=B0=83?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/auth/login.dart | 2 +- lib/pages/my/vloger.dart | 2 +- lib/pages/video/index.dart | 16 +- lib/pages/video/module/attention.dart | 993 +++++++++++++++++++++++++- lib/pages/video/module/recommend.dart | 6 +- lib/update/upgrade_dialog.dart | 2 +- 6 files changed, 1001 insertions(+), 20 deletions(-) diff --git a/lib/pages/auth/login.dart b/lib/pages/auth/login.dart index 19dafc6..6d44e59 100644 --- a/lib/pages/auth/login.dart +++ b/lib/pages/auth/login.dart @@ -91,7 +91,7 @@ class _LoginState extends State { Storage.write('userId', userId); Storage.write('token', obj['access_token']); // 获取用户账户信息 - final accountRes = await Http.get('${CommonApi.accountInfo}/$userId'); + final accountRes = await Http.get('${CommonApi.accountInfo}'); logger.i(accountRes); // 刷新短视频列表 final videoController = Get.find(); diff --git a/lib/pages/my/vloger.dart b/lib/pages/my/vloger.dart index 873db9e..450a55a 100644 --- a/lib/pages/my/vloger.dart +++ b/lib/pages/my/vloger.dart @@ -521,7 +521,7 @@ class MyPageState extends State with SingleTickerProviderStateMixin { /// 关注按钮 Widget _buildFoucsButton(BuildContext context) { // final vlogerId = '1943510443312078850'; // 18832510385,后面改回博主的id - final vlogerId = args['vlogerId']; + final vlogerId = args['memberId']; return AnimatedSwitcher( duration: const Duration(milliseconds: 300), diff --git a/lib/pages/video/index.dart b/lib/pages/video/index.dart index 0ca9d6e..94b3ecb 100644 --- a/lib/pages/video/index.dart +++ b/lib/pages/video/index.dart @@ -4,6 +4,7 @@ library; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import '../../IM/im_core.dart'; import '../../behavior/custom_scroll_behavior.dart'; import '../../components/keepalive_wrapper.dart'; import '../../controller/video_module_controller.dart'; @@ -13,7 +14,7 @@ import './module/recommend.dart'; // import './module/buying.dart'; // import './module/drama.dart'; // import './module/live.dart'; -import 'module/friend.dart'; +import './module/friend.dart'; // 引入tab内容模块 // import './module/subscribe.dart'; @@ -137,18 +138,19 @@ class _VideoPageState extends State with SingleTickerProviderStateMix child: PageView( controller: pageController, onPageChanged: (index) { + logger.i('$index'); // 根据当前 tab 控制对应播放器播放,其它暂停 if (index == 0) { - // AttentionModule.playVideo(); - // FriendModule.pauseVideo(); + AttentionModule.playVideo(); + // FriendModule.pauseVideo(); RecommendModule.pauseVideo(); } else if (index == 1) { - // AttentionModule.pauseVideo(); - // FriendModule.playVideo(); + AttentionModule.pauseVideo(); + // FriendModule.playVideo(); RecommendModule.pauseVideo(); } else if (index == 2) { - // AttentionModule.pauseVideo(); - // FriendModule.pauseVideo(); + AttentionModule.pauseVideo(); + // FriendModule.pauseVideo(); RecommendModule.playVideo(); } videoModuleController.updateVideoTabIndex(index); diff --git a/lib/pages/video/module/attention.dart b/lib/pages/video/module/attention.dart index c9a152e..0fe12c5 100644 --- a/lib/pages/video/module/attention.dart +++ b/lib/pages/video/module/attention.dart @@ -1,26 +1,1005 @@ -/// 关注模块 +/// 精选推荐模块 library; +import 'dart:async'; +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:loopin/IM/controller/chat_controller.dart'; +import 'package:loopin/IM/im_core.dart'; +import 'package:loopin/IM/im_message.dart'; +import 'package:loopin/api/video_api.dart'; +import 'package:loopin/components/my_toast.dart'; +import 'package:loopin/components/network_or_asset_image.dart'; +import 'package:loopin/models/summary_type.dart'; +import 'package:loopin/service/http.dart'; +import 'package:loopin/utils/wxsdk.dart'; +import 'package:media_kit/media_kit.dart'; +import 'package:media_kit_video/media_kit_video.dart'; +import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart'; +import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; + +import '../../../behavior/custom_scroll_behavior.dart'; +import '../../../controller/video_module_controller.dart'; +import '../../../router/fade_route.dart'; +import '../components/popup_reply.dart'; class AttentionModule extends StatefulWidget { - const AttentionModule({ super.key }); + const AttentionModule({super.key}); + static Player? _player; + + static void setPlayer(Player player) { + _player = player; + } + + static void pauseVideo() { + _player?.pause(); + } + + static void playVideo() { + _player?.play(); + } @override State createState() => _AttentionModuleState(); } class _AttentionModuleState 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; + bool isLoadingMore = false; + + // 页面controller + late PageController pageController = PageController( + initialPage: videoModuleController.videoPlayIndex.value, + viewportFraction: 1.0, + ); + + // 播放器controller + late Player player = Player(); + late VideoController videoController = VideoController(player); + + final List subscriptions = []; + // 进度条slider当前阈值 + double sliderValue = 0.0; + bool sliderDraging = false; + late Duration position = Duration.zero; // 当前时长 + 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': '哪怕身处谷底,只要抬头仰望,便能看见漫天繁星,心怀希望,就能找到出路,奔赴美好。'}, + ]; + // 分享列表 + List shareList = [ + {'icon': 'assets/images/share-wx.png', 'label': '好友'}, + {'icon': 'assets/images/share-wx.png', 'label': '微信'}, + {'icon': 'assets/images/share-pyq.png', 'label': '朋友圈'}, + {'icon': 'assets/images/share-link.png', 'label': '复制链接'}, + {'icon': 'assets/images/share-download.png', 'label': '下载'}, + {'icon': 'assets/images/share-download.png', 'label': '下载'}, + {'icon': 'assets/images/share-download.png', 'label': '下载'}, + {'icon': 'assets/images/share-download.png', 'label': '下载'}, + {'icon': 'assets/images/share-download.png', 'label': '下载下载下载下载下载下载下载下载下载下载下载下载'}, + {'icon': 'assets/images/share-download.png', 'label': '下载下载下载下载下载下载下载下载下载下载下载下载'}, + ]; + + @override + void initState() { + super.initState(); +/* videoModuleController.needRefresh.listen((need) { + if (need) { + reInit(); + videoModuleController.clearNeedRefresh(); + } + });*/ + AttentionModule.setPlayer(player); + // 获取视频数据 + logger.d('AttentionModule initState1111111111111111111111111111'); + fetchVideoList(); + } + + @override + void setState(VoidCallback fn) { + if (mounted) { + super.setState(fn); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (subscriptions.isEmpty) { + subscriptions.addAll( + [ + // 监听视频时长 + player.stream.duration.listen((event) { + setState(() { + duration = event; + }); + }), + // 监听视频播放进度 + player.stream.position.listen((event) { + setState(() { + position = event; + if (position > Duration.zero && !sliderDraging) { + // 设置视频播放位置 + sliderValue = (position.inMilliseconds / duration.inMilliseconds).clamp(0.0, 1.0); + } + }); + }), + ], + ); + } + } + + @override + void dispose() { + player.dispose(); + pageController.dispose(); + for (final subscription in subscriptions) { + subscription.cancel(); + } + super.dispose(); + } + + void reInit() async { + await player.stop(); + // 重置状态 + page = 1; + isLoadingMore = false; + videoList.clear(); + videoModuleController.updateVideoPlayIndex(0); + sliderValue = 0.0; + sliderDraging = false; + position = Duration.zero; + duration = Duration.zero; + pageController.jumpToPage(0); + + // 拉新数据 + fetchVideoList(); + } + + Future fetchVideoList() async { + if (isLoadingMore) return; + isLoadingMore = true; + try { + final res = await Http.post(VideoApi.followList, data: { + 'current': page, + 'size': pageSize, + }); + logger.d('关注用户的视频列表:$res'); + final data = res['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; + } + + setState(() { + if (page == 1) { + // 初始化 + videoList = videos; + } else { + videoList.addAll(videos); + } + }); + // 处理完成后 + if (videos.isNotEmpty) { + page++; + } + logger.i('获取新的视频数据了'); + + // 初始化播放器 + player.open( + Media( + videoList[videoModuleController.videoPlayIndex.value]['url'], + ), + play: false); + player.setPlaylistMode(PlaylistMode.loop); // 循环播放; + } + } catch (e) { + logger.i('获取视频失败: $e'); + } finally { + isLoadingMore = false; // 加载完成,标记为 false + } + } + + // 评论弹框 + void handleComment(index) { + //获取评论数据 + + 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'); + }, + ))); + }, + ), + ], + ), + ); + }, + ); + } + + // 分享弹框 + void handleShare(index) { + if (chatController.chatList.isNotEmpty) { + chatController.getConversationList(); + } + showModalBottomSheet( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)), + ), + clipBehavior: Clip.antiAlias, + context: context, + isScrollControlled: true, + builder: (context) { + return Material( + color: Colors.white, + child: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 分享列表 + SizedBox( + height: 110, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: shareList.length, + padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0), + itemBuilder: (context, index) { + return GestureDetector( + onTap: () => handleShareClick(index), + child: Container( + width: 64, + margin: EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset('${shareList[index]['icon']}', width: 48.0), + SizedBox(height: 5), + Text( + '${shareList[index]['label']}', + style: TextStyle(fontSize: 12.0), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + }, + ), + ), + + // 会话列表 + 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, + ), + ], + ), + ), + ); + }, + ), + ); + }), + + // 取消按钮 + SafeArea( + top: false, + child: InkWell( + onTap: () => Get.back(), + child: Container( + alignment: Alignment.center, + width: double.infinity, + height: 50.0, + color: Colors.grey[50], + child: Text( + '取消', + style: TextStyle(color: Colors.black87), + ), + ), + ), + ), + ], + ), + ), + ); + }, + ); + } + + void handleShareClick(int index) { + print("分享项 $index 被点击"); + final description = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '获取title失败'; + + if (index == 1) { + // 好友 + Wxsdk.shareToFriend(title: '快来看看这个视频', description: description, webpageUrl: 'https://baidu.com'); + } else if (index == 2) { + // 朋友圈 + Wxsdk.shareToTimeline(title: '快来看看这个视频', webpageUrl: 'https://baidu.com'); + } + } + + void handlCoverClick(V2TimConversation conv) async { + // 发送VideoMsg,获取当前视频信息 + final userId = conv.userID; + final String url = videoList[videoModuleController.videoPlayIndex.value]['url']; + final img = videoList[videoModuleController.videoPlayIndex.value]['firstFrameImg']; + final width = videoList[videoModuleController.videoPlayIndex.value]['width']; + final height = videoList[videoModuleController.videoPlayIndex.value]['height']; + final makeJson = jsonEncode({ + "width": width, + "height": height, + "imgUrl": img, + "videoUrl": url, + }); + final res = await IMMessage().createCustomMessage( + data: makeJson, + ); + if (res.success) { + final sendRes = await IMMessage().sendMessage(msg: res.data!.messageInfo!, toUserID: userId, cloudCustomData: SummaryType.shareVideo); + if (sendRes.success) { + MyToast().tip( + title: '分享成功', + position: 'center', + type: 'success', + ); + Get.back(); + } else { + logger.e(res.desc); + } + } else { + logger.e(res.desc); + } + } + @override Widget build(BuildContext context) { + // super.build(context); return Container( - color: Colors.amber[200], + color: Colors.black, child: Column( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 5.0, children: [ - Image.asset('assets/images/empty.png', width: 100.0,), - Text('暂无关注信息~', style: TextStyle(color: Colors.white54, fontSize: 14.0),), + Expanded( + child: Stack( + 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; + duration = Duration.zero; + }); + + player.stop(); + // await player.open(Media(videoList[index]['src'])); + await player.open(Media(videoList[index]['url'])); + // 如果滚动到列表末尾,且还有更多数据 + if (index == videoList.length - 2 && !isLoadingMore) { + await fetchVideoList(); // 拉取更多 + } + }, + itemCount: videoList.length, + itemBuilder: (context, index) { + final videoWidth = videoList[index]['width'] ?? 1; + final videoHeight = videoList[index]['height'] ?? 1; // 防止除以0 + final isHorizontal = videoWidth > videoHeight; + return Stack( + children: [ + // 视频区域 + Positioned( + top: 0, + left: 0, + right: 0, + bottom: 0, + 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), + child: Image.network( + videoList[index]['firstFrameImg'] ?? 'https://wuzhongjie.com.cn/download/logo.png', + fit: isHorizontal ? BoxFit.contain : BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + ), + + // 播放/暂停按钮 + StreamBuilder( + stream: player.stream.playing, + builder: (context, playing) { + return Visibility( + visible: playing.data == false, + child: Center( + child: IconButton( + padding: EdgeInsets.zero, + onPressed: () { + player.playOrPause(); + }, + icon: Icon( + playing.data == true ? Icons.pause : Icons.play_arrow_rounded, + color: Colors.white60, + size: 80, + ), + style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.black.withAlpha(15))), + ), + ), + ); + }, + ), + ], + ), + onTap: () { + player.playOrPause(); + }, + ), + ), + // 右侧操作栏 + Positioned( + bottom: 100.0, + right: 6.0, + child: Column( + spacing: 15.0, + children: [ + // 头像 + Stack( + children: [ + SizedBox( + height: 55.0, + width: 48.0, + child: GestureDetector( + onTap: () { + player.pause(); + Get.toNamed('/vloger', arguments: videoList[videoModuleController.videoPlayIndex.value]); + }, + child: UnconstrainedBox( + alignment: Alignment.topCenter, + child: Container( + height: 48.0, + width: 48.0, + decoration: BoxDecoration( + border: Border.all(color: Colors.white, width: 2.0), + borderRadius: BorderRadius.circular(100.0), + ), + child: ClipOval( + child: NetworkOrAssetImage( + imageUrl: videoList[index]['avatar'], + ), + ), + ), + ), + ), + ), + Positioned( + bottom: 0, + left: 15.0, + child: InkWell( + child: Container( + height: 18.0, + width: 18.0, + decoration: BoxDecoration( + color: videoList[index]['doIFollowVloger'] ? Colors.white : Color(0xFFFF5000), + borderRadius: BorderRadius.circular(100.0), + ), + child: Icon( + videoList[index]['doIFollowVloger'] ? Icons.check : Icons.add, + color: videoList[index]['doIFollowVloger'] ? Color(0xFFFF5000) : Colors.white, + size: 14.0, + ), + ), + onTap: () { + setState(() { + videoList[index]['doIFollowVloger'] = !videoList[index]['doIFollowVloger']; + }); + }, + ), + ), + ], + ), + // 点赞 + GestureDetector( + child: Column( + children: [ + SvgPicture.asset( + 'assets/images/svg/heart.svg', + colorFilter: ColorFilter.mode(videoList[index]['doILikeThisVlog'] ? Color(0xFFFF5000) : Colors.white, BlendMode.srcIn), + height: 40.0, + width: 40.0, + ), + Text( + '${videoList[index]['likeCounts'] + (videoList[index]['doILikeThisVlog'] ? 1 : 0)}', + style: TextStyle(color: Colors.white, fontSize: 12.0), + ), + ], + ), + onTap: () { + setState(() { + videoList[index]['doILikeThisVlog'] = !videoList[index]['doILikeThisVlog']; + }); + }, + ), + // 评论 + GestureDetector( + child: Column( + children: [ + SvgPicture.asset( + 'assets/images/svg/reply.svg', + colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn), + height: 40.0, + width: 40.0, + ), + Text( + '${videoList[index]['commentsCounts']}', + style: TextStyle(color: Colors.white, fontSize: 12.0), + ), + ], + ), + onTap: () { + 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: [ + SvgPicture.asset( + 'assets/images/svg/share.svg', + colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn), + height: 40.0, + width: 40.0, + ), + ], + ), + onTap: () { + handleShare(index); + }, + ), + //举报 + GestureDetector( + child: Column( + children: [ + SvgPicture.asset( + 'assets/images/svg/report.svg', + colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn), + height: 40.0, + width: 40.0, + ), + ], + ), + onTap: () { + // 举报 + }, + ), + ], + ), + ), + // 底部信息区域 + Positioned( + bottom: 15.0, + left: 10.0, + right: 80.0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '@${videoList[videoModuleController.videoPlayIndex.value]['nickname'] ?? '未知'}', + 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), + ); + final tp = TextPainter( + text: span, + maxLines: 3, + textDirection: TextDirection.ltr, + ); + tp.layout(maxWidth: constraints.maxWidth); + final isOverflow = tp.didExceedMaxLines; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + text, + maxLines: videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? null : 3, + overflow: + videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? TextOverflow.visible : TextOverflow.ellipsis, + style: const TextStyle(color: Colors.white, fontSize: 14.0), + ), + if (isOverflow) + Padding( + padding: const EdgeInsets.only(top: 6.0), + child: GestureDetector( + onTap: () { + setState(() { + videoList[videoModuleController.videoPlayIndex.value]['expanded'] = + !videoList[videoModuleController.videoPlayIndex.value]['expanded']; + }); + }, + child: Text( + videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? '收起' : '展开更多', + textAlign: TextAlign.right, + style: const TextStyle( + color: Colors.white, + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ); + }, + ), + ], + )), + // mini播放进度条 + Positioned( + bottom: 0.0, + left: 6.0, + right: 6.0, + child: Visibility( + visible: videoModuleController.videoPlayIndex.value == index && position > Duration.zero, + child: Listener( + 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, // 设置滑块覆盖层的颜色 + ), + 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(); + } + }, + ), + ), + onPointerMove: (e) { + setState(() { + sliderDraging = true; + }); + }, + ), + ), + ), + // 播放位置指示器 + Positioned( + bottom: 100.0, + left: 10.0, + right: 10.0, + child: Visibility( + visible: sliderDraging, + child: DefaultTextStyle( + style: TextStyle(color: Colors.white54, fontSize: 18.0, fontFamily: 'Arial'), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 8.0, + children: [ + Text(position.label(reference: duration), style: TextStyle(color: Colors.white)), + Text('/', style: TextStyle(fontSize: 14.0)), + Text(duration.label(reference: duration)), + ], + ), + )), + ), + ], + ); + }, + ), + + /// 固定层 + // 红包广告,先不做 + // Ads(), + ], + ), + ), ], ), ); diff --git a/lib/pages/video/module/recommend.dart b/lib/pages/video/module/recommend.dart index 2b7fbfd..5d889e1 100644 --- a/lib/pages/video/module/recommend.dart +++ b/lib/pages/video/module/recommend.dart @@ -185,14 +185,14 @@ class _RecommendModuleState extends State { 'size': pageSize, }); 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['rows'] is List) { - List videos = data['rows']; + if (data['records'] is List) { + List videos = data['records']; for (var item in videos) { // print("喜欢:${item['likeCounts']}"); // print("评论:${item['commentsCounts']}"); diff --git a/lib/update/upgrade_dialog.dart b/lib/update/upgrade_dialog.dart index c9c8caf..143e368 100644 --- a/lib/update/upgrade_dialog.dart +++ b/lib/update/upgrade_dialog.dart @@ -44,7 +44,7 @@ class UpgradeDialog extends StatelessWidget { onPressed: onConfirm, child: Text("立即更新"), ), - if (!force) + if (force) TextButton( onPressed: () => Navigator.pop(context), child: Text("暂不更新"),