style(player): 播放页与首页浮层的控件样式统一
播放页顶部按钮与选集条去掉底色、进度条不再画滑块圆点并压到最底部; 剧名与简介收在互动栏左侧,清屏后只留右下角退出按钮。 首页观看条与进度条共用 20pt 页边距,进度槽底色与播放页一致。
This commit is contained in:
1 parent
d5bcda54d3
commit
543d0aadd5
12 files changed
+298
-297
No files matched your search
@@ -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,
|
||||
),
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user