初始化
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_model/video_model.dart';
|
||||
import 'package:hgdj/hj_page/drama/drama_detail_page.dart';
|
||||
import 'package:hgdj/hj_page/drama/drama_list_page.dart';
|
||||
import 'package:hgdj/hj_page/short_video/view/media_action_menu.dart';
|
||||
import 'package:hgdj/hj_page/short_video/view/video_menu_kit.dart';
|
||||
import 'package:hgdj/hj_utils/const.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import '../../../tools_base/widget/shrink_wrap.dart';
|
||||
|
||||
/// 短剧操作台:左侧「剧名 + 第N集 + 标签」+ 右侧动作菜单 + 中间暂停播放按钮。
|
||||
/// 点赞/收藏/评论/分享全部挂在**剧**上,下载则是当前这一集;短剧没有作者人设
|
||||
class DramaMenu extends StatefulWidget {
|
||||
final VideoPlayerController? playerCtr;
|
||||
final VideoModel? videoModel;
|
||||
final VoidCallback? onSwitchLine; //切换 CDN 线路后重新起播
|
||||
final bool showEntry; //剧名是否带箭头可点进二级页;二级页自身传 false,否则会重复入栈
|
||||
|
||||
/// 信息栏底部要距播放器底多远。由播放器按「进度条 + 剧集条 + 底部安全区」算好传进来——
|
||||
/// 写死过一次(40+48),二级页在带手势条的机型上安全区一顶就和进度条压到一起了
|
||||
final double bottomInset;
|
||||
|
||||
const DramaMenu({
|
||||
super.key,
|
||||
this.playerCtr,
|
||||
this.videoModel,
|
||||
this.onSwitchLine,
|
||||
this.showEntry = true,
|
||||
required this.bottomInset,
|
||||
});
|
||||
|
||||
@override
|
||||
State<DramaMenu> createState() => _DramaMenuState();
|
||||
}
|
||||
|
||||
class _DramaMenuState extends State<DramaMenu> {
|
||||
//信息栏各行的上间距(设计稿 剧名→标签行 12)
|
||||
double get gapValue => 12;
|
||||
|
||||
VideoModel? get _video => widget.videoModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
VideoMenuSwitch.addListen(_menuListen);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
VideoMenuSwitch.removeLister(_menuListen);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _menuListen(bool isShow) {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
//外层那个 bottom:4 已经占掉一部分,这里补齐到调用方要求的总距离
|
||||
padding: EdgeInsets.only(bottom: widget.bottomInset - 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MenuReveal(
|
||||
visible: VideoMenuSwitch.isShow,
|
||||
offset: const Offset(-0.1, 0), // 隐藏时向左滑出
|
||||
child: _buildLeftPanel(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
MediaActionMenu(
|
||||
style: MediaStyle.Drama,
|
||||
videoModel: _video,
|
||||
playerCtr: widget.playerCtr,
|
||||
onSwitchLine: widget.onSwitchLine,
|
||||
),
|
||||
],
|
||||
),
|
||||
12.sizeBoxW,
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.playerCtr != null)
|
||||
CenterPlayPause(controller: widget.playerCtr!),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ============ 左侧信息栏(剧名 / 第N集 + 标签)============
|
||||
|
||||
Widget _buildLeftPanel() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
//剧名,二级页里不带箭头也不可点
|
||||
_buildTitleItem(),
|
||||
//第N集 + 标签
|
||||
_buildTagItem(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTitleItem() {
|
||||
final title = _video?.title?.trim() ?? '';
|
||||
if (title.isEmpty) return const SizedBox();
|
||||
final content = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
if (widget.showEntry)
|
||||
Image.asset('chevron_right.webp'.videoPath, width: 20, height: 20),
|
||||
],
|
||||
);
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: gapValue),
|
||||
child: widget.showEntry
|
||||
? InkWell(enableFeedback: false, onTap: _openDrama, child: content)
|
||||
: content,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTagItem() {
|
||||
final episodeText = Text(
|
||||
"第${_video?.episodeNo ?? 1}集",
|
||||
style: const TextStyle(
|
||||
color: Colors.white, fontSize: 13, fontWeight: FontWeight.w500),
|
||||
);
|
||||
if (_video?.tags?.isNotEmpty != true) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: gapValue), child: episodeText);
|
||||
}
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: gapValue),
|
||||
child: Row(
|
||||
children: [
|
||||
episodeText,
|
||||
6.sizeBoxW,
|
||||
Expanded(child: _tagWrap()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _tagWrap() {
|
||||
return ShrinkWrap(
|
||||
spacing: 0,
|
||||
runSpacing: 6,
|
||||
maxLines: 1,
|
||||
children: _video?.tags
|
||||
?.map((e) => InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
widget.playerCtr?.pause();
|
||||
//短剧标签要出的是「剧」,不能用视频标签页——那边的卡片点开跳的是视频详情页
|
||||
DramaListPage.toTag(e);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(8, 3, 8, 3),
|
||||
margin: const EdgeInsets.only(right: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x0DFFFFFF),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Text(e.name ?? '',
|
||||
style: const TextStyle(
|
||||
color: Color(0xffffffff), fontSize: 11)),
|
||||
),
|
||||
))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/// 进短剧二级页,定位到当前这一集。只有刷剧模式挂得上(二级页 showEntry=false)
|
||||
void _openDrama() {
|
||||
widget.playerCtr?.pause();
|
||||
final index = (_video?.episodeNo ?? 1) - 1;
|
||||
//把这一刻的集数+秒数带进连播模式,写法与 DramaVideoPlayer._openDrama 一致
|
||||
Get.to(
|
||||
() => DramaDetailPage(
|
||||
drama: _video?.dramaInfo,
|
||||
initialIndex: index < 0 ? 0 : index,
|
||||
initialSeconds: widget.playerCtr?.value.position.inSeconds ?? 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user