From 1666bc220e4ceead0c82ca83c5cd9a5e3e4c108c Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Tue, 20 Oct 2026 11:19:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(flutter):=20=E5=BA=95=E6=A0=8F=E5=8F=B3?= =?UTF-8?q?=E4=BE=A7=E6=94=B9=E4=B8=BA=E6=B8=85=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/player/player_view.dart | 3 --- lib/pages/player/player_view_actions.dart | 4 ++++ lib/pages/player/widgets/player_more_sheet.dart | 15 +++++++++++++++ lib/pages/player/widgets/player_overlay.dart | 15 +++++++-------- .../player/widgets/player_overlay_models.dart | 4 ---- .../player/widgets/player_overlay_parts.dart | 12 +++++++++--- test/player/player_fullscreen_test.dart | 16 ++++++++++++++++ 7 files changed, 51 insertions(+), 18 deletions(-) diff --git a/lib/pages/player/player_view.dart b/lib/pages/player/player_view.dart index 3d81078..f0793d0 100644 --- a/lib/pages/player/player_view.dart +++ b/lib/pages/player/player_view.dart @@ -431,9 +431,6 @@ class _PlayerPageState extends State with WidgetsBindingObserver { onSave: () => unawaited(_toggleSave(context, drama)), onShare: () => logic.shareDrama(context, drama), onMore: () => _openMore(context, drama, episode), - onPip: logic.pictureInPicture.isPlatformSupported - ? () => unawaited(logic.enterPip(context)) - : null, onScrubStart: ready ? () => logic.beginScrub(videoPort!) : null, onSeek: ready ? (position) => logic.seekTo(context, videoPort!, position) diff --git a/lib/pages/player/player_view_actions.dart b/lib/pages/player/player_view_actions.dart index 0733240..ac3c172 100644 --- a/lib/pages/player/player_view_actions.dart +++ b/lib/pages/player/player_view_actions.dart @@ -32,6 +32,7 @@ extension _PlayerViewActions on _PlayerPageState { speed: _playbackSpeed, qualities: logic.qualities, textScale: _textScale, + pipAvailable: logic.pictureInPicture.isPlatformSupported, ), actions: PlayerMoreActions( onSpeedChanged: (value) { @@ -42,6 +43,9 @@ extension _PlayerViewActions on _PlayerPageState { onClear: _hideChrome, onDownload: () => requestEpisodeDownload(context, episode.id), onReport: () => unawaited(_reportDrama(context, drama, l10n)), + onPip: logic.pictureInPicture.isPlatformSupported + ? () => unawaited(logic.enterPip(context)) + : null, ), ), ); diff --git a/lib/pages/player/widgets/player_more_sheet.dart b/lib/pages/player/widgets/player_more_sheet.dart index 525bc01..6a885e6 100644 --- a/lib/pages/player/widgets/player_more_sheet.dart +++ b/lib/pages/player/widgets/player_more_sheet.dart @@ -18,6 +18,7 @@ class PlayerMoreState { required this.speed, required this.qualities, required this.textScale, + required this.pipAvailable, }); /// 当前倍速。 @@ -28,6 +29,9 @@ class PlayerMoreState { /// 当前文字缩放比例。 final double textScale; + + /// 设备是否提供画中画入口。 + final bool pipAvailable; } /// 更多面板动作集合。 @@ -39,6 +43,7 @@ class PlayerMoreActions { required this.onClear, required this.onDownload, required this.onReport, + this.onPip, }); /// 倍速变化。 @@ -55,6 +60,9 @@ class PlayerMoreActions { /// 投诉当前剧。 final VoidCallback onReport; + + /// 进入画中画;平台不支持时为空。 + final VoidCallback? onPip; } /// K29 播放页白色更多面板。 @@ -136,6 +144,13 @@ class _PlayerMoreSheetState extends State { title: l10n.offlineCache, onTap: () => _closeThen(widget.actions.onDownload), ), + if (widget.state.pipAvailable && widget.actions.onPip != null) + _enabledRow( + key: const Key('player-more-pip'), + icon: Icons.picture_in_picture_alt_rounded, + title: l10n.pictureInPicture, + onTap: () => _closeThen(widget.actions.onPip!), + ), _textSizeRow(l10n), _disabledRow( key: const Key('player-more-cast'), diff --git a/lib/pages/player/widgets/player_overlay.dart b/lib/pages/player/widgets/player_overlay.dart index 9b9be7e..ce65340 100644 --- a/lib/pages/player/widgets/player_overlay.dart +++ b/lib/pages/player/widgets/player_overlay.dart @@ -281,14 +281,13 @@ class PlayerPlaybackOverlay extends StatelessWidget { ), ), ), - if (actions.onPip != null) ...[ - const SizedBox(width: 8), - _PipButton( - key: const Key('player-pip'), - tooltip: data.l10n.pictureInPicture, - onPressed: actions.onPip!, - ), - ], + const SizedBox(width: 8), + _BottomIconButton( + key: const Key('player-bottom-clear-screen'), + tooltip: data.l10n.clearScreen, + icon: Icons.fullscreen_rounded, + onPressed: actions.onClear, + ), ], ); } diff --git a/lib/pages/player/widgets/player_overlay_models.dart b/lib/pages/player/widgets/player_overlay_models.dart index 873753c..7c43d8c 100644 --- a/lib/pages/player/widgets/player_overlay_models.dart +++ b/lib/pages/player/widgets/player_overlay_models.dart @@ -87,7 +87,6 @@ class PlayerOverlayActions { required this.onMore, required this.onScrubStart, required this.onSeek, - this.onPip, }); /// 返回上一页。 @@ -131,7 +130,4 @@ class PlayerOverlayActions { /// 提交进度跳转。 final Future Function(Duration)? onSeek; - - /// 进入小窗播放。 - final VoidCallback? onPip; } diff --git a/lib/pages/player/widgets/player_overlay_parts.dart b/lib/pages/player/widgets/player_overlay_parts.dart index bcc217e..8fec70a 100644 --- a/lib/pages/player/widgets/player_overlay_parts.dart +++ b/lib/pages/player/widgets/player_overlay_parts.dart @@ -73,10 +73,16 @@ class _SpeedEntry extends StatelessWidget { } } -class _PipButton extends StatelessWidget { - const _PipButton({super.key, required this.tooltip, required this.onPressed}); +class _BottomIconButton extends StatelessWidget { + const _BottomIconButton({ + super.key, + required this.tooltip, + required this.icon, + required this.onPressed, + }); final String tooltip; + final IconData icon; final VoidCallback onPressed; @override @@ -91,7 +97,7 @@ class _PipButton extends StatelessWidget { foregroundColor: AppColors.onSurface, ), onPressed: onPressed, - icon: const Icon(Icons.picture_in_picture_alt_rounded, size: 17), + icon: Icon(icon, size: 17), ), ); } diff --git a/test/player/player_fullscreen_test.dart b/test/player/player_fullscreen_test.dart index 8fe2c00..45e48d7 100644 --- a/test/player/player_fullscreen_test.dart +++ b/test/player/player_fullscreen_test.dart @@ -434,6 +434,20 @@ void main() { expect(find.byKey(const Key('player-exit-clear-screen')), findsOneWidget); }); + testWidgets('should_enter_clear_screen_from_bottom_button', (tester) async { + await _pumpApp(tester); + await _openSeriesPlayer(tester); + + expect(find.byKey(const Key('player-bottom-clear-screen')), findsOneWidget); + expect(find.byKey(const Key('player-pip')), findsNothing); + await tester.tap(find.byKey(const Key('player-bottom-clear-screen'))); + await tester.pumpAndSettle(); + + expect(find.byKey(const Key('player-controls-top')), findsNothing); + expect(find.byKey(const Key('player-controls-bottom')), findsNothing); + expect(find.byKey(const Key('player-exit-clear-screen')), findsOneWidget); + }); + testWidgets('should_open_unlock_panel_with_honest_method_states', ( tester, ) async { @@ -478,6 +492,8 @@ void _expectFullscreenOverlay(WidgetTester tester) { } expect(find.byKey(const Key('player-play-toggle')), findsNothing); expect(find.byKey(const Key('player-episodes')), findsOneWidget); + expect(find.byKey(const Key('player-bottom-clear-screen')), findsOneWidget); + expect(find.byKey(const Key('player-pip')), findsNothing); expect(find.byKey(const Key('player-rail-comments')), findsOneWidget); expect(find.byKey(const Key('player-fullscreen-toggle')), findsNothing); expect(find.byType(Slider), findsOneWidget);