style(player): 播放页与首页浮层的控件样式统一

播放页顶部按钮与选集条去掉底色、进度条不再画滑块圆点并压到最底部;
剧名与简介收在互动栏左侧,清屏后只留右下角退出按钮。
首页观看条与进度条共用 20pt 页边距,进度槽底色与播放页一致。
This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-29 15:44:22 +08:00
1 parent d5bcda54d3
commit 543d0aadd5
12 files changed
+298 -297

No files matched your search

+82 -59
View File
@@ -19,8 +19,17 @@ import 'seek_progress_bar.dart';
part 'player_overlay_parts.dart';
const double _topButtonSize = 36;
const double _topButtonSize = 40;
const double _topBarHeight = 58;
/// 底部信息、选集条、进度条之间的统一行距。
const double _blockGap = 8;
/// 底部选集条与右侧清屏按钮的统一高度,两者必须等高。
const double _bottomActionHeight = 34;
/// 选集条内部左右留白。
const double _bottomActionInset = 10;
const double _boostHintIconSize = 16;
const double _boostHintFontSize = 13;
const double _synopsisRailInset = 54;
@@ -107,11 +116,12 @@ class PlayerPlaybackOverlay extends StatelessWidget {
key: const Key('player-back'),
tooltip: data.l10n.backToSeries,
icon: Icons.chevron_left_rounded,
iconSize: 30,
onPressed: actions.onBack!,
)
else
const SizedBox(width: _topButtonSize),
const SizedBox(width: 8),
const SizedBox(width: 2),
Expanded(
child: Text(
data.l10n.episodeLabel(data.episode.number),
@@ -135,7 +145,7 @@ class PlayerPlaybackOverlay extends StatelessWidget {
_TopButton(
key: const Key('player-more'),
tooltip: data.l10n.more,
icon: Icons.more_vert_rounded,
icon: Icons.more_horiz_rounded,
onPressed: actions.onMore,
),
],
@@ -226,19 +236,20 @@ class PlayerPlaybackOverlay extends StatelessWidget {
child: _bottomInfo(context),
),
if (data.boosting) _boostHint(),
_bottomProgress(),
// 加速时收起底栏。拖动进度时底栏只隐藏,占位保留,进度条不挪位。
// 加速时收起选集条。拖动进度时只隐藏,占位保留,底部不跳动。
scrubbing
? IgnorePointer(
child: Opacity(opacity: 0, child: _bottomActions()),
)
: Offstage(offstage: data.boosting, child: _bottomActions()),
// 进度条压在最底部,和首页 feed 一致。
_bottomProgress(),
],
),
);
}
/// 加速提示:橙色小三角 + 当前倍速文案,悬在进度条上方。
/// 加速提示:橙色小三角 + 当前倍速文案,压在底部信息区下方。
Widget _boostHint() {
return Padding(
key: const Key('player-speed-boost-hint'),
@@ -270,25 +281,28 @@ class PlayerPlaybackOverlay extends StatelessWidget {
Widget _bottomInfo(BuildContext context) {
final series = localizedDramaTitle(data.l10n, data.drama);
final synopsis = localizedDramaSynopsis(data.l10n, data.drama);
// 右侧让出互动栏的宽度:剧名和简介一样,收在互动栏左侧,长标题也不会钻到按钮下面。
return Padding(
key: const Key('player-controls-bottom'),
padding: const EdgeInsets.fromLTRB(14, 8, 14, 0),
padding: const EdgeInsets.fromLTRB(
14,
_blockGap,
14 + _synopsisRailInset,
0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_detailRow(series),
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.only(right: _synopsisRailInset),
child: _EpisodeSynopsis(
key: const Key('player-episode-synopsis'),
l10n: data.l10n,
drama: data.drama,
episodeNumber: data.episode.number,
synopsis: synopsis,
authorNote: data.authorNote,
),
_EpisodeSynopsis(
key: const Key('player-episode-synopsis'),
l10n: data.l10n,
drama: data.drama,
episodeNumber: data.episode.number,
synopsis: synopsis,
authorNote: data.authorNote,
),
],
),
@@ -296,9 +310,10 @@ class PlayerPlaybackOverlay extends StatelessWidget {
}
Widget _bottomProgress() {
// 进度条不贴底:底部留 16pt,线心离底边更远,也不再和选集条挤在一起。
return Padding(
key: const Key('player-progress-container'),
padding: EdgeInsets.fromLTRB(14, controlsVisible ? 2 : 8, 14, 0),
padding: const EdgeInsets.fromLTRB(14, 2, 14, 16),
child: SeekProgressBar(
key: const Key('player-progress'),
valueListenable: data.controller,
@@ -345,10 +360,9 @@ class PlayerPlaybackOverlay extends StatelessWidget {
),
),
),
const SizedBox(width: 4),
const Icon(
Icons.chevron_right_rounded,
color: AppColors.playerMuted,
color: AppColors.onPrimary,
size: 20,
shadows: _playerTextShadows,
),
@@ -362,55 +376,63 @@ class PlayerPlaybackOverlay extends StatelessWidget {
Widget _bottomActions() {
return Padding(
key: const Key('player-bottom-actions'),
padding: const EdgeInsets.fromLTRB(14, 6, 14, 6),
padding: const EdgeInsets.fromLTRB(14, 2, 14, 0),
child: Row(
children: [
Expanded(
child: Material(
key: const Key('player-episodes-pill'),
color: AppColors.playerOverlayPill,
borderRadius: BorderRadius.circular(7),
clipBehavior: Clip.antiAlias,
child: InkWell(
key: const Key('player-episodes'),
onTap: actions.onEpisodes,
borderRadius: BorderRadius.circular(7),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 14,
vertical: 8,
),
child: Row(
children: [
Expanded(
child: Text(
'${data.l10n.episodes} · '
'${data.l10n.allEpisodesBadge(data.totalEpisodes)} · '
'${data.l10n.freeWatch}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
// 清屏后只留右下角的退出按钮,左侧选集条一起收起。
if (!controlsVisible)
const Spacer()
else ...[
Expanded(
// 选集条给一层浅底色:视频画面亮时纯文字不好认,也顺带标出可点区域。
child: Material(
key: const Key('player-episodes-pill'),
color: AppColors.textPrimary.withValues(alpha: 0.14),
borderRadius: BorderRadius.circular(8),
clipBehavior: Clip.antiAlias,
child: InkWell(
key: const Key('player-episodes'),
onTap: actions.onEpisodes,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: _bottomActionInset,
),
child: SizedBox(
height: _bottomActionHeight,
child: Row(
children: [
Expanded(
child: Text(
'${data.l10n.episodes} · '
'${data.l10n.allEpisodesBadge(data.totalEpisodes)} · '
'${data.l10n.freeWatch}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.onPrimary,
fontSize: 13.5,
fontWeight: FontWeight.w800,
shadows: _playerTextShadows,
),
),
),
const SizedBox(width: 8),
const Icon(
Icons.keyboard_arrow_up_rounded,
color: AppColors.onPrimary,
fontSize: 13.5,
fontWeight: FontWeight.w800,
size: 18,
shadows: _playerTextShadows,
),
),
],
),
const SizedBox(width: 8),
const Icon(
Icons.keyboard_arrow_up_rounded,
color: AppColors.onPrimary,
size: 18,
shadows: _playerTextShadows,
),
],
),
),
),
),
),
),
const SizedBox(width: 8),
const SizedBox(width: 8),
],
_BottomIconButton(
key: const Key('player-bottom-clear-screen'),
tooltip: controlsVisible
@@ -419,6 +441,7 @@ class PlayerPlaybackOverlay extends StatelessWidget {
icon: controlsVisible
? Icons.fullscreen_rounded
: Icons.fullscreen_exit_rounded,
height: _bottomActionHeight,
onPressed: controlsVisible ? actions.onClear : actions.onExitClear,
),
],