diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart index c4e8e60..131a8d5 100644 --- a/lib/core/theme/app_colors.dart +++ b/lib/core/theme/app_colors.dart @@ -91,20 +91,11 @@ abstract final class AppColors { /// 导航未选中。 static const navUnselected = muted; - /// 播放器专用深色页面背景。 - static const playerBackground = Color(0xFF0D0D10); - - /// 播放器底部信息面板,保留轻微视频透出。 - static const playerPanel = Color(0xED0D0D10); - /// 播放器次要文字。 static const playerMuted = Color(0xFFBBBBBB); - /// 播放器底栏胶囊。 - static const playerChip = Color(0xFF1C1C1E); - - /// 播放器面板分割线。 - static const playerDivider = Color(0xFF2A2A2D); + /// 播放器控件胶囊与圆形底色。 + static const playerOverlayPill = Color(0xB3000000); static const outlineDark = border; static const outlineVariantDark = border; diff --git a/lib/pages/player/player_view.dart b/lib/pages/player/player_view.dart index 428be04..a09199c 100644 --- a/lib/pages/player/player_view.dart +++ b/lib/pages/player/player_view.dart @@ -20,7 +20,6 @@ import '../../core/theme/app_colors.dart'; import '../../core/theme/app_theme.dart'; import '../../core/widgets/auth_gate.dart'; import '../../core/widgets/download_action.dart'; -import '../../core/widgets/remote_image.dart'; import '../../l10n/app_localizations.dart'; import '../interaction/widgets/report_dialog.dart'; import 'player_binding.dart'; @@ -168,7 +167,11 @@ class _PlayerPageState extends State with WidgetsBindingObserver { data: AppTheme.light(), child: widget.embedded ? content - : Scaffold(backgroundColor: AppColors.ink, body: content), + : Scaffold( + key: const Key('player-scaffold'), + backgroundColor: Colors.transparent, + body: content, + ), ), ); } @@ -215,17 +218,11 @@ class _PlayerPageState extends State with WidgetsBindingObserver { return Stack( fit: StackFit.expand, children: [ - ColoredBox( - key: const Key('player-background'), - color: AppColors.playerBackground, - child: widget.homeMode - ? null - : RemoteImage( - drama.posterUrl, - fit: BoxFit.cover, - showFallbackIcon: true, - ), - ), + if (!ready) + const ColoredBox( + key: Key('player-fallback-background'), + color: Colors.black, + ), ?video, if (widget.homeMode && _chromeVisible && !logic.chromeHidden) Positioned.fill( diff --git a/lib/pages/player/widgets/player_overlay.dart b/lib/pages/player/widgets/player_overlay.dart index 91b097c..4e3128a 100644 --- a/lib/pages/player/widgets/player_overlay.dart +++ b/lib/pages/player/widgets/player_overlay.dart @@ -16,7 +16,9 @@ const double _topButtonSize = 36; const double _topBarHeight = 58; const double _railButtonSize = 34; const double _railGap = 18; -const double _panelRadius = 16; +const List _playerTextShadows = [ + Shadow(color: Colors.black54, blurRadius: 5, offset: Offset(0, 1)), +]; /// K29 竖屏播放页浮层。 class PlayerPlaybackOverlay extends StatelessWidget { @@ -86,13 +88,6 @@ class PlayerPlaybackOverlay extends StatelessWidget { key: const Key('player-controls-top'), height: _topBarHeight, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xCC000000), Color(0x00000000)], - ), - ), child: Row( children: [ if (actions.onBack != null) @@ -115,6 +110,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { color: AppColors.onPrimary, fontSize: 15, fontWeight: FontWeight.w700, + shadows: _playerTextShadows, ), ), ), @@ -163,16 +159,9 @@ class PlayerPlaybackOverlay extends StatelessWidget { left: 0, right: 0, bottom: 0, - child: Container( + child: Padding( key: const Key('player-controls-bottom'), padding: const EdgeInsets.fromLTRB(14, 8, 14, 6), - decoration: const BoxDecoration( - color: AppColors.playerPanel, - border: Border(top: BorderSide(color: AppColors.playerDivider)), - borderRadius: BorderRadius.vertical( - top: Radius.circular(_panelRadius), - ), - ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -226,6 +215,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { fontFamily: 'Outfit', fontSize: 18 * data.textScale, fontWeight: FontWeight.w800, + shadows: _playerTextShadows, ), ), ), @@ -234,6 +224,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { Icons.chevron_right_rounded, color: AppColors.playerMuted, size: 20, + shadows: _playerTextShadows, ), ], ), @@ -247,7 +238,8 @@ class PlayerPlaybackOverlay extends StatelessWidget { children: [ Expanded( child: Material( - color: AppColors.playerChip, + key: const Key('player-episodes-pill'), + color: AppColors.playerOverlayPill, borderRadius: BorderRadius.circular(999), clipBehavior: Clip.antiAlias, child: InkWell( @@ -272,6 +264,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { color: AppColors.onPrimary, fontSize: 11, fontWeight: FontWeight.w700, + shadows: _playerTextShadows, ), ), ), @@ -280,6 +273,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { Icons.keyboard_arrow_up_rounded, color: AppColors.onPrimary, size: 16, + shadows: _playerTextShadows, ), ], ), diff --git a/lib/pages/player/widgets/player_overlay_parts.dart b/lib/pages/player/widgets/player_overlay_parts.dart index d41b36b..f50403f 100644 --- a/lib/pages/player/widgets/player_overlay_parts.dart +++ b/lib/pages/player/widgets/player_overlay_parts.dart @@ -55,6 +55,7 @@ class _SpeedEntry extends StatelessWidget { Icons.speed_rounded, color: AppColors.onPrimary, size: 15, + shadows: _playerTextShadows, ), const SizedBox(width: 4), Text( @@ -63,6 +64,7 @@ class _SpeedEntry extends StatelessWidget { color: AppColors.onPrimary, fontSize: 12, fontWeight: FontWeight.w700, + shadows: _playerTextShadows, ), ), ], @@ -93,7 +95,7 @@ class _BottomIconButton extends StatelessWidget { tooltip: tooltip, padding: EdgeInsets.zero, style: IconButton.styleFrom( - backgroundColor: AppColors.playerChip, + backgroundColor: AppColors.playerOverlayPill, foregroundColor: AppColors.onPrimary, ), onPressed: onPressed, @@ -252,16 +254,20 @@ class _EpisodeSynopsisState extends State<_EpisodeSynopsis> { widget.l10n.episodeLabel(widget.episodeNumber), key: const Key('player-episode-label'), style: TextStyle( - color: AppColors.blue, + color: AppColors.onPrimary, fontSize: 11 * widget.textScale, fontWeight: FontWeight.w700, + shadows: _playerTextShadows, ), ), const Padding( padding: EdgeInsets.symmetric(horizontal: 6), child: Text( '|', - style: TextStyle(color: AppColors.playerDivider), + style: TextStyle( + color: Colors.white54, + shadows: _playerTextShadows, + ), ), ), Expanded( @@ -274,6 +280,7 @@ class _EpisodeSynopsisState extends State<_EpisodeSynopsis> { color: AppColors.playerMuted, fontSize: 11 * widget.textScale, height: 1.35, + shadows: _playerTextShadows, ), ), ), @@ -284,6 +291,7 @@ class _EpisodeSynopsisState extends State<_EpisodeSynopsis> { : Icons.keyboard_arrow_down_rounded, color: AppColors.playerMuted, size: 16, + shadows: _playerTextShadows, ), ], ), @@ -295,6 +303,7 @@ class _EpisodeSynopsisState extends State<_EpisodeSynopsis> { style: const TextStyle( color: AppColors.playerMuted, fontSize: 10, + shadows: _playerTextShadows, ), ), ], diff --git a/test/player/player_fullscreen_test.dart b/test/player/player_fullscreen_test.dart index f4a78e7..47eb77e 100644 --- a/test/player/player_fullscreen_test.dart +++ b/test/player/player_fullscreen_test.dart @@ -187,16 +187,35 @@ void main() { final rail = tester.getRect( find.byKey(const Key('player-interaction-rail')), ); + expect(find.byKey(const Key('player-background')), findsNothing); + final fallback = find.byKey(const Key('player-fallback-background')); + if (fallback.evaluate().isNotEmpty) { + expect(tester.widget(fallback).color, Colors.black); + } expect( tester - .widget(find.byKey(const Key('player-background'))) + .widget(find.byKey(const Key('player-scaffold'))) + .backgroundColor, + Colors.transparent, + ); + expect( + tester + .widget(find.byKey(const Key('player-controls-top'))) + .decoration, + isNull, + ); + expect( + tester + .widget(find.byKey(const Key('player-controls-bottom'))) + .child, + isNotNull, + ); + expect( + tester + .widget(find.byKey(const Key('player-episodes-pill'))) .color, - AppColors.playerBackground, + AppColors.playerOverlayPill, ); - final panel = tester.widget( - find.byKey(const Key('player-controls-bottom')), - ); - expect((panel.decoration! as BoxDecoration).color, AppColors.playerPanel); expect(bottom.height, lessThanOrEqualTo(200)); expect(bottom.top, greaterThanOrEqualTo(screenHeight * 0.78));