Merge pull request #627 from guozhigq/feature-updateVideoDetailStructure

Feature update video detail structure
This commit is contained in:
guozhigq
2024-03-14 00:08:32 +08:00
committed by GitHub
3 changed files with 308 additions and 345 deletions

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/common/reply_type.dart';
@ -19,6 +20,7 @@ import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/utils/video_utils.dart';
import 'package:screen_brightness/screen_brightness.dart';
import '../../../http/danmaku.dart';
import '../../../utils/id_utils.dart';
import 'widgets/header_control.dart';
@ -383,4 +385,86 @@ class VideoDetailController extends GetxController
? replyReplyBottomSheetCtr!.close()
: print('replyReplyBottomSheetCtr is null');
}
/// 发送弹幕
void showShootDanmakuSheet() {
final TextEditingController textController = TextEditingController();
bool isSending = false; // 追踪是否正在发送
showDialog(
context: Get.context!,
builder: (BuildContext context) {
// TODO: 支持更多类型和颜色的弹幕
return AlertDialog(
title: const Text('发送弹幕'),
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return TextField(
controller: textController,
);
}),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return TextButton(
onPressed: isSending
? null
: () async {
final String msg = textController.text;
if (msg.isEmpty) {
SmartDialog.showToast('弹幕内容不能为空');
return;
} else if (msg.length > 100) {
SmartDialog.showToast('弹幕内容不能超过100个字符');
return;
}
setState(() {
isSending = true; // 开始发送,更新状态
});
//修改按钮文字
// SmartDialog.showToast('弹幕发送中,\n$msg');
final dynamic res = await DanmakaHttp.shootDanmaku(
oid: cid.value,
msg: textController.text,
bvid: bvid,
progress:
plPlayerController.position.value.inMilliseconds,
type: 1,
);
setState(() {
isSending = false; // 发送结束,更新状态
});
if (res['status']) {
SmartDialog.showToast('发送成功');
// 发送成功,自动预览该弹幕,避免重新请求
// TODO: 暂停状态下预览弹幕仍会移动与计时可考虑添加到dmSegList或其他方式实现
plPlayerController.danmakuController?.addItems([
DanmakuItem(
msg,
color: Colors.white,
time: plPlayerController
.position.value.inMilliseconds,
type: DanmakuItemType.scroll,
isSend: true,
)
]);
Get.back();
} else {
SmartDialog.showToast('发送失败,错误信息为${res['msg']}');
}
},
child: Text(isSending ? '发送中...' : '发送'),
);
})
],
);
},
);
}
}

View File

