From 31405750e619914c0dabb0dfbdb015270203b1e0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 10 Mar 2024 22:44:07 +0800 Subject: [PATCH] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E9=A1=B5=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 84 ++++ lib/pages/video/detail/view.dart | 450 +++++++++--------- .../video/detail/widgets/header_control.dart | 119 ----- 3 files changed, 308 insertions(+), 345 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index b4005b5a5..40723efae 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -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 ? '发送中...' : '发送'), + ); + }) + ], + ); + }, + ); + } } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 04d41b2e3..e8ca935de 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -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 with TickerProviderStateMixin, RouteAware { - late VideoDetailController videoDetailController; + late VideoDetailController vdCtr; PlPlayerController? plPlayerController; final ScrollController _extendNestCtr = ScrollController(); late StreamController appbarStream; @@ -65,20 +65,18 @@ class _VideoDetailPageState extends State 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 appbarStreamListen(); fullScreenStatusListener(); if (Platform.isAndroid) { - floating = videoDetailController.floating!; + floating = vdCtr.floating!; autoEnterPip(); } } // 获取视频资源,初始化播放器 Future 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 /// 顺序播放 列表循环 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 } // 播放完展示控制栏 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 /// 未开启自动播放时触发播放 Future 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 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 /// 开启 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 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 @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 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 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 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 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: [ - 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) ...[ - 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 ), ]; }, - // 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 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: [ Builder( builder: (BuildContext context) { return CustomScrollView( key: const PageStorageKey('简介'), slivers: [ - 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 ), Obx( () => VideoReplyPanel( - bvid: videoDetailController.bvid, - oid: videoDetailController.oid.value, + bvid: vdCtr.bvid, + oid: vdCtr.oid.value, ), ) ], @@ -543,56 +572,26 @@ class _VideoDetailPageState extends State /// 重新进入会刷新 // 播放完成/暂停播放 - // 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 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']); }, ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e72e6a694..a8414c189 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -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 { ); } - /// 发送弹幕 - 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 scheduleTimeChoices = [ @@ -1166,41 +1082,6 @@ class _HeaderControlState extends State { // ), // 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) ...[ SizedBox(