Files
QingTing-Player/lib/widgets/add_to_playlist_sheet.dart
lxh2875931338 7bf8d18f18 fix(ui): 加入歌单弹层不再被迷你播放器遮挡
原因:迷你播放器挂在 Navigator overlay 上,层级高于底部弹层,
弹层里的歌单列表被它盖住,看起来像"没读到歌单"。
(日志确认查询正常:读到 1 个歌单)

- RouteManager 抽到 services/route_manager.dart,新增临时隐藏开关
- 打开"加入歌单"弹层时隐藏迷你播放器,关闭后恢复
- main.dart 的 shouldShow 改用 showMiniPlayer(原来直接读路由,开关不生效)
- 弹层底部加 SafeArea minimum + 16px 余量,不再贴着手势区

真机验证:弹层正常显示 "TL / 1 首",底部留出安全距离。
2026-11-01 00:09:27 +08:00

173 lines
5.6 KiB
Dart

// lib/widgets/add_to_playlist_sheet.dart
import 'package:flutter/material.dart';
import '../models/playlist_summary.dart';
import '../repositories/playlist_repository.dart';
import '../services/route_manager.dart';
/// 把一首歌加入歌单的底部弹层。
///
/// 视觉先保持系统样式,等设计定稿后只换这个文件即可,
/// 调用方(文件列表长按)不用改。
Future<void> showAddToPlaylistSheet(
BuildContext context, {
required String songPath,
}) async {
final repo = PlaylistRepository();
List<PlaylistSummary> playlists;
try {
playlists = await repo.getSummaries();
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('读取歌单失败: $e')),
);
}
return;
}
if (!context.mounted) return;
RouteManager().setTemporarilyHidden(true);
try {
await showModalBottomSheet<void>(
context: context,
backgroundColor: const Color(0xFF1A1F1E),
builder: (sheetContext) {
return SafeArea(
minimum: const EdgeInsets.only(bottom: 12),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(20, 18, 20, 8),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'加入歌单',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
),
ListTile(
leading: const Icon(Icons.add, color: Color(0xFFB8D4D0)),
title: const Text(
'新建歌单',
style: TextStyle(color: Color(0xFFB8D4D0)),
),
onTap: () async {
Navigator.pop(sheetContext);
await _createPlaylistAndAdd(context, repo, songPath);
},
),
const Divider(height: 1, color: Colors.white12),
if (playlists.isEmpty)
const Padding(
padding: EdgeInsets.symmetric(vertical: 24),
child: Text(
'还没有歌单,先新建一个',
style: TextStyle(color: Colors.grey, fontSize: 13),
),
)
else
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 320),
child: ListView.builder(
shrinkWrap: true,
itemCount: playlists.length,
itemBuilder: (context, index) {
final playlist = playlists[index];
return ListTile(
leading: const Icon(
Icons.queue_music,
color: Color(0xFFB8D4D0),
),
title: Text(
playlist.name,
style: const TextStyle(color: Colors.white),
),
subtitle: Text(
'${playlist.songCount} 首',
style: const TextStyle(
color: Colors.grey, fontSize: 12),
),
onTap: () async {
final added = await repo.addSongs(
playlist.id,
[songPath],
);
if (!sheetContext.mounted) return;
Navigator.pop(sheetContext);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(added > 0
? '已加入「${playlist.name}」'
: '这首歌已经在「${playlist.name}」里了'),
),
);
},
);
},
),
),
const SizedBox(height: 16),
],
),
);
},
);
} finally {
RouteManager().setTemporarilyHidden(false);
}
}
Future<void> _createPlaylistAndAdd(
BuildContext context,
PlaylistRepository repo,
String songPath,
) async {
final controller = TextEditingController();
final name = await showDialog<String>(
context: context,
builder: (dialogContext) => AlertDialog(
backgroundColor: const Color(0xFF1A1F1E),
title: const Text('新建歌单', style: TextStyle(color: Colors.white)),
content: TextField(
controller: controller,
autofocus: true,
style: const TextStyle(color: Colors.white),
decoration: const InputDecoration(hintText: '歌单名称'),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: const Text('取消'),
),
TextButton(
onPressed: () =>
Navigator.pop(dialogContext, controller.text.trim()),
child: const Text('创建'),
),
],
),
);
if (name == null || name.isEmpty) return;
try {
final playlist = await repo.createPlaylist(name);
await repo.addSongs(playlist.id, [songPath]);
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('已新建「$name」并加入这首歌')),
);
} catch (e) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('新建歌单失败: $e')),
);
}
}