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