@ -25,7 +25,7 @@ import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/storage.dart';
import '../../../services/shutdown_timer_service.dart';
import 'widgets/header_control.dart';
import 'widgets/app_bar.dart';
class VideoDetailPage extends StatefulWidget {
const VideoDetailPage({Key? key}) : super(key: key);
@ -38,7 +38,7 @@ class VideoDetailPage extends StatefulWidget {
class _VideoDetailPageState extends State<VideoDetailPage>
with TickerProviderStateMixin, RouteAware {
late VideoDetailController videoDetailController;
late VideoDetailController vdCtr;
PlPlayerController? plPlayerController;
final ScrollController _extendNestCtr = ScrollController();
late StreamController<double> appbarStream;
@ -65,20 +65,18 @@ class _VideoDetailPageState extends State<VideoDetailPage>
void initState() {
super.initState();
heroTag = Get.arguments['heroTag'];
videoDetailController = Get.put(VideoDetailController(), tag: heroTag);
vdCtr = Get.put(VideoDetailController(), tag: heroTag);
videoIntroController = Get.put(
VideoIntroController(bvid: Get.parameters['bvid']!),
tag: heroTag);
videoIntroController.videoDetail.listen((value) {
videoPlayerServiceHandler.onVideoDetailChange(
value, videoDetailController.cid.value);
videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value);
});
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
bangumiIntroController.bangumiDetail.listen((value) {
videoPlayerServiceHandler.onVideoDetailChange(
value, videoDetailController.cid.value);
videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value);
});
videoDetailController.cid.listen((p0) {
vdCtr.cid.listen((p0) {
videoPlayerServiceHandler.onVideoDetailChange(
bangumiIntroController.bangumiDetail.value, p0);
});
@ -93,16 +91,16 @@ class _VideoDetailPageState extends State<VideoDetailPage>
appbarStreamListen();
fullScreenStatusListener();
if (Platform.isAndroid) {
floating = videoDetailController.floating!;
floating = vdCtr.floating!;
autoEnterPip();
}
}
// 获取视频资源,初始化播放器
Future<void> videoSourceInit() async {
_futureBuilderFuture = videoDetailController.queryVideoUrl();
if (videoDetailController.autoPlay.value) {
plPlayerController = videoDetailController.plPlayerController;
_futureBuilderFuture = vdCtr.queryVideoUrl();
if (vdCtr.autoPlay.value) {
plPlayerController = vdCtr.plPlayerController;
plPlayerController!.addStatusLister(playerListener);
}
}
@ -131,10 +129,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
/// 顺序播放 列表循环
if (plPlayerController!.playRepeat != PlayRepeat.pause &&
plPlayerController!.playRepeat != PlayRepeat.singleCycle) {
if (videoDetailController.videoType == SearchType.video) {
if (vdCtr.videoType == SearchType.video) {
videoIntroController.nextPlay();
}
if (videoDetailController.videoType == SearchType.media_bangumi) {
if (vdCtr.videoType == SearchType.media_bangumi) {
bangumiIntroController.nextPlay();
}
}
@ -146,8 +144,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
}
// 播放完展示控制栏
try {
PiPStatus currentStatus =
await videoDetailController.floating!.pipStatus;
PiPStatus currentStatus = await vdCtr.floating!.pipStatus;
if (currentStatus == PiPStatus.disabled) {
plPlayerController!.onLockControl(false);
}
@ -168,17 +165,17 @@ class _VideoDetailPageState extends State<VideoDetailPage>
/// 未开启自动播放时触发播放
Future<void> handlePlay() async {
await videoDetailController.playerInit();
plPlayerController = videoDetailController.plPlayerController;
await vdCtr.playerInit();
plPlayerController = vdCtr.plPlayerController;
plPlayerController!.addStatusLister(playerListener);
videoDetailController.autoPlay.value = true;
videoDetailController.isShowCover.value = false;
vdCtr.autoPlay.value = true;
vdCtr.isShowCover.value = false;
}
void fullScreenStatusListener() {
plPlayerController?.isFullScreen.listen((bool isFullScreen) {
if (isFullScreen) {
videoDetailController.hiddenReplyReplyPanel();
vdCtr.hiddenReplyReplyPanel();
}
});
}
@ -190,8 +187,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
plPlayerController!.removeStatusLister(playerListener);
plPlayerController!.dispose();
}
if (videoDetailController.floating != null) {
videoDetailController.floating!.dispose();
if (vdCtr.floating != null) {
vdCtr.floating!.dispose();
}
videoPlayerServiceHandler.onVideoDetailDispose();
if (Platform.isAndroid) {
@ -207,10 +204,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
/// 开启
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)
as bool) {
videoDetailController.brightness = plPlayerController!.brightness.value;
vdCtr.brightness = plPlayerController!.brightness.value;
}
if (plPlayerController != null) {
videoDetailController.defaultST = plPlayerController!.position.value;
vdCtr.defaultST = plPlayerController!.position.value;
videoIntroController.isPaused = true;
plPlayerController!.removeStatusLister(playerListener);
plPlayerController!.pause();
@ -226,17 +223,16 @@ class _VideoDetailPageState extends State<VideoDetailPage>
plPlayerController!.videoPlayerController != null) {
setState(() => isShowing = true);
}
videoDetailController.isFirstTime = false;
vdCtr.isFirstTime = false;
final bool autoplay = autoPlayEnable;
videoDetailController.playerInit(autoplay: autoplay);
vdCtr.playerInit(autoplay: autoplay);
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
videoDetailController.autoPlay.value =
!videoDetailController.isShowCover.value;
vdCtr.autoPlay.value = !vdCtr.isShowCover.value;
videoIntroController.isPaused = false;
if (_extendNestCtr.position.pixels == 0 && autoplay) {
await Future.delayed(const Duration(milliseconds: 300));
plPlayerController?.seekTo(videoDetailController.defaultST);
plPlayerController?.seekTo(vdCtr.defaultST);
plPlayerController?.play();
}
plPlayerController?.addStatusLister(playerListener);
@ -259,9 +255,158 @@ class _VideoDetailPageState extends State<VideoDetailPage>
@override
Widget build(BuildContext context) {
final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
// final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
final sizeContext = MediaQuery.sizeOf(context);
final _context = MediaQuery.of(context);
late double defaultVideoHeight = sizeContext.width * 9 / 16;
late RxDouble videoHeight = defaultVideoHeight.obs;
final double pinnedHeaderHeight =
statusBarHeight + kToolbarHeight + videoHeight;
statusBarHeight + kToolbarHeight + videoHeight.value;
// ignore: no_leading_underscores_for_local_identifiers
// 竖屏
final bool isPortrait = _context.orientation == Orientation.portrait;
// 横屏
final bool isLandscape = _context.orientation == Orientation.landscape;
final Rx<bool> isFullScreen = plPlayerController?.isFullScreen ?? false.obs;
// 全屏时高度撑满
if (isLandscape || isFullScreen.value == true) {
videoHeight.value = Get.size.height;
enterFullScreen();
} else {
videoHeight.value = defaultVideoHeight;
exitFullScreen();
}
/// 播放器面板
Widget videoPlayerPanel = FutureBuilder(
future: _futureBuilderFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data['status']) {
return Obx(
() {
return !vdCtr.autoPlay.value
? const SizedBox()
: PLVideoPlayer(
controller: plPlayerController!,
headerControl: vdCtr.headerControl,
danmuWidget: Obx(
() => PlDanmaku(
key: Key(vdCtr.danmakuCid.value.toString()),
cid: vdCtr.danmakuCid.value,
playerController: plPlayerController!,
),
),
);
},
);
} else {
// 加载失败异常处理
return const SizedBox();
}
},
);
/// tabbar
Widget tabbarBuild = SizedBox(
width: double.infinity,
height: 45,
child: Row(
children: [
Expanded(
child: TabBar(
controller: vdCtr.tabCtr,
dividerColor: Colors.transparent,
tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(),
),
),
SizedBox(
width: 200,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
height: 32,
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () => vdCtr.showShootDanmakuSheet(),
child: const Text(
'发弹幕',
),
),
),
const SizedBox(width: 4),
SizedBox(
width: 34,
height: 32,
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () {
plPlayerController?.isOpenDanmu.value =
!(plPlayerController?.isOpenDanmu.value ?? false);
},
child: Obx(() => Text(
'',
style: TextStyle(
color: (plPlayerController?.isOpenDanmu.value ??
false)
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.outline,
),
)),
),
),
const SizedBox(width: 14),
],
),
),
),
],
),
);
/// 手动播放
Widget handlePlayPanel() {
return Stack(
children: [
GestureDetector(
onTap: () {
handlePlay();
},
child: NetworkImgLayer(
type: 'emote',
src: vdCtr.videoItem['pic'],
width: Get.width,
height: videoHeight.value,
),
),
Positioned(
top: 0,
left: 0,
right: 0,
child: buildCustomAppBar(),
),
Positioned(
right: 12,
bottom: 10,
child: IconButton(
tooltip: '播放',
onPressed: () => handlePlay(),
icon: Image.asset(
'assets/images/play.png',
width: 60,
height: 60,
)),
),
],
);
}
Widget childWhenDisabled = SafeArea(
top: MediaQuery.of(context).orientation == Orientation.portrait &&
plPlayerController?.isFullScreen.value == true,
@ -273,7 +418,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
children: [
Scaffold(
resizeToAvoidBottomInset: false,
key: videoDetailController.scaffoldKey,
key: vdCtr.scaffoldKey,
backgroundColor: Colors.black,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(0),
@ -299,21 +444,19 @@ class _VideoDetailPageState extends State<VideoDetailPage>
return SliverAppBar(
automaticallyImplyLeading: false,
// 假装使用一个非空变量避免Obx检测不到而罢工
pinned: videoDetailController.autoPlay.value ^
false ^
videoDetailController.autoPlay.value,
pinned: vdCtr.autoPlay.value,
elevation: 0,
scrolledUnderElevation: 0,
forceElevated: innerBoxIsScrolled,
expandedHeight: MediaQuery.of(context).orientation ==
Orientation.landscape ||
plPlayerController?.isFullScreen.value == true
? MediaQuery.sizeOf(context).height -
? (MediaQuery.sizeOf(context).height -
(MediaQuery.of(context).orientation ==
Orientation.landscape
? 0
: MediaQuery.of(context).padding.top)
: videoHeight,
: MediaQuery.of(context).padding.top))
: videoHeight.value,
backgroundColor: Colors.black,
flexibleSpace: FlexibleSpaceBar(
background: PopScope(
@ -333,108 +476,27 @@ class _VideoDetailPageState extends State<VideoDetailPage>
child: LayoutBuilder(
builder: (BuildContext context,
BoxConstraints boxConstraints) {
final double maxWidth =
boxConstraints.maxWidth;
final double maxHeight =
boxConstraints.maxHeight;
// final double maxWidth =
// boxConstraints.maxWidth;
// final double maxHeight =
// boxConstraints.maxHeight;
return Stack(
children: <Widget>[
if (isShowing)
FutureBuilder(
future: _futureBuilderFuture,
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.hasData &&
snapshot.data['status']) {
return Obx(
() =>
!videoDetailController
.autoPlay.value
? nil
: PLVideoPlayer(
controller:
plPlayerController!,
headerControl:
videoDetailController
.headerControl,
danmuWidget: Obx(
() => PlDanmaku(
key: Key(videoDetailController
.danmakuCid
.value
.toString()),
cid: videoDetailController
.danmakuCid
.value,
playerController:
plPlayerController!,
),
),
),
);
} else {
return buildCustomAppBar();
}
},
),
if (isShowing) videoPlayerPanel,
/// 关闭自动播放时 手动播放
if (!videoDetailController
.autoPlay.value) ...<Widget>[
Obx(
() => Visibility(
visible: videoDetailController
.isShowCover.value,
child: Positioned(
top: 0,
left: 0,
right: 0,
child: GestureDetector(
onTap: () {
handlePlay();
},
child: NetworkImgLayer(
type: 'emote',
src: videoDetailController
.videoItem['pic'],
width: maxWidth,
height: maxHeight,
),
),
),
Obx(
() => Visibility(
visible: !vdCtr.autoPlay.value &&
vdCtr.isShowCover.value,
child: Positioned(
top: 0,
left: 0,
right: 0,
child: handlePlayPanel(),
),
),
Obx(
() => Visibility(
visible: videoDetailController
.isShowCover.value &&
videoDetailController
.isEffective.value,
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
child: buildCustomAppBar(),
),
Positioned(
right: 12,
bottom: 10,
child: IconButton(
tooltip: '播放',
onPressed: () =>
handlePlay(),
icon: Image.asset(
'assets/images/play.png',
width: 60,
height: 60,
)),
),
],
)),
),
]
),
],
);
},
@ -445,18 +507,16 @@ class _VideoDetailPageState extends State<VideoDetailPage>
),
];
},
// pinnedHeaderSliverHeightBuilder: () {
// return playerStatus != PlayerStatus.playing
// ? statusBarHeight + kToolbarHeight
// : pinnedHeaderHeight;
// },
/// 不收回
pinnedHeaderSliverHeightBuilder: () {
return MediaQuery.of(context).orientation ==
Orientation.landscape ||
plPlayerController?.isFullScreen.value == true
? MediaQuery.sizeOf(context).height
: pinnedHeaderHeight;
: playerStatus != PlayerStatus.playing
? kToolbarHeight
: pinnedHeaderHeight;
},
onlyOneScrollInBody: true,
body: ColoredBox(
@ -464,54 +524,23 @@ class _VideoDetailPageState extends State<VideoDetailPage>
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
Opacity(
opacity: 0,
child: SizedBox(
width: double.infinity,
height: 0,
child: Obx(
() => TabBar(
controller: videoDetailController.tabCtr,
dividerColor: Colors.transparent,
indicatorColor:
Theme.of(context).colorScheme.background,
tabs: videoDetailController.tabs
.map((String name) => Tab(text: name))
.toList(),
),
),
),
),
tabbarBuild,
Expanded(
child: TabBarView(
controller: videoDetailController.tabCtr,
controller: vdCtr.tabCtr,
children: <Widget>[
Builder(
builder: (BuildContext context) {
return CustomScrollView(
key: const PageStorageKey<String>('简介'),
slivers: <Widget>[
if (videoDetailController.videoType ==
SearchType.video) ...[
VideoIntroPanel(
bvid: videoDetailController.bvid),
] else if (videoDetailController.videoType ==
if (vdCtr.videoType == SearchType.video) ...[
VideoIntroPanel(bvid: vdCtr.bvid),
] else if (vdCtr.videoType ==
SearchType.media_bangumi) ...[
Obx(() => BangumiIntroPanel(
cid: videoDetailController.cid.value)),
cid: vdCtr.cid.value)),
],
// if (videoDetailController.videoType ==
// SearchType.video) ...[
// SliverPersistentHeader(
// floating: true,
// pinned: true,
// delegate: SliverHeaderDelegate(
// height: 50,
// child:
// const MenuRow(loadingStatus: false),
// ),
// ),
// ],
SliverToBoxAdapter(
child: Divider(
indent: 12,
@ -528,8 +557,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
),
Obx(
() => VideoReplyPanel(
bvid: videoDetailController.bvid,
oid: videoDetailController.oid.value,
bvid: vdCtr.bvid,
oid: vdCtr.oid.value,
),
)
],
@ -543,56 +572,26 @@ class _VideoDetailPageState extends State<VideoDetailPage>
/// 重新进入会刷新
// 播放完成/暂停播放
// StreamBuilder(
// stream: appbarStream.stream,
// initialData: 0,
// builder: ((context, snapshot) {
// return ScrollAppBar(
// snapshot.data!.toDouble(),
// () => continuePlay(),
// playerStatus,
// null,
// );
// }),
// )
StreamBuilder(
stream: appbarStream.stream,
initialData: 0,
builder: ((context, snapshot) {
return ScrollAppBar(
snapshot.data!.toDouble(),
() => continuePlay(),
playerStatus,
null,
);
}),
)
],
),
);
Widget childWhenEnabled = FutureBuilder(
key: Key(heroTag),
future: _futureBuilderFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data['status']) {
return Obx(
() => !videoDetailController.autoPlay.value
? const SizedBox()
: PLVideoPlayer(
controller: plPlayerController!,
headerControl: HeaderControl(
controller: plPlayerController,
videoDetailCtr: videoDetailController,
bvid: videoDetailController.bvid,
videoType: videoDetailController.videoType,
),
danmuWidget: Obx(
() => PlDanmaku(
key: Key(
videoDetailController.danmakuCid.value.toString()),
cid: videoDetailController.danmakuCid.value,
playerController: plPlayerController!,
),
),
),
);
} else {
return nil;
}
},
);
if (Platform.isAndroid) {
return PiPSwitcher(
childWhenDisabled: childWhenDisabled,
childWhenEnabled: childWhenEnabled,
childWhenEnabled: videoPlayerPanel,
floating: floating,
);
} else {
@ -633,8 +632,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
ComBtn(
icon: const Icon(Icons.history_outlined, size: 22),
fuc: () async {
var res = await UserHttp.toViewLater(
bvid: videoDetailController.bvid);
var res = await UserHttp.toViewLater(bvid: vdCtr.bvid);
SmartDialog.showToast(res['msg']);
},
),

View File

@ -1,5 +1,4 @@
import 'dart:io';
import 'dart:math';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
@ -17,7 +16,6 @@ import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/http/danmaku.dart';
import 'package:pilipala/services/shutdown_timer_service.dart';
import '../../../../models/common/search_type.dart';
import '../../../../models/video_detail_res.dart';
@ -221,88 +219,6 @@ class _HeaderControlState extends State<HeaderControl> {
);
}
/// 发送弹幕
void showShootDanmakuSheet() {
final TextEditingController textController = TextEditingController();
bool isSending = false; // 追踪是否正在发送
showDialog(
context: Get.context!,
builder: (BuildContext context) {
// TODO: 支持更多类型和颜色的弹幕
return AlertDialog(
title: const Text('发送弹幕(测试)'),
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return TextField(
controller: textController,
);
}),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return TextButton(
onPressed: isSending
? null
: () async {
final String msg = textController.text;
if (msg.isEmpty) {
SmartDialog.showToast('弹幕内容不能为空');
return;
} else if (msg.length > 100) {
SmartDialog.showToast('弹幕内容不能超过100个字符');
return;
}
setState(() {
isSending = true; // 开始发送,更新状态
});
//修改按钮文字
// SmartDialog.showToast('弹幕发送中,\n$msg');
final dynamic res = await DanmakaHttp.shootDanmaku(
oid: widget.videoDetailCtr!.cid.value,
msg: textController.text,
bvid: widget.videoDetailCtr!.bvid,
progress:
widget.controller!.position.value.inMilliseconds,
type: 1,
);
setState(() {
isSending = false; // 发送结束,更新状态
});
if (res['status']) {
SmartDialog.showToast('发送成功');
// 发送成功,自动预览该弹幕,避免重新请求
// TODO: 暂停状态下预览弹幕仍会移动与计时可考虑添加到dmSegList或其他方式实现
widget.controller!.danmakuController!.addItems([
DanmakuItem(
msg,
color: Colors.white,
time: widget
.controller!.position.value.inMilliseconds,
type: DanmakuItemType.scroll,
isSend: true,
)
]);
Get.back();
} else {
SmartDialog.showToast('发送失败,错误信息为${res['msg']}');
}
},
child: Text(isSending ? '发送中...' : '发送'),
);
})
],
);
},
);
}
/// 定时关闭
void scheduleExit() async {
const List<int> scheduleTimeChoices = [
@ -1166,41 +1082,6 @@ class _HeaderControlState extends State<HeaderControl> {
// ),
// fuc: () => _.screenshot(),
// ),
SizedBox(
width: 56,
height: 34,
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () => showShootDanmakuSheet(),
child: const Text(
'发弹幕',
style: textStyle,
),
),
),
SizedBox(
width: 34,
height: 34,
child: Obx(
() => IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () {
_.isOpenDanmu.value = !_.isOpenDanmu.value;
},
icon: Icon(
_.isOpenDanmu.value
? Icons.subtitles_outlined
: Icons.subtitles_off_outlined,
size: 19,
color: Colors.white,
),
),
),
),
SizedBox(width: buttonSpace),
if (Platform.isAndroid) ...<Widget>[
SizedBox(