style(ui): 播放页间距等宽 + 控制行上移 25 + 进度条重写

- 标题/歌手/专辑的间距统一为 10,视感更整齐
- 底部留白 70 -> 45(控制行上移 25)
- 进度条改用 LayoutBuilder + Positioned 绝对宽度,
  背景/缓冲/已播三层顺序明确,不再出现两端亮块
This commit is contained in:
2026-10-31 23:31:54 +08:00
parent 1c57d59d73
commit e3f72a69a9
+82 -65
View File
@@ -308,7 +308,7 @@ class _PlayerPageState extends BaseState<PlayerPage> {
Widget _buildMainPage(AudioService service, Song song, bool isPlaying) { Widget _buildMainPage(AudioService service, Song song, bool isPlaying) {
return Padding( return Padding(
// 顶部留 16:与 AppBar/指示器拉开距离,避免封面贴着顶部 // 顶部留 16:与 AppBar/指示器拉开距离,避免封面贴着顶部
padding: const EdgeInsets.fromLTRB(24, 16, 24, 0), padding: const EdgeInsets.fromLTRB(24, 24, 24, 0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -366,17 +366,17 @@ class _PlayerPageState extends BaseState<PlayerPage> {
), ),
], ],
), ),
const SizedBox(height: 2), const SizedBox(height: 10),
Text( Text(
song.artist, song.artist,
style: TextStyle(fontSize: 14, color: Colors.grey[400]), style: TextStyle(fontSize: 16, color: Colors.grey[300]),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
// ---- 信息区(专辑 / 目录)---- // ---- 信息区(专辑 / 目录)----
if (_album.isNotEmpty || _folder.isNotEmpty) ...[ if (_album.isNotEmpty || _folder.isNotEmpty) ...[
const SizedBox(height: 14), const SizedBox(height: 10),
if (_album.isNotEmpty) if (_album.isNotEmpty)
Text( Text(
_album, _album,
@@ -409,7 +409,8 @@ class _PlayerPageState extends BaseState<PlayerPage> {
// ---- 控制 ---- // ---- 控制 ----
_buildControlButtons(service, song, isPlaying), _buildControlButtons(service, song, isPlaying),
const SizedBox(height: 20), // 底部多留 50px:控制行整体上移
const SizedBox(height: 45),
], ],
), ),
); );
@@ -548,70 +549,86 @@ class _PlayerPageState extends BaseState<PlayerPage> {
Expanded( Expanded(
child: SizedBox( child: SizedBox(
height: 24, height: 24,
child: Stack( child: LayoutBuilder(
alignment: Alignment.centerLeft, builder: (context, constraints) {
children: [ final width = constraints.maxWidth;
Container( return Stack(
height: 3,
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(2),
),
),
FractionallySizedBox(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
widthFactor: bufferProgress.clamp(0.0, 1.0), children: [
child: Container( // 背景轨道
height: 3, Positioned(
decoration: BoxDecoration( left: 0,
color: const Color(0xFF4A7A7A), right: 0,
borderRadius: BorderRadius.circular(2), top: 10.5,
), child: Container(
), height: 3,
), decoration: BoxDecoration(
FractionallySizedBox( color: Colors.grey[800],
alignment: Alignment.centerLeft, borderRadius: BorderRadius.circular(2),
widthFactor: displayProgress.clamp(0.0, 1.0), ),
child: Container(
height: 3,
decoration: BoxDecoration(
color: const Color(0xFFB8D4D0),
borderRadius: BorderRadius.circular(2),
),
),
),
Positioned.fill(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
trackHeight: 0,
activeTrackColor: Colors.transparent,
inactiveTrackColor: Colors.transparent,
thumbColor: Colors.transparent,
overlayColor: Colors.transparent,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 0,
), ),
), ),
child: Slider( // 已缓冲
value: displayProgress.clamp(0.0, 1.0), Positioned(
min: 0.0, left: 0,
max: 1.0, top: 10.5,
onChanged: (value) { child: Container(
setState(() => _dragProgress = value); width: width * bufferProgress.clamp(0.0, 1.0),
}, height: 3,
onChangeEnd: (value) { decoration: BoxDecoration(
final newPosition = Duration( color: const Color(0xFF4A7A7A),
milliseconds: borderRadius: BorderRadius.circular(2),
(value * duration.inMilliseconds) ),
.round(), ),
);
service.seekTo(newPosition);
setState(() => _dragProgress = null);
},
), ),
), // 已播放
), Positioned(
], left: 0,
top: 10.5,
child: Container(
width:
width * displayProgress.clamp(0.0, 1.0),
height: 3,
decoration: BoxDecoration(
color: const Color(0xFFB8D4D0),
borderRadius: BorderRadius.circular(2),
),
),
),
Positioned.fill(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
trackHeight: 0,
activeTrackColor: Colors.transparent,
inactiveTrackColor: Colors.transparent,
thumbColor: Colors.transparent,
overlayColor: Colors.transparent,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 0,
),
),
child: Slider(
value: displayProgress.clamp(0.0, 1.0),
min: 0.0,
max: 1.0,
onChanged: (value) {
setState(() => _dragProgress = value);
},
onChangeEnd: (value) {
final newPosition = Duration(
milliseconds:
(value * duration.inMilliseconds)
.round(),
);
service.seekTo(newPosition);
setState(() => _dragProgress = null);
},
),
),
),
],
);
},
), ),
), ),
), ),