2025-07-21 15:46:30 +08:00
|
|
|
|
/// 精选推荐模块
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
import 'dart:async';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'dart:convert';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
|
|
|
import 'package:get/get.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:loopin/IM/im_core.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/IM/im_message.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:loopin/api/video_api.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/components/my_toast.dart';
|
|
|
|
|
import 'package:loopin/components/network_or_asset_image.dart';
|
|
|
|
|
import 'package:loopin/models/summary_type.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:loopin/service/http.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/utils/wxsdk.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
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';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
import '../../../behavior/custom_scroll_behavior.dart';
|
|
|
|
|
import '../../../controller/video_module_controller.dart';
|
|
|
|
|
import '../../../router/fade_route.dart';
|
|
|
|
|
import '../components/popup_reply.dart';
|
|
|
|
|
|
|
|
|
|
class RecommendModule extends StatefulWidget {
|
|
|
|
|
const RecommendModule({super.key});
|
|
|
|
|
static Player? _player;
|
|
|
|
|
|
|
|
|
|
static void setPlayer(Player player) {
|
|
|
|
|
_player = player;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void pauseVideo() {
|
|
|
|
|
_player?.pause();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void playVideo() {
|
|
|
|
|
_player?.play();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<RecommendModule> createState() => _RecommendModuleState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _RecommendModuleState extends State<RecommendModule> {
|
|
|
|
|
VideoModuleController videoModuleController = Get.put(VideoModuleController());
|
2025-08-21 10:50:38 +08:00
|
|
|
|
final ChatController chatController = Get.find<ChatController>();
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
// class _RecommendModuleState extends State<RecommendModule> with AutomaticKeepAliveClientMixin {
|
|
|
|
|
// @override
|
|
|
|
|
// bool get wantKeepAlive => true;
|
|
|
|
|
// VideoModuleController videoModuleController = Get.find<VideoModuleController>();
|
|
|
|
|
|
|
|
|
|
// 分页内容
|
|
|
|
|
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<StreamSubscription> 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 = [
|
2025-08-21 10:50:38 +08:00
|
|
|
|
{'icon': 'assets/images/share-wx.png', 'label': '好友'},
|
2025-07-21 15:46:30 +08:00
|
|
|
|
{'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': '下载'},
|
2025-08-21 10:50:38 +08:00
|
|
|
|
{'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': '下载下载下载下载下载下载下载下载下载下载下载下载'},
|
2025-07-21 15:46:30 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
videoModuleController.needRefresh.listen((need) {
|
|
|
|
|
if (need) {
|
|
|
|
|
reInit();
|
|
|
|
|
videoModuleController.clearNeedRefresh();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
RecommendModule.setPlayer(player);
|
|
|
|
|
// 获取视频数据
|
|
|
|
|
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<void> fetchVideoList() async {
|
|
|
|
|
if (isLoadingMore) return;
|
|
|
|
|
isLoadingMore = true;
|
|
|
|
|
try {
|
|
|
|
|
final res = await Http.post(VideoApi.vlogList, data: {
|
|
|
|
|
'current': page,
|
|
|
|
|
'size': pageSize,
|
|
|
|
|
});
|
|
|
|
|
final data = res['data'];
|
2025-08-21 17:50:34 +08:00
|
|
|
|
logger.d('关注用户的视频列表:$data');
|
2025-07-21 15:46:30 +08:00
|
|
|
|
if (data == null || (data is List && data.isEmpty)) {
|
|
|
|
|
// MyDialog.toast('没有更多了', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-21 17:50:34 +08:00
|
|
|
|
if (data['records'] is List) {
|
|
|
|
|
List videos = data['records'];
|
2025-08-21 10:50:38 +08:00
|
|
|
|
for (var item in videos) {
|
|
|
|
|
// print("喜欢:${item['likeCounts']}");
|
|
|
|
|
// print("评论:${item['commentsCounts']}");
|
|
|
|
|
// logger.i(item);
|
|
|
|
|
item['expanded'] = false;
|
|
|
|
|
}
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
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); // 循环播放;
|
|
|
|
|
|
|
|
|
|
// 第一次加载后播放第一个视频
|
|
|
|
|
if (page == 2 && videoModuleController.videoTabIndex.value == 2 && Get.currentRoute == '/' && videoModuleController.layoutPageCurrent.value == 0) {
|
|
|
|
|
player.play(); // 播放第一个
|
|
|
|
|
} else {
|
|
|
|
|
logger.i('没播放视频');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.i('获取视频失败: $e');
|
|
|
|
|
} finally {
|
|
|
|
|
isLoadingMore = false; // 加载完成,标记为 false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 评论弹框
|
|
|
|
|
void handleComment(index) {
|
2025-08-21 10:50:38 +08:00
|
|
|
|
//获取评论数据
|
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
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) {
|
2025-08-21 10:50:38 +08:00
|
|
|
|
if (chatController.chatList.isNotEmpty) {
|
|
|
|
|
chatController.getConversationList();
|
|
|
|
|
}
|
2025-07-21 15:46:30 +08:00
|
|
|
|
showModalBottomSheet(
|
|
|
|
|
backgroundColor: Colors.white,
|
2025-08-21 10:50:38 +08:00
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
clipBehavior: Clip.antiAlias,
|
|
|
|
|
context: context,
|
2025-08-21 10:50:38 +08:00
|
|
|
|
isScrollControlled: true,
|
2025-07-21 15:46:30 +08:00
|
|
|
|
builder: (context) {
|
|
|
|
|
return Material(
|
|
|
|
|
color: Colors.white,
|
2025-08-21 10:50:38 +08:00
|
|
|
|
child: Padding(
|
|
|
|
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
child: Column(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
2025-07-21 15:46:30 +08:00
|
|
|
|
children: [
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// 分享列表
|
|
|
|
|
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),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
child: Column(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
2025-07-21 15:46:30 +08:00
|
|
|
|
children: [
|
|
|
|
|
Image.asset('${shareList[index]['icon']}', width: 48.0),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
SizedBox(height: 5),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
Text(
|
|
|
|
|
'${shareList[index]['label']}',
|
|
|
|
|
style: TextStyle(fontSize: 12.0),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
// 会话列表
|
|
|
|
|
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,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// 取消按钮
|
|
|
|
|
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),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-21 10:50:38 +08:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
// super.build(context);
|
|
|
|
|
return Container(
|
|
|
|
|
color: Colors.black,
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
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,
|
2025-08-21 10:50:38 +08:00
|
|
|
|
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'],
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
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'];
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// 点赞
|
2025-07-21 15:46:30 +08:00
|
|
|
|
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'];
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// 评论
|
2025-07-21 15:46:30 +08:00
|
|
|
|
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),
|
|
|
|
|
// ),
|
|
|
|
|
// ],
|
|
|
|
|
// ),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// 转发
|
2025-07-21 15:46:30 +08:00
|
|
|
|
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);
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
//举报
|
|
|
|
|
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: () {
|
|
|
|
|
// 举报
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
// 底部信息区域
|
|
|
|
|
Positioned(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
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,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
// 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(),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|