From fac3c19d3f57bb240dd0893ccc61e98fbe1c44b7 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Wed, 5 Mar 2025 21:49:57 +0800 Subject: [PATCH] mod: show fullscreen action item Closes #367 Signed-off-by: bggRGjQaUbCoE --- lib/pages/setting/widgets/model.dart | 7 + .../video/detail/introduction/controller.dart | 20 + lib/pages/video/detail/introduction/view.dart | 92 +-- .../introduction/widgets/action_item.dart | 62 +- .../video/detail/widgets/header_control.dart | 764 +++++++++++------- lib/plugin/pl_player/controller.dart | 2 + lib/plugin/pl_player/widgets/app_bar_ani.dart | 2 +- lib/utils/storage.dart | 4 + 8 files changed, 559 insertions(+), 394 deletions(-) diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart index 206a9ebd..fef004af 100644 --- a/lib/pages/setting/widgets/model.dart +++ b/lib/pages/setting/widgets/model.dart @@ -2126,6 +2126,13 @@ List get extraSettings => [ GStorage.slideDismissReplyPage = value; }, ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '全屏展示点赞/投币/收藏等操作按钮', + leading: Icon(MdiIcons.dotsHorizontalCircleOutline), + setKey: SettingBoxKey.showFSActionItem, + defaultVal: true, + ), SettingsModel( settingsType: SettingsType.sw1tch, enableFeedback: true, diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index d1a81590..76de0fb7 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -861,4 +861,24 @@ class VideoIntroController extends GetxController } return res; } + + // 收藏 + showFavBottomSheet(BuildContext context, {type = 'tap'}) { + if (userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } + // 快速收藏 & + // 点按 收藏至默认文件夹 + // 长按选择文件夹 + if (enableQuickFav) { + if (type == 'tap') { + actionFavVideo(type: 'default'); + } else { + Utils.showFavBottomSheet(context: context, ctr: this); + } + } else if (type != 'longPress') { + Utils.showFavBottomSheet(context: context, ctr: this); + } + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a77c0b3e..ecbd084f 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -232,26 +232,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } } - // 收藏 - showFavBottomSheet({type = 'tap'}) { - if (videoIntroController.userInfo == null) { - SmartDialog.showToast('账号未登录'); - return; - } - // 快速收藏 & - // 点按 收藏至默认文件夹 - // 长按选择文件夹 - if (videoIntroController.enableQuickFav) { - if (type == 'tap') { - videoIntroController.actionFavVideo(type: 'default'); - } else { - Utils.showFavBottomSheet(context: context, ctr: videoIntroController); - } - } else if (type != 'longPress') { - Utils.showFavBottomSheet(context: context, ctr: videoIntroController); - } - } - // 视频介绍 showIntroDetail() { if (widget.loadingStatus) { @@ -857,7 +837,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); } - Widget actionGrid(BuildContext context, videoIntroController) { + Widget actionGrid( + BuildContext context, VideoIntroController videoIntroController) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Container( @@ -896,14 +877,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsDown), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), - onTap: () => - handleState(videoIntroController.actionDislikeVideo), - selectStatus: videoIntroController.hasDislike.value, - loadingStatus: widget.loadingStatus, - semanticsLabel: '点踩', - text: "点踩"), + icon: const Icon(FontAwesomeIcons.thumbsDown), + selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), + onTap: () => + handleState(videoIntroController.actionDislikeVideo), + selectStatus: videoIntroController.hasDislike.value, + loadingStatus: widget.loadingStatus, + semanticsLabel: '点踩', + text: "点踩", + ), ), // ActionItem( // icon: const Icon(FontAwesomeIcons.clock), @@ -931,8 +913,9 @@ class _VideoInfoState extends State with TickerProviderStateMixin { key: _favKey, icon: const Icon(FontAwesomeIcons.star), selectIcon: const Icon(FontAwesomeIcons.solidStar), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), + onTap: () => videoIntroController.showFavBottomSheet(context), + onLongPress: () => videoIntroController + .showFavBottomSheet(context, type: 'longPress'), selectStatus: videoIntroController.hasFav.value, loadingStatus: widget.loadingStatus, semanticsLabel: '收藏', @@ -943,31 +926,37 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.comment), - onTap: () => videoDetailCtr.tabCtr - .animateTo(videoDetailCtr.tabCtr.index == 1 ? 0 : 1), - selectStatus: false, - loadingStatus: widget.loadingStatus, - semanticsLabel: '评论', - text: !widget.loadingStatus - ? Utils.numFormat(videoDetail.stat!.reply!) - : '评论'), + icon: const Icon(FontAwesomeIcons.comment), + onTap: () => videoDetailCtr.tabCtr + .animateTo(videoDetailCtr.tabCtr.index == 1 ? 0 : 1), + selectStatus: false, + loadingStatus: widget.loadingStatus, + semanticsLabel: '评论', + text: !widget.loadingStatus + ? Utils.numFormat(videoDetail.stat!.reply!) + : '评论', + ), ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: widget.loadingStatus, - semanticsLabel: '分享', - text: !widget.loadingStatus - ? Utils.numFormat(videoDetail.stat!.share!) - : '分享'), + icon: const Icon(FontAwesomeIcons.shareFromSquare), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + loadingStatus: widget.loadingStatus, + semanticsLabel: '分享', + text: !widget.loadingStatus + ? Utils.numFormat(videoDetail.stat!.share!) + : '分享', + ), ], ), ); }); } - Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) { + Widget actionRow( + BuildContext context, + VideoIntroController videoIntroController, + VideoDetailController videoDetailCtr, + ) { return Row(children: [ Obx( () => ActionRowItem( @@ -994,8 +983,9 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Obx( () => ActionRowItem( icon: const Icon(FontAwesomeIcons.heart), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), + onTap: () => videoIntroController.showFavBottomSheet(context), + onLongPress: () => videoIntroController.showFavBottomSheet(context, + type: 'longPress'), selectStatus: videoIntroController.hasFav.value, loadingStatus: widget.loadingStatus, text: !widget.loadingStatus diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index a22d2805..c7270e32 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:PiliPlus/utils/feed_back.dart'; class ActionItem extends StatefulWidget { - final Icon? icon; + final Icon icon; final Icon? selectIcon; final Function? onTap; final Function? onLongPress; @@ -16,10 +16,11 @@ class ActionItem extends StatefulWidget { final bool needAnim; final bool hasOneThree; final Function? callBack; + final bool? expand; const ActionItem({ super.key, - this.icon, + required this.icon, this.selectIcon, this.onTap, this.onLongPress, @@ -30,6 +31,7 @@ class ActionItem extends StatefulWidget { this.hasOneThree = false, this.callBack, required this.semanticsLabel, + this.expand, }); @override @@ -113,8 +115,10 @@ class ActionItemState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { - return Expanded( - child: Semantics( + return widget.expand == false ? _buildItem : Expanded(child: _buildItem); + } + + Widget get _buildItem => Semantics( label: (widget.text ?? "") + (widget.selectStatus ? "已" : "") + widget.semanticsLabel, @@ -155,42 +159,42 @@ class ActionItemState extends State with TickerProviderStateMixin { Icon( widget.selectStatus ? widget.selectIcon!.icon! - : widget.icon!.icon!, + : widget.icon.icon, size: 18, color: widget.selectStatus ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, + : widget.icon.color ?? + Theme.of(context).colorScheme.outline, ), ], ), - AnimatedOpacity( - opacity: widget.loadingStatus! ? 0 : 1, - duration: const Duration(milliseconds: 200), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition(scale: animation, child: child); - }, - child: Text( - widget.text ?? '', - key: ValueKey(widget.text ?? ''), - style: TextStyle( - color: widget.selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize), - semanticsLabel: "", + if (widget.text != null) + AnimatedOpacity( + opacity: widget.loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + widget.text!, + key: ValueKey(widget.text ?? ''), + style: TextStyle( + color: widget.selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize), + semanticsLabel: "", + ), ), ), - ), ], ), ), - ), - ); - } + ); } class _ArcPainter extends CustomPainter { diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 7b12dffa..c205b62c 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -5,6 +5,7 @@ import 'dart:math'; import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart'; import 'package:PiliPlus/models/common/super_resolution_type.dart'; import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; +import 'package:PiliPlus/pages/video/detail/introduction/widgets/action_item.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -28,7 +29,6 @@ import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/services/shutdown_timer_service.dart'; import '../../../../models/video/play/CDN.dart'; -import '../../../../models/video_detail_res.dart'; import '../../../setting/widgets/select_dialog.dart'; import '../introduction/index.dart'; import 'package:marquee/marquee.dart'; @@ -61,13 +61,14 @@ class _HeaderControlState extends State { double buttonSpace = 8; String get heroTag => widget.heroTag; late VideoIntroController videoIntroController; - late VideoDetailData videoDetail; late bool horizontalScreen; RxString now = ''.obs; Timer? clock; late String defaultCDNService; bool get isFullScreen => widget.controller.isFullScreen.value; Box get setting => GStorage.setting; + late final _coinKey = GlobalKey(); + late final _favKey = GlobalKey(); @override void initState() { @@ -1746,346 +1747,483 @@ class _HeaderControlState extends State { }); } - @override - Widget build(BuildContext context) { - final plPlayerController = widget.controller; - // final bool isLandscape = - // MediaQuery.of(context).orientation == Orientation.landscape; - - bool equivalentFullScreen = !isFullScreen && - !horizontalScreen && - MediaQuery.of(context).orientation == Orientation.landscape; - return LayoutBuilder(builder: (context, boxConstraints) { - return AppBar( + Widget _buildHeader(bool showFSActionItem) => AppBar( backgroundColor: Colors.transparent, foregroundColor: Colors.white, primary: false, automaticallyImplyLeading: false, - title: Row( + toolbarHeight: showFSActionItem && isFullScreen ? 112 : null, + flexibleSpace: Column( + mainAxisSize: MainAxisSize.min, children: [ - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '返回', - icon: const Icon( - FontAwesomeIcons.arrowLeft, - size: 15, - color: Colors.white, - ), - onPressed: () { - if (isFullScreen) { - widget.controller.triggerFullScreen(status: false); - } else if (MediaQuery.of(context).orientation == - Orientation.landscape && - !horizontalScreen) { - verticalScreenForTwoSeconds(); - } else { - Get.back(); - } - }, - ), - ), - if (!isFullScreen || - MediaQuery.of(context).orientation != Orientation.portrait) - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '返回主页', - icon: const Icon( - FontAwesomeIcons.house, - size: 15, - color: Colors.white, + const SizedBox(height: 11), + Row( + children: [ + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '返回', + icon: const Icon( + FontAwesomeIcons.arrowLeft, + size: 15, + color: Colors.white, + ), + onPressed: () { + if (isFullScreen) { + widget.controller.triggerFullScreen(status: false); + } else if (MediaQuery.of(context).orientation == + Orientation.landscape && + !horizontalScreen) { + verticalScreenForTwoSeconds(); + } else { + Get.back(); + } + }, ), - onPressed: () { - widget.videoDetailCtr.plPlayerController.backToHome = true; - Get.until((route) => route.isFirst); - }, ), - ), - if ((videoIntroController.videoDetail.value.title != null) && - (isFullScreen || equivalentFullScreen)) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: boxConstraints.maxWidth / 2 - 60, - maxHeight: 25), - child: Obx( - () => Marquee( - text: videoIntroController.videoDetail.value.title!, - style: const TextStyle( - color: Colors.white, - fontSize: 16, + if (!isFullScreen || + MediaQuery.of(context).orientation != Orientation.portrait) + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '返回主页', + icon: const Icon( + FontAwesomeIcons.house, + size: 15, + color: Colors.white, + ), + onPressed: () { + widget.videoDetailCtr.plPlayerController.backToHome = + true; + Get.until((route) => route.isFirst); + }, + ), + ), + if ((videoIntroController.videoDetail.value.title != null) && + (isFullScreen || + (!isFullScreen && + !horizontalScreen && + MediaQuery.of(context).orientation == + Orientation.landscape))) + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10), + constraints: BoxConstraints(maxHeight: 25), + child: Obx( + () => Marquee( + text: + videoIntroController.videoDetail.value.title!, + style: const TextStyle( + color: Colors.white, + fontSize: 16, + ), + scrollAxis: Axis.horizontal, + crossAxisAlignment: CrossAxisAlignment.start, + blankSpace: 200, + velocity: 40, + startAfter: const Duration(seconds: 1), + showFadingOnlyWhenScrolling: true, + fadingEdgeStartFraction: 0, + fadingEdgeEndFraction: 0.1, + numberOfRounds: 1, + startPadding: 0, + accelerationDuration: const Duration(seconds: 1), + accelerationCurve: Curves.linear, + decelerationDuration: + const Duration(milliseconds: 500), + decelerationCurve: Curves.easeOut, + ), + ), ), - scrollAxis: Axis.horizontal, - crossAxisAlignment: CrossAxisAlignment.start, - blankSpace: 200, - velocity: 40, - startAfter: const Duration(seconds: 1), - showFadingOnlyWhenScrolling: true, - fadingEdgeStartFraction: 0, - fadingEdgeEndFraction: 0.1, - numberOfRounds: 1, - startPadding: 0, - accelerationDuration: const Duration(seconds: 1), - accelerationCurve: Curves.linear, - decelerationDuration: const Duration(milliseconds: 500), - decelerationCurve: Curves.easeOut, + if (videoIntroController.isShowOnlineTotal) + Obx( + () => Text( + '${videoIntroController.total.value}人正在看', + style: const TextStyle( + color: Colors.white, + fontSize: 11, + ), + ), + ), + ], + ), + ) + else + const Spacer(), + if (MediaQuery.of(context).orientation == + Orientation.landscape && + (isFullScreen || !horizontalScreen)) ...[ + // const Spacer(), + // show current datetime + Obx( + () => Text( + now.value, + style: const TextStyle( + color: Colors.white, + fontSize: 13, ), ), ), - if (videoIntroController.isShowOnlineTotal) - Obx( - () => Text( - '${videoIntroController.total.value}人正在看', - style: const TextStyle( - color: Colors.white, - fontSize: 11, - ), - ), - ), + const SizedBox(width: 15), ], - ), - const Spacer(), - if (MediaQuery.of(context).orientation == Orientation.landscape && - (isFullScreen || !horizontalScreen)) ...[ - // const Spacer(), - // show current datetime - Obx( - () => Text( - now.value, - style: const TextStyle( - color: Colors.white, - fontSize: 13, + // ComBtn( + // icon: const Icon( + // FontAwesomeIcons.cropSimple, + // size: 15, + // color: Colors.white, + // ), + // fuc: () => _.screenshot(), + // ), + if (widget.videoDetailCtr.enableSponsorBlock == true) + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '提交片段', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => widget.videoDetailCtr.onBlock(context), + icon: Stack( + alignment: Alignment.center, + children: [ + Icon( + Icons.shield_outlined, + size: 19, + color: Colors.white, + ), + Icon( + Icons.play_arrow_rounded, + size: 13, + color: Colors.white, + ), + ], + ), + ), + ), + Obx( + () => widget.videoDetailCtr.segmentList.isNotEmpty == true + ? SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '片段信息', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => + widget.videoDetailCtr.showSBDetail(context), + icon: Icon( + MdiIcons.advertisements, + size: 19, + color: Colors.white, + ), + ), + ) + : const SizedBox.shrink(), + ), + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '发弹幕', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: widget.videoDetailCtr.showShootDanmakuSheet, + icon: const Icon( + Icons.comment_outlined, + size: 19, + color: Colors.white, + ), ), ), - ), - const SizedBox(width: 15), - ], - // ComBtn( - // icon: const Icon( - // FontAwesomeIcons.cropSimple, - // size: 15, - // color: Colors.white, - // ), - // fuc: () => _.screenshot(), - // ), - if (widget.videoDetailCtr.enableSponsorBlock == true) - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '提交片段', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => widget.videoDetailCtr.onBlock(context), - icon: Stack( - alignment: Alignment.center, - children: [ - Icon( - Icons.shield_outlined, + SizedBox( + width: 42, + height: 34, + child: Obx( + () => IconButton( + tooltip: + "${plPlayerController.isOpenDanmu.value ? '关闭' : '开启'}弹幕", + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + plPlayerController.isOpenDanmu.value = + !plPlayerController.isOpenDanmu.value; + setting.put(SettingBoxKey.enableShowDanmaku, + plPlayerController.isOpenDanmu.value); + // SmartDialog.showToast( + // "已${plPlayerController.isOpenDanmu.value ? '开启' : '关闭'}弹幕", + // displayTime: const Duration(seconds: 1)); + }, + icon: Icon( + plPlayerController.isOpenDanmu.value + ? Icons.subtitles_outlined + : Icons.subtitles_off_outlined, size: 19, color: Colors.white, ), - Icon( - Icons.play_arrow_rounded, - size: 13, - color: Colors.white, + ), + ), + ), + if (Platform.isAndroid) + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '画中画', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), ), - ], - ), - ), - ), - Obx( - () => widget.videoDetailCtr.segmentList.isNotEmpty == true - ? SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '片段信息', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => - widget.videoDetailCtr.showSBDetail(context), - icon: Icon( - MdiIcons.advertisements, - size: 19, - color: Colors.white, - ), - ), - ) - : const SizedBox.shrink(), - ), - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '发弹幕', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: widget.videoDetailCtr.showShootDanmakuSheet, - icon: const Icon( - Icons.comment_outlined, - size: 19, - color: Colors.white, - ), - ), - ), - SizedBox( - width: 42, - height: 34, - child: Obx( - () => IconButton( - tooltip: - "${plPlayerController.isOpenDanmu.value ? '关闭' : '开启'}弹幕", - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - plPlayerController.isOpenDanmu.value = - !plPlayerController.isOpenDanmu.value; - setting.put(SettingBoxKey.enableShowDanmaku, - plPlayerController.isOpenDanmu.value); - // SmartDialog.showToast( - // "已${plPlayerController.isOpenDanmu.value ? '开启' : '关闭'}弹幕", - // displayTime: const Duration(seconds: 1)); - }, - icon: Icon( - plPlayerController.isOpenDanmu.value - ? Icons.subtitles_outlined - : Icons.subtitles_off_outlined, - size: 19, - color: Colors.white, - ), - ), - ), - ), - if (Platform.isAndroid) - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '画中画', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: () async { - bool canUsePiP = widget.floating != null && - await widget.floating!.isPipAvailable; - widget.controller.hiddenControls(false); - if (canUsePiP) { - bool enableBackgroundPlay = setting.get( - SettingBoxKey.enableBackgroundPlay, - defaultValue: true); - if (!enableBackgroundPlay && context.mounted) { - // SmartDialog.showToast('建议开启【后台播放】功能\n避免画中画没有暂停按钮'); - // await Future.delayed(const Duration(seconds: 2), () { - // }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Column( - children: [ - const Row( + onPressed: () async { + bool canUsePiP = widget.floating != null && + await widget.floating!.isPipAvailable; + widget.controller.hiddenControls(false); + if (canUsePiP) { + bool enableBackgroundPlay = setting.get( + SettingBoxKey.enableBackgroundPlay, + defaultValue: true); + if (!enableBackgroundPlay && context.mounted) { + // SmartDialog.showToast('建议开启【后台播放】功能\n避免画中画没有暂停按钮'); + // await Future.delayed(const Duration(seconds: 2), () { + // }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Column( children: [ - Icon( - Icons.check, - color: Colors.green, + const Row( + children: [ + Icon( + Icons.check, + color: Colors.green, + ), + SizedBox(width: 10), + Text('画中画', + style: TextStyle( + fontSize: 15, height: 1.5)) + ], ), - SizedBox(width: 10), - Text('画中画', + const SizedBox(height: 10), + const Text( + '建议开启【后台音频服务】\n' + '避免画中画没有暂停按钮', style: TextStyle( - fontSize: 15, height: 1.5)) + fontSize: 12.5, height: 1.5)), + Row(children: [ + TextButton( + style: ButtonStyle( + foregroundColor: + WidgetStateProperty.resolveWith( + (states) { + return Theme.of(context) + .snackBarTheme + .actionTextColor; + }), + ), + onPressed: () async { + plPlayerController + .setBackgroundPlay(true); + SmartDialog.showToast("请重新载入本页面刷新"); + // Get.back(); + }, + child: const Text('启用后台音频服务')), + const SizedBox(width: 10), + TextButton( + style: ButtonStyle( + foregroundColor: + WidgetStateProperty.resolveWith( + (states) { + return Theme.of(context) + .snackBarTheme + .actionTextColor; + }), + ), + onPressed: () {}, + child: const Text('不启用')) + ]) ], ), - const SizedBox(height: 10), - const Text( - '建议开启【后台音频服务】\n' - '避免画中画没有暂停按钮', - style: - TextStyle(fontSize: 12.5, height: 1.5)), - Row(children: [ - TextButton( - style: ButtonStyle( - foregroundColor: - WidgetStateProperty.resolveWith( - (states) { - return Theme.of(context) - .snackBarTheme - .actionTextColor; - }), - ), - onPressed: () async { - plPlayerController - .setBackgroundPlay(true); - SmartDialog.showToast("请重新载入本页面刷新"); - // Get.back(); - }, - child: const Text('启用后台音频服务')), - const SizedBox(width: 10), - TextButton( - style: ButtonStyle( - foregroundColor: - WidgetStateProperty.resolveWith( - (states) { - return Theme.of(context) - .snackBarTheme - .actionTextColor; - }), - ), - onPressed: () {}, - child: const Text('不启用')) - ]) - ], - ), - duration: const Duration(seconds: 2), - showCloseIcon: true, - ), - ); - await Future.delayed(const Duration(seconds: 3), () {}); - } - final Rational aspectRatio = Rational( - widget.videoDetailCtr.data.dash!.video!.first.width!, - widget.videoDetailCtr.data.dash!.video!.first.height!, - ); - if (!context.mounted) return; - await widget.floating!.enable(EnableManual( - aspectRatio: aspectRatio, - )); - } else {} - }, - icon: const Icon( - Icons.picture_in_picture_outlined, - size: 19, - color: Colors.white, + duration: const Duration(seconds: 2), + showCloseIcon: true, + ), + ); + await Future.delayed( + const Duration(seconds: 3), () {}); + } + final Rational aspectRatio = Rational( + widget + .videoDetailCtr.data.dash!.video!.first.width!, + widget + .videoDetailCtr.data.dash!.video!.first.height!, + ); + if (!context.mounted) return; + await widget.floating!.enable(EnableManual( + aspectRatio: aspectRatio, + )); + } else {} + }, + icon: const Icon( + Icons.picture_in_picture_outlined, + size: 19, + color: Colors.white, + ), + ), + ), + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: "更多设置", + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: showSettingSheet, + icon: const Icon( + Icons.more_vert_outlined, + size: 19, + color: Colors.white, + ), ), ), - ), - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: "更多设置", - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: showSettingSheet, - icon: const Icon( - Icons.more_vert_outlined, - size: 19, - color: Colors.white, - ), - ), + ], ), + if (showFSActionItem) + isFullScreen + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + width: 42, + height: 34, + child: Obx( + () => ActionItem( + expand: false, + icon: const Icon( + FontAwesomeIcons.thumbsUp, + color: Colors.white, + ), + selectIcon: + const Icon(FontAwesomeIcons.solidThumbsUp), + onTap: videoIntroController.actionLikeVideo, + onLongPress: videoIntroController.actionOneThree, + selectStatus: videoIntroController.hasLike.value, + semanticsLabel: '点赞', + needAnim: true, + hasOneThree: videoIntroController.hasLike.value && + videoIntroController.hasCoin.value && + videoIntroController.hasFav.value, + callBack: (start) { + if (start) { + _coinKey.currentState?.controller?.forward(); + _favKey.currentState?.controller?.forward(); + } else { + _coinKey.currentState?.controller?.reverse(); + _favKey.currentState?.controller?.reverse(); + } + }, + ), + ), + ), + SizedBox( + width: 42, + height: 34, + child: Obx( + () => ActionItem( + expand: false, + icon: const Icon( + FontAwesomeIcons.thumbsDown, + color: Colors.white, + ), + selectIcon: + const Icon(FontAwesomeIcons.solidThumbsDown), + onTap: videoIntroController.actionDislikeVideo, + selectStatus: + videoIntroController.hasDislike.value, + semanticsLabel: '点踩', + ), + ), + ), + SizedBox( + width: 42, + height: 34, + child: Obx( + () => ActionItem( + key: _coinKey, + expand: false, + icon: const Icon( + FontAwesomeIcons.b, + color: Colors.white, + ), + selectIcon: const Icon(FontAwesomeIcons.b), + onTap: videoIntroController.actionCoinVideo, + selectStatus: videoIntroController.hasCoin.value, + semanticsLabel: '投币', + needAnim: true, + ), + ), + ), + SizedBox( + width: 42, + height: 34, + child: Obx( + () => ActionItem( + key: _favKey, + expand: false, + icon: const Icon( + FontAwesomeIcons.star, + color: Colors.white, + ), + selectIcon: + const Icon(FontAwesomeIcons.solidStar), + onTap: () => videoIntroController + .showFavBottomSheet(context), + onLongPress: () => videoIntroController + .showFavBottomSheet(context, + type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + semanticsLabel: '收藏', + needAnim: true, + ), + ), + ), + SizedBox( + width: 42, + height: 34, + child: ActionItem( + expand: false, + icon: const Icon( + FontAwesomeIcons.shareFromSquare, + color: Colors.white, + ), + onTap: videoIntroController.actionShareVideo, + selectStatus: false, + semanticsLabel: '分享', + ), + ), + ], + ) + : const SizedBox.shrink(), ], ), ); - }); + + PlPlayerController get plPlayerController => widget.controller; + + @override + Widget build(BuildContext context) { + // final bool isLandscape = + // MediaQuery.of(context).orientation == Orientation.landscape; + + return plPlayerController.showFSActionItem + ? Obx(() => _buildHeader(true)) + : _buildHeader(false); } } diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index ea518179..d0872e97 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -254,6 +254,8 @@ class PlPlayerController { /// 弹幕开关 Rx isOpenDanmu = false.obs; + late final showFSActionItem = GStorage.showFSActionItem; + /// 弹幕权重 int danmakuWeight = 0; int filterCount = 0; diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart index 6e5a3f66..10c0fd7e 100644 --- a/lib/plugin/pl_player/widgets/app_bar_ani.dart +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -28,7 +28,7 @@ class AppBarAni extends StatelessWidget implements PreferredSizeWidget { parent: controller, curve: Curves.linear, )), - child: Container( + child: DecoratedBox( decoration: BoxDecoration( gradient: position! == 'top' ? const LinearGradient( diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 25643f7f..34461524 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -394,6 +394,9 @@ class GStorage { static bool slideDismissReplyPage = GStorage.setting .get(SettingBoxKey.slideDismissReplyPage, defaultValue: Platform.isIOS); + static bool get showFSActionItem => + GStorage.setting.get(SettingBoxKey.showFSActionItem, defaultValue: true); + static List get dynamicDetailRatio => List.from(setting .get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0])); @@ -649,6 +652,7 @@ class SettingBoxKey { collapsibleVideoPage = 'collapsibleVideoPage', enableHttp2 = 'enableHttp2', slideDismissReplyPage = 'slideDismissReplyPage', + showFSActionItem = 'showFSActionItem', // Sponsor Block enableSponsorBlock = 'enableSponsorBlock',