From e4a960ecf968db038b3777bb3ea10c3fa77d556e Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Sun, 12 Oct 2025 16:13:23 +0800 Subject: [PATCH] opt ui Signed-off-by: bggRGjQaUbCoE --- lib/pages/audio/controller.dart | 16 +- lib/pages/video/view.dart | 593 +++++++++--------- lib/plugin/pl_player/view.dart | 25 +- lib/plugin/pl_player/widgets/app_bar_ani.dart | 1 - .../pl_player/widgets/bottom_control.dart | 10 +- 5 files changed, 332 insertions(+), 313 deletions(-) diff --git a/lib/pages/audio/controller.dart b/lib/pages/audio/controller.dart index 0286c3cd..c1893f17 100644 --- a/lib/pages/audio/controller.dart +++ b/lib/pages/audio/controller.dart @@ -110,6 +110,13 @@ class AudioController extends GetxController }); } + void _updateCurrItem(DetailItem item) { + audioItem.value = item; + hasLike.value = item.stat.hasLike_7; + coinNum.value = item.stat.hasCoin_8 ? 2 : 0; + hasFav.value = item.stat.hasFav; + } + Future _queryPlayList({ bool isInit = false, bool isLoadPrev = false, @@ -136,11 +143,7 @@ class AudioController extends GetxController final index = data.list.indexWhere((e) => e.item.oid == oid); if (index != -1) { this.index = index; - final item = data.list[index]; - audioItem.value = item; - hasLike.value = item.stat.hasLike_7; - coinNum.value = item.stat.hasCoin_8 ? 2 : 0; - hasFav.value = item.stat.hasFav; + _updateCurrItem(data.list[index]); playlist = data.list; } } else if (isLoadPrev) { @@ -356,6 +359,7 @@ class AudioController extends GetxController hasLike.value = true; } coinNum.value += coin; + GlobalData().afterCoin(coin); } else { res.toast(); } @@ -540,7 +544,7 @@ class AudioController extends GetxController itemType = item.itemType; _queryPlayUrl().then((res) { if (res) { - this.audioItem.value = audioItem; + _updateCurrItem(audioItem); } }); } diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index 42f5431e..d97983fe 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -456,6 +456,8 @@ class _VideoDetailPageVState extends State ModalRoute.of(context)! as PageRoute, ); + padding = MediaQuery.viewPaddingOf(context); + final size = MediaQuery.sizeOf(context); maxWidth = size.width; maxHeight = size.height; @@ -548,325 +550,330 @@ class _VideoDetailPageVState extends State return Obx( () { final isFullScreen = this.isFullScreen; - return Scaffold( - resizeToAvoidBottomInset: false, - appBar: isFullScreen - ? null - : PreferredSize( - preferredSize: const Size.fromHeight(0), - child: Obx( - () { - final scrollRatio = - videoDetailController.scrollRatio.value; - bool shouldShow = - scrollRatio != 0 && - videoDetailController.scrollCtr.offset != 0 && - isPortrait; - return Stack( - clipBehavior: Clip.none, - children: [ - AppBar( - backgroundColor: Colors.black, - toolbarHeight: 0, - systemOverlayStyle: Platform.isAndroid - ? shouldShow - ? null - : SystemUiOverlayStyle( - statusBarIconBrightness: - Brightness.light, - systemNavigationBarIconBrightness: - themeData.brightness.reverse, - ) - : null, - ), - if (shouldShow) + return Padding( + padding: EdgeInsets.only( + top: isFullScreen && isPortrait ? padding.top : 0, + ), + child: Scaffold( + resizeToAvoidBottomInset: false, + appBar: isFullScreen + ? null + : PreferredSize( + preferredSize: const Size.fromHeight(0), + child: Obx( + () { + final scrollRatio = + videoDetailController.scrollRatio.value; + bool shouldShow = + scrollRatio != 0 && + videoDetailController.scrollCtr.offset != 0 && + isPortrait; + return Stack( + clipBehavior: Clip.none, + children: [ AppBar( - backgroundColor: themeData.colorScheme.surface - .withValues(alpha: scrollRatio), + backgroundColor: Colors.black, toolbarHeight: 0, systemOverlayStyle: Platform.isAndroid - ? SystemUiOverlayStyle( - statusBarIconBrightness: - themeData.brightness.reverse, - systemNavigationBarIconBrightness: - themeData.brightness.reverse, - ) + ? shouldShow + ? null + : SystemUiOverlayStyle( + statusBarIconBrightness: + Brightness.light, + systemNavigationBarIconBrightness: + themeData.brightness.reverse, + ) : null, ), - ], - ); - }, + if (shouldShow) + AppBar( + backgroundColor: themeData.colorScheme.surface + .withValues(alpha: scrollRatio), + toolbarHeight: 0, + systemOverlayStyle: Platform.isAndroid + ? SystemUiOverlayStyle( + statusBarIconBrightness: + themeData.brightness.reverse, + systemNavigationBarIconBrightness: + themeData.brightness.reverse, + ) + : null, + ), + ], + ); + }, + ), ), - ), - body: ExtendedNestedScrollView( - key: videoDetailController.scrollKey, - controller: videoDetailController.scrollCtr, - onlyOneScrollInBody: true, - pinnedHeaderSliverHeightBuilder: () { - double pinnedHeight = this.isFullScreen || !isPortrait - ? maxHeight - : videoDetailController.isExpanding || - videoDetailController.isCollapsing - ? animHeight - : videoDetailController.isCollapsing || - plPlayerController?.playerStatus.status.value == - PlayerStatus.playing - ? videoDetailController.minVideoHeight - : kToolbarHeight; - if (videoDetailController.isExpanding && - videoDetailController.animationController.value == 1) { - videoDetailController.isExpanding = false; - WidgetsBinding.instance.addPostFrameCallback((_) { - videoDetailController.scrollRatio.value = 0; - refreshPage(); - }); - } else if (videoDetailController.isCollapsing && - videoDetailController.animationController.value == 1) { - videoDetailController.isCollapsing = false; - WidgetsBinding.instance.addPostFrameCallback((_) { - refreshPage(); - }); - } - return pinnedHeight; - }, - headerSliverBuilder: (context, innerBoxIsScrolled) { - final isFullScreen = this.isFullScreen; - final height = !isPortrait || isFullScreen - ? maxHeight - : videoDetailController.isExpanding || - videoDetailController.isCollapsing - ? animHeight - : videoDetailController.videoHeight; - return [ - SliverAppBar( - elevation: 0, - scrolledUnderElevation: 0, - primary: false, - automaticallyImplyLeading: false, - pinned: true, - expandedHeight: isFullScreen || !isPortrait - ? maxHeight - : videoDetailController.isExpanding || - videoDetailController.isCollapsing - ? animHeight - : videoDetailController.videoHeight, - flexibleSpace: Stack( - clipBehavior: Clip.none, - children: [ - SizedBox( - width: maxWidth, - height: height, - child: videoPlayer( + body: ExtendedNestedScrollView( + key: videoDetailController.scrollKey, + controller: videoDetailController.scrollCtr, + onlyOneScrollInBody: true, + pinnedHeaderSliverHeightBuilder: () { + double pinnedHeight = this.isFullScreen || !isPortrait + ? maxHeight - (isPortrait ? padding.top : 0) + : videoDetailController.isExpanding || + videoDetailController.isCollapsing + ? animHeight + : videoDetailController.isCollapsing || + plPlayerController?.playerStatus.status.value == + PlayerStatus.playing + ? videoDetailController.minVideoHeight + : kToolbarHeight; + if (videoDetailController.isExpanding && + videoDetailController.animationController.value == 1) { + videoDetailController.isExpanding = false; + WidgetsBinding.instance.addPostFrameCallback((_) { + videoDetailController.scrollRatio.value = 0; + refreshPage(); + }); + } else if (videoDetailController.isCollapsing && + videoDetailController.animationController.value == 1) { + videoDetailController.isCollapsing = false; + WidgetsBinding.instance.addPostFrameCallback((_) { + refreshPage(); + }); + } + return pinnedHeight; + }, + headerSliverBuilder: (context, innerBoxIsScrolled) { + final isFullScreen = this.isFullScreen; + final height = isFullScreen || !isPortrait + ? maxHeight - (isPortrait ? padding.top : 0) + : videoDetailController.isExpanding || + videoDetailController.isCollapsing + ? animHeight + : videoDetailController.videoHeight; + return [ + SliverAppBar( + elevation: 0, + scrolledUnderElevation: 0, + primary: false, + automaticallyImplyLeading: false, + pinned: true, + expandedHeight: height, + flexibleSpace: Stack( + clipBehavior: Clip.none, + children: [ + SizedBox( width: maxWidth, height: height, + child: videoPlayer( + width: maxWidth, + height: height, + ), ), - ), - Obx( - () { - Widget toolbar() => Opacity( - opacity: videoDetailController.scrollRatio.value, - child: Container( - color: themeData.colorScheme.surface, - alignment: Alignment.topCenter, - child: SizedBox( - height: kToolbarHeight, - child: Stack( - clipBehavior: Clip.none, - children: [ - Align( - alignment: Alignment.centerLeft, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '返回', - icon: Icon( - FontAwesomeIcons.arrowLeft, - size: 15, - color: themeData - .colorScheme - .onSurface, - ), - onPressed: Get.back, - ), - ), - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '返回主页', - icon: Icon( - FontAwesomeIcons.house, - size: 15, - color: themeData - .colorScheme - .onSurface, - ), - onPressed: () { - videoDetailController - .plPlayerController - ..isCloseAll = true - ..dispose(); - Get.until( - (route) => route.isFirst, - ); - }, - ), - ), - ], - ), - ), - Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.play_arrow_rounded, - color: - themeData.colorScheme.primary, - ), - Text( - '${videoDetailController.playedTime == null - ? '立即' - : plPlayerController!.playerStatus.status.value == PlayerStatus.completed - ? '重新' - : '继续'}播放', - style: TextStyle( - color: - themeData.colorScheme.primary, - ), - ), - ], - ), - ), - Align( - alignment: Alignment.centerRight, - child: - videoDetailController.playedTime == - null - ? _moreBtn( - themeData.colorScheme.onSurface, - ) - : SizedBox( + Obx( + () { + Widget toolbar() => Opacity( + opacity: videoDetailController.scrollRatio.value, + child: Container( + color: themeData.colorScheme.surface, + alignment: Alignment.topCenter, + child: SizedBox( + height: kToolbarHeight, + child: Stack( + clipBehavior: Clip.none, + children: [ + Align( + alignment: Alignment.centerLeft, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( width: 42, height: 34, child: IconButton( - tooltip: "更多设置", - style: const ButtonStyle( - padding: - WidgetStatePropertyAll( - EdgeInsets.zero, - ), - ), - onPressed: () => - videoDetailController - .headerCtrKey - .currentState - ?.showSettingSheet(), + tooltip: '返回', icon: Icon( - Icons.more_vert_outlined, - size: 19, + FontAwesomeIcons.arrowLeft, + size: 15, color: themeData .colorScheme .onSurface, ), + onPressed: Get.back, ), ), - ), - ], + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '返回主页', + icon: Icon( + FontAwesomeIcons.house, + size: 15, + color: themeData + .colorScheme + .onSurface, + ), + onPressed: () { + videoDetailController + .plPlayerController + ..isCloseAll = true + ..dispose(); + Get.until( + (route) => route.isFirst, + ); + }, + ), + ), + ], + ), + ), + Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.play_arrow_rounded, + color: + themeData.colorScheme.primary, + ), + Text( + '${videoDetailController.playedTime == null + ? '立即' + : plPlayerController!.playerStatus.status.value == PlayerStatus.completed + ? '重新' + : '继续'}播放', + style: TextStyle( + color: themeData + .colorScheme + .primary, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.centerRight, + child: + videoDetailController.playedTime == + null + ? _moreBtn( + themeData.colorScheme.onSurface, + ) + : SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: "更多设置", + style: const ButtonStyle( + padding: + WidgetStatePropertyAll( + EdgeInsets.zero, + ), + ), + onPressed: () => + videoDetailController + .headerCtrKey + .currentState + ?.showSettingSheet(), + icon: Icon( + Icons.more_vert_outlined, + size: 19, + color: themeData + .colorScheme + .onSurface, + ), + ), + ), + ), + ], + ), ), ), - ), - ); - return videoDetailController.scrollRatio.value == 0 || - videoDetailController.scrollCtr.offset == 0 || - !isPortrait - ? const SizedBox.shrink() - : Positioned.fill( - bottom: -2, - child: GestureDetector( - onTap: () async { - if (videoDetailController.isQuerying) { - if (kDebugMode) { - debugPrint( - 'handlePlay: querying', - ); + ); + return videoDetailController.scrollRatio.value == + 0 || + videoDetailController.scrollCtr.offset == + 0 || + !isPortrait + ? const SizedBox.shrink() + : Positioned.fill( + bottom: -2, + child: GestureDetector( + onTap: () async { + if (videoDetailController.isQuerying) { + if (kDebugMode) { + debugPrint( + 'handlePlay: querying', + ); + } + return; } - return; - } - if (videoDetailController.videoUrl == - null || - videoDetailController.audioUrl == - null) { - if (kDebugMode) { - debugPrint( - 'handlePlay: videoUrl/audioUrl not initialized', - ); + if (videoDetailController.videoUrl == + null || + videoDetailController.audioUrl == + null) { + if (kDebugMode) { + debugPrint( + 'handlePlay: videoUrl/audioUrl not initialized', + ); + } + videoDetailController.queryVideoUrl(); + return; } - videoDetailController.queryVideoUrl(); - return; - } - videoDetailController.scrollRatio.value = - 0; - if (plPlayerController == null || - videoDetailController.playedTime == - null) { - handlePlay(); - } else { - if (plPlayerController! - .videoPlayerController! - .state - .completed) { - await plPlayerController! - .videoPlayerController! - .seek(Duration.zero); - plPlayerController! - .videoPlayerController! - .play(); + videoDetailController + .scrollRatio + .value = + 0; + if (plPlayerController == null || + videoDetailController.playedTime == + null) { + handlePlay(); } else { - plPlayerController! + if (plPlayerController! .videoPlayerController! - .playOrPause(); + .state + .completed) { + await plPlayerController! + .videoPlayerController! + .seek(Duration.zero); + plPlayerController! + .videoPlayerController! + .play(); + } else { + plPlayerController! + .videoPlayerController! + .playOrPause(); + } } - } - }, - behavior: HitTestBehavior.opaque, - child: toolbar(), - ), - ); - }, - ), - ], - ), - ), - ]; - }, - body: Scaffold( - key: videoDetailController.childKey, - resizeToAvoidBottomInset: false, - backgroundColor: Colors.transparent, - body: Column( - children: [ - buildTabbar(onTap: videoDetailController.animToTop), - Expanded( - child: videoTabBarView( - controller: videoDetailController.tabCtr, - children: [ - videoIntro( - isHorizontal: false, - needCtr: false, - isNested: true, + }, + behavior: HitTestBehavior.opaque, + child: toolbar(), + ), + ); + }, ), - if (videoDetailController.showReply) - videoReplyPanel(isNested: true), - if (_shouldShowSeasonPanel) seasonPanel, ], ), ), - ], + ]; + }, + body: Scaffold( + key: videoDetailController.childKey, + resizeToAvoidBottomInset: false, + backgroundColor: Colors.transparent, + body: Column( + children: [ + buildTabbar(onTap: videoDetailController.animToTop), + Expanded( + child: videoTabBarView( + controller: videoDetailController.tabCtr, + children: [ + videoIntro( + isHorizontal: false, + needCtr: false, + isNested: true, + ), + if (videoDetailController.showReply) + videoReplyPanel(isNested: true), + if (_shouldShowSeasonPanel) seasonPanel, + ], + ), + ), + ], + ), ), ), ), @@ -878,7 +885,6 @@ class _VideoDetailPageVState extends State Widget get childWhenDisabledLandscape => Obx( () { final isFullScreen = this.isFullScreen; - final padding = MediaQuery.viewPaddingOf(context); return Scaffold( resizeToAvoidBottomInset: false, appBar: isFullScreen @@ -1052,7 +1058,6 @@ class _VideoDetailPageVState extends State Widget get childWhenDisabledAlmostSquare => Obx(() { final isFullScreen = this.isFullScreen; - final padding = MediaQuery.viewPaddingOf(context); return Scaffold( resizeToAvoidBottomInset: false, appBar: isFullScreen @@ -1368,6 +1373,7 @@ class _VideoDetailPageVState extends State late bool isPortrait; late double maxWidth; late double maxHeight; + late EdgeInsets padding; @override Widget build(BuildContext context) { @@ -1730,7 +1736,6 @@ class _VideoDetailPageVState extends State bool needCtr = true, bool isNested = false, }) { - final bottom = MediaQuery.viewPaddingOf(context).bottom; Widget introPanel() => KeepAliveWrapper( builder: (context) { final child = CustomScrollView( @@ -1786,7 +1791,7 @@ class _VideoDetailPageVState extends State (videoDetailController.isPlayAll && !isPortrait ? 80 : StyleString.safeSpace) + - bottom, + padding.bottom, ), ), ], @@ -1808,7 +1813,7 @@ class _VideoDetailPageVState extends State Positioned( left: 12, right: 12, - bottom: 12 + bottom, + bottom: 12 + padding.bottom, child: Material( type: MaterialType.transparency, child: InkWell( diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 248c4a52..2d948348 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -40,6 +40,7 @@ import 'package:PiliPlus/plugin/pl_player/widgets/forward_seek.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/mpv_convert_webp.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; +import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -793,10 +794,16 @@ class _PLVideoPlayerState extends State late final transformationController = TransformationController(); - late ThemeData theme; + late ColorScheme colorScheme; late double maxWidth; late double maxHeight; + @override + void didChangeDependencies() { + super.didChangeDependencies(); + colorScheme = ColorScheme.of(context); + } + void _onInteractionStart(ScaleStartDetails details) { if (plPlayerController.controlsLock.value) return; // 如果起点太靠上则屏蔽 @@ -911,12 +918,12 @@ class _PLVideoPlayerState extends State borderRadius: const BorderRadius.all( Radius.circular(6), ), - color: theme.colorScheme.secondaryContainer, + color: colorScheme.secondaryContainer, ), child: Text( '松开手指,取消进退', style: TextStyle( - color: theme.colorScheme.onSecondaryContainer, + color: colorScheme.onSecondaryContainer, ), ), ), @@ -1172,10 +1179,11 @@ class _PLVideoPlayerState extends State @override Widget build(BuildContext context) { - theme = Theme.of(context); maxWidth = widget.maxWidth; maxHeight = widget.maxHeight; - final Color primary = theme.colorScheme.primary; + final primary = colorScheme.isLight + ? colorScheme.inversePrimary + : colorScheme.primary; late final thumbGlowColor = primary.withAlpha(80); late final bufferedBarColor = primary.withValues(alpha: 0.4); const TextStyle textStyle = TextStyle( @@ -1508,7 +1516,7 @@ class _PLVideoPlayerState extends State child: FilledButton.tonal( style: FilledButton.styleFrom( tapTargetSize: MaterialTapTargetSize.shrinkWrap, - backgroundColor: theme.colorScheme.secondaryContainer + backgroundColor: colorScheme.secondaryContainer .withValues(alpha: 0.8), visualDensity: VisualDensity.compact, padding: const EdgeInsets.all(15), @@ -1660,7 +1668,7 @@ class _PLVideoPlayerState extends State videoDetailController.showDmTreandChart.value) if (videoDetailController.dmTrend.value?.dataOrNull case final list?) - buildDmChart(theme, list, videoDetailController), + buildDmChart(primary, list, videoDetailController), ], ); }, @@ -2040,12 +2048,11 @@ class _PLVideoPlayerState extends State } Widget buildDmChart( - ThemeData theme, + Color color, List dmTrend, VideoDetailController videoDetailController, [ double offset = 0, ]) { - final color = theme.colorScheme.primary; return IgnorePointer( child: Container( height: 12, diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart index b393e454..747a6bf6 100644 --- a/lib/plugin/pl_player/widgets/app_bar_ani.dart +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -51,7 +51,6 @@ class AppBarAni extends StatelessWidget { ), ), child: ViewSafeArea( - top: isTop && isFullScreen, left: isFullScreen, right: isFullScreen, child: child, diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index 0aa1d190..1f7518ce 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/progress_bar/segment_progress_bar.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/view.dart'; +import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -29,8 +30,10 @@ class BottomControl extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = Theme.of(context); - Color primary = theme.colorScheme.primary; + final colorScheme = ColorScheme.of(context); + final primary = colorScheme.isLight + ? colorScheme.inversePrimary + : colorScheme.primary; final thumbGlowColor = primary.withAlpha(80); final bufferedBarColor = primary.withValues(alpha: 0.4); //阅读器限制 @@ -113,6 +116,7 @@ class BottomControl extends StatelessWidget { thumbGlowColor: thumbGlowColor, barHeight: 3.5, thumbRadius: 7, + thumbGlowRadius: 25, onDragStart: onDragStart, onDragUpdate: (e) => onDragUpdate(e, max), onSeek: (e) => onSeek(e, max), @@ -175,7 +179,7 @@ class BottomControl extends StatelessWidget { if (videoDetailController.showDmTreandChart.value) if (videoDetailController.dmTrend.value?.dataOrNull case final list?) - buildDmChart(theme, list, videoDetailController, 4.5), + buildDmChart(primary, list, videoDetailController, 4.5), ], ), ),