优化部分功能逻辑:元数据信息漂移

优化部分体验问题,包含动画、界面层级等
This commit is contained in:
2026-08-26 21:13:09 +08:00
parent 94d37fe263
commit 8f3f827429
3 changed files with 251 additions and 198 deletions
+99 -73
View File
@@ -1,3 +1,4 @@
// lib/main.dart
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
@@ -49,10 +50,10 @@ void main() async {
debugPrint('❌ MediaKit 初始化失败: $e'); debugPrint('❌ MediaKit 初始化失败: $e');
} }
// ⭐ 创建 HandlerAudioService 稍后初始化) // ⭐ 创建 Handler
_audioHandler = AudioPlayerHandler(); _audioHandler = AudioPlayerHandler();
// ⭐ 注册切歌回调:当歌曲切换时,立即更新通知 // ⭐ 注册切歌回调
AudioService().setOnSongChanged((song) { AudioService().setOnSongChanged((song) {
debugPrint( debugPrint(
'📢 [main] song.artwork is ${song.artwork != null ? 'not null' : 'null'}'); '📢 [main] song.artwork is ${song.artwork != null ? 'not null' : 'null'}');
@@ -60,7 +61,7 @@ void main() async {
id: song.id, id: song.id,
title: song.title, title: song.title,
artist: song.artist, artist: song.artist,
artwork: song.artwork, // ⭐ 传递封面图 artwork: song.artwork,
); );
}); });
@@ -69,7 +70,6 @@ void main() async {
providers: [ providers: [
ChangeNotifierProvider(create: (_) => AudioService()), ChangeNotifierProvider(create: (_) => AudioService()),
ChangeNotifierProvider(create: (_) => AppLifecycleService()), ChangeNotifierProvider(create: (_) => AppLifecycleService()),
// ⭐ 生命周期管理器
ChangeNotifierProvider<AppLifecycleManager>( ChangeNotifierProvider<AppLifecycleManager>(
create: (_) => lifecycleManager, create: (_) => lifecycleManager,
), ),
@@ -108,9 +108,18 @@ class RouteManager extends ChangeNotifier {
// ════════════════════════════════════════════════════════════ // ════════════════════════════════════════════════════════════
// 导航观察者(监听路由变化) // 导航观察者(监听路由变化)
// ════════════════════════════════════════════════════════════ // ════════════════════════════════════════════════════════════
// lib/main.dart
class MiniPlayerNavigatorObserver extends NavigatorObserver { class MiniPlayerNavigatorObserver extends NavigatorObserver {
void _updateRoute(Route? route) { void _updateRoute(Route? route) {
final name = route?.settings.name ?? '/'; if (route == null) return;
// ⭐ 忽略 PopupRoute(菜单、对话框等)
if (route is PopupRoute) {
return;
}
final name = route.settings.name ?? '/';
RouteManager().updateRoute(name); RouteManager().updateRoute(name);
} }
@@ -121,12 +130,21 @@ class MiniPlayerNavigatorObserver extends NavigatorObserver {
@override @override
void didPop(Route route, Route? previousRoute) { void didPop(Route route, Route? previousRoute) {
_updateRoute(previousRoute); // ⭐ 返回时,使用上一个路由(previousRoute
if (previousRoute != null && previousRoute is! PopupRoute) {
final name = previousRoute.settings.name ?? '/';
RouteManager().updateRoute(name);
} else if (previousRoute == null) {
RouteManager().updateRoute('/');
}
} }
@override @override
void didReplace({Route? newRoute, Route? oldRoute}) { void didReplace({Route? newRoute, Route? oldRoute}) {
_updateRoute(newRoute); if (newRoute != null && newRoute is! PopupRoute) {
final name = newRoute.settings.name ?? '/';
RouteManager().updateRoute(name);
}
} }
} }
@@ -141,49 +159,31 @@ class QTPlayerApp extends StatefulWidget {
} }
class _QTPlayerAppState extends State<QTPlayerApp> { class _QTPlayerAppState extends State<QTPlayerApp> {
// ⭐ build 计数器(诊断用)
static int _buildCount = 0; static int _buildCount = 0;
bool _isInitializing = false; bool _isInitializing = false;
// ⭐ 保存生命周期监听器引用
void Function(AppLifecycleStatus)? _lifecycleListener; void Function(AppLifecycleStatus)? _lifecycleListener;
// ============================================================
// 生命周期
// ============================================================
@override @override
void initState() { void initState() {
super.initState(); super.initState();
debugPrint('⏱️ T1.5 initState: ${stopwatch.elapsedMilliseconds}ms'); debugPrint('⏱️ T1.5 initState: ${stopwatch.elapsedMilliseconds}ms');
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
debugPrint('⏱️ T2 首帧回调: ${stopwatch.elapsedMilliseconds}ms'); debugPrint('⏱️ T2 首帧回调: ${stopwatch.elapsedMilliseconds}ms');
// ⭐ 1. 初始化生命周期管理器
lifecycleManager.init(); lifecycleManager.init();
// ⭐ 2. 绑定生命周期到播放器
_bindLifecycleToPlayback(); _bindLifecycleToPlayback();
// ⭐ 3. 后台初始化
_startBackgroundInitialization(); _startBackgroundInitialization();
}); });
} }
@override @override
void dispose() { void dispose() {
// ⭐ 移除生命周期监听器
if (_lifecycleListener != null) { if (_lifecycleListener != null) {
lifecycleManager.removeListenerFromManager(_lifecycleListener!); lifecycleManager.removeListenerFromManager(_lifecycleListener!);
} }
super.dispose(); super.dispose();
} }
// ============================================================
// 生命周期 → 播放器绑定
// ============================================================
void _bindLifecycleToPlayback() { void _bindLifecycleToPlayback() {
if (_lifecycleListener != null) { if (_lifecycleListener != null) {
lifecycleManager.removeListenerFromManager(_lifecycleListener!); lifecycleManager.removeListenerFromManager(_lifecycleListener!);
@@ -195,15 +195,12 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
case AppLifecycleStatus.inactive: case AppLifecycleStatus.inactive:
debugPrint('🎵 App 进入后台,播放继续'); debugPrint('🎵 App 进入后台,播放继续');
break; break;
case AppLifecycleStatus.resumed: case AppLifecycleStatus.resumed:
debugPrint('🎵 App 回到前台'); debugPrint('🎵 App 回到前台');
break; break;
case AppLifecycleStatus.backgroundIdle: case AppLifecycleStatus.backgroundIdle:
debugPrint('🎵 长后台唤醒,检查播放状态'); debugPrint('🎵 长后台唤醒,检查播放状态');
break; break;
default: default:
break; break;
} }
@@ -212,9 +209,6 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
lifecycleManager.addListenerToManager(_lifecycleListener!); lifecycleManager.addListenerToManager(_lifecycleListener!);
} }
// ============================================================
// 后台初始化(精细时间切片)
// ============================================================
Future<void> _startBackgroundInitialization() async { Future<void> _startBackgroundInitialization() async {
if (_isInitializing) return; if (_isInitializing) return;
_isInitializing = true; _isInitializing = true;
@@ -222,7 +216,6 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
final tStart = DateTime.now(); final tStart = DateTime.now();
debugPrint('⏱️ T3 后台初始化开始'); debugPrint('⏱️ T3 后台初始化开始');
// ----- 1. PlaybackService -----
final t1 = DateTime.now(); final t1 = DateTime.now();
try { try {
PlaybackService().init(); PlaybackService().init();
@@ -231,7 +224,6 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
debugPrint('❌ PlaybackService 失败: $e'); debugPrint('❌ PlaybackService 失败: $e');
} }
// ----- 2. WebDAV(只加载凭证) -----
final t2 = DateTime.now(); final t2 = DateTime.now();
try { try {
final hasCred = await WebDAVService.instance.loadCredentials(); final hasCred = await WebDAVService.instance.loadCredentials();
@@ -240,10 +232,8 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
debugPrint('❌ WebDAV 凭证加载失败: $e'); debugPrint('❌ WebDAV 凭证加载失败: $e');
} }
// ----- 3. ⭐ AudioService(后台播放核心) -----
await _initAudioService(); await _initAudioService();
// ----- 4. 通知权限 -----
final t3 = DateTime.now(); final t3 = DateTime.now();
try { try {
if (await Permission.notification.isDenied) { if (await Permission.notification.isDenied) {
@@ -260,9 +250,6 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
debugPrint('⏱️ T4 全部初始化完成: ${tEnd.difference(tStart).inMilliseconds}ms'); debugPrint('⏱️ T4 全部初始化完成: ${tEnd.difference(tStart).inMilliseconds}ms');
} }
// ════════════════════════════════════════════════════════════
// ⭐ AudioService 初始化(后台播放核心)
// ════════════════════════════════════════════════════════════
Future<void> _initAudioService() async { Future<void> _initAudioService() async {
try { try {
await audio_service.AudioService.init( await audio_service.AudioService.init(
@@ -280,42 +267,61 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
} }
} }
// ════════════════════════════════════════════════════════════
// 辅助方法
// ════════════════════════════════════════════════════════════
String _elapsedMs(DateTime start) { String _elapsedMs(DateTime start) {
return '${DateTime.now().difference(start).inMilliseconds}'; return '${DateTime.now().difference(start).inMilliseconds}';
} }
// ════════════════════════════════════════════════════════════ // ⭐ 路由生成(播放页滑入动画,速度 200ms)
// Build // lib/main.dart - _onGenerateRoute 方法
// ════════════════════════════════════════════════════════════ Route<dynamic> _onGenerateRoute(RouteSettings settings) {
if (settings.name == '/player') {
return PageRouteBuilder(
settings: settings,
pageBuilder: (context, animation, secondaryAnimation) {
return const PlayerPage();
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
// ⭐ 应用贝塞尔曲线
final curvedAnimation = CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic, // 平滑减速
// curve: Curves.easeOutBack, // 轻微回弹,更灵动
// curve: Curves.fastOutSlowIn, // Material 风格
);
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
final tween = Tween(begin: begin, end: end);
final offsetAnimation = tween.animate(curvedAnimation);
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 300),
);
}
return MaterialPageRoute(
settings: settings,
builder: (context) {
switch (settings.name) {
case '/':
return const HomePage();
case '/playlist':
return const PlaylistPage();
default:
return const HomePage();
}
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_buildCount++; _buildCount++;
debugPrint( debugPrint(
'🎨 [QTPlayerApp] build #$_buildCount ${stopwatch.elapsedMilliseconds}ms'); '🎨 [QTPlayerApp] build #$_buildCount ${stopwatch.elapsedMilliseconds}ms');
// 监听歌曲变化,更新通知栏
// main.dart 中 build 方法里的这部分
final audioService = context.watch<AudioService>();
final handler = context.read<AudioPlayerHandler>();
final song = audioService.currentSong;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (song != null) {
// ⭐ song 不为空时才调用
handler.updateNotification(
id: song.id,
title: song.title,
artist: song.artist,
);
} else {
// 清空通知
handler.mediaItem.add(null);
}
});
return MaterialApp( return MaterialApp(
title: '清听', title: '清听',
theme: ThemeData( theme: ThemeData(
@@ -331,11 +337,7 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
), ),
navigatorKey: navigatorKey, navigatorKey: navigatorKey,
navigatorObservers: [MiniPlayerNavigatorObserver()], navigatorObservers: [MiniPlayerNavigatorObserver()],
routes: { onGenerateRoute: _onGenerateRoute,
'/': (context) => const HomePage(),
'/player': (context) => const PlayerPage(),
'/playlist': (context) => const PlaylistPage(),
},
builder: (context, child) { builder: (context, child) {
return Stack( return Stack(
fit: StackFit.expand, fit: StackFit.expand,
@@ -344,12 +346,36 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
AnimatedBuilder( AnimatedBuilder(
animation: RouteManager(), animation: RouteManager(),
builder: (context, _) { builder: (context, _) {
if (!RouteManager().showMiniPlayer) { final routeManager = RouteManager();
return const SizedBox.shrink(); final shouldShow = routeManager.currentRoute != '/player' &&
} routeManager.currentRoute != '/playlist';
return const Align(
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
switchInCurve: Curves.easeOut,
switchOutCurve: Curves.easeIn,
transitionBuilder: (child, animation) {
// ⭐ 淡入淡出 + 轻微上滑
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.1),
end: Offset.zero,
).animate(animation),
child: child,
),
);
},
child: shouldShow
? const Align(
key: ValueKey('mini_player_visible'),
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: GlobalMiniPlayer(), child: GlobalMiniPlayer(),
)
: const SizedBox.shrink(
key: ValueKey('mini_player_hidden'),
),
); );
}, },
), ),
+49 -17
View File
@@ -3,6 +3,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'playback_service.dart'; import 'playback_service.dart';
import '../metadata/metadata_service.dart'; import '../metadata/metadata_service.dart';
@@ -60,6 +61,9 @@ class AudioService extends ChangeNotifier {
bool _isUserSeeking = false; bool _isUserSeeking = false;
// ⭐ 播放代数:每次切歌递增,用于校验异步任务是否过期
int _playbackGeneration = 0;
// ---- Getter ---- // ---- Getter ----
Song? get currentSong => _currentSong; Song? get currentSong => _currentSong;
bool get isPlaying => _isPlaying; bool get isPlaying => _isPlaying;
@@ -144,6 +148,9 @@ class AudioService extends ChangeNotifier {
return; return;
} }
_playbackGeneration++;
final generation = _playbackGeneration;
final song = _queue[_currentIndex]; final song = _queue[_currentIndex];
_currentSong = song; _currentSong = song;
@@ -158,7 +165,11 @@ class AudioService extends ChangeNotifier {
_syncPlayerStateDelayed(); _syncPlayerStateDelayed();
_loadMetadataForCurrentSong(); // ⭐ 立即推送基本信息(无 artwork)
_onSongChanged?.call(song);
// ⭐ 异步加载完整 metadata(含 artwork
_loadMetadataForCurrentSong(generation);
} }
String _generateSongKey(String url, int fileSize, int modifiedTime) { String _generateSongKey(String url, int fileSize, int modifiedTime) {
@@ -166,10 +177,21 @@ class AudioService extends ChangeNotifier {
return raw.hashCode.toString(); return raw.hashCode.toString();
} }
Future<void> _loadMetadataForCurrentSong() async { // ⭐ 核心:带 generation 校验的 metadata 加载
// lib/services/audio_service.dart
Future<void> _loadMetadataForCurrentSong(int generation) async {
if (_currentIndex < 0 || _currentIndex >= _queue.length) return; if (_currentIndex < 0 || _currentIndex >= _queue.length) return;
final song = _queue[_currentIndex];
if (song.url == null || song.url!.isEmpty) return; final currentIndex = _currentIndex;
final song = _queue[currentIndex];
final songId = song.id;
if (song.url == null || song.url!.isEmpty) {
// 没有 URL,直接推送 fallback
_onSongChanged?.call(song);
return;
}
try { try {
final metadata = await MetadataService().getMetadata( final metadata = await MetadataService().getMetadata(
@@ -178,23 +200,41 @@ class AudioService extends ChangeNotifier {
fileId: song.id, fileId: song.id,
); );
if (metadata.isNotEmpty) { // generation 校验
if (_playbackGeneration != generation) {
debugPrint('⚠️ [AudioService] metadata stale (generation), ignoring');
return;
}
if (_currentIndex != currentIndex) {
debugPrint(
'⚠️ [AudioService] metadata stale (index changed), ignoring');
return;
}
if (currentIndex >= _queue.length || _queue[currentIndex].id != songId) {
debugPrint('⚠️ [AudioService] metadata stale (song changed), ignoring');
return;
}
// 更新 Song
final updatedSong = Song( final updatedSong = Song(
id: song.id, id: song.id,
title: metadata.title.isNotEmpty ? metadata.title : song.title, title: metadata.title.isNotEmpty ? metadata.title : song.title,
artist: metadata.artist.isNotEmpty ? metadata.artist : song.artist, artist: metadata.artist.isNotEmpty ? metadata.artist : song.artist,
url: song.url, url: song.url,
artwork: metadata.artwork, // ✅ 已有 artwork: metadata.artwork,
); );
_queue[_currentIndex] = updatedSong; _queue[_currentIndex] = updatedSong;
_currentSong = updatedSong; _currentSong = updatedSong;
notifyListeners(); notifyListeners();
// ⭐ 传递完整的 updatedSong(包含 artwork // ⭐ 一次性推送完整数据(含 artwork
_onSongChanged?.call(updatedSong); _onSongChanged?.call(updatedSong);
} debugPrint(
'✅ [AudioService] metadata updated: ${updatedSong.title} - ${updatedSong.artist} (generation $generation)');
} catch (e) { } catch (e) {
debugPrint('⚠️ [AudioService] metadata load failed: $e'); debugPrint('⚠️ [AudioService] metadata load failed: $e, using fallback');
// 异常时推送 fallback(无 artwork
_onSongChanged?.call(song);
} }
} }
@@ -207,10 +247,8 @@ class AudioService extends ChangeNotifier {
debugPrint('🗑️ [AudioService] clearing cache for: ${song.title}'); debugPrint('🗑️ [AudioService] clearing cache for: ${song.title}');
// 1. 获取当前索引
final currentIndex = _currentIndex; final currentIndex = _currentIndex;
// 2. 尝试获取文件信息生成 song_key
String songKey; String songKey;
try { try {
final provider = MetadataService().getProviderForUrl(url); final provider = MetadataService().getProviderForUrl(url);
@@ -226,17 +264,14 @@ class AudioService extends ChangeNotifier {
songKey = url.hashCode.toString(); songKey = url.hashCode.toString();
} }
// 3. 清除 SQLite 记录
final db = SongDatabase(); final db = SongDatabase();
await db.deleteSong(songKey); await db.deleteSong(songKey);
await db.deleteCache(songKey); await db.deleteCache(songKey);
debugPrint('🗑️ [AudioService] SQLite records deleted: $songKey'); debugPrint('🗑️ [AudioService] SQLite records deleted: $songKey');
// 4. 删除封面图
await ArtworkHelper.deleteArtwork(songKey); await ArtworkHelper.deleteArtwork(songKey);
debugPrint('🗑️ [AudioService] artwork deleted'); debugPrint('🗑️ [AudioService] artwork deleted');
// 5. 删除 metadata 临时缓存文件
try { try {
final cacheDir = await getTemporaryDirectory(); final cacheDir = await getTemporaryDirectory();
final cachePath = '${cacheDir.path}/metadata_${url.hashCode}.tmp'; final cachePath = '${cacheDir.path}/metadata_${url.hashCode}.tmp';
@@ -249,14 +284,11 @@ class AudioService extends ChangeNotifier {
// 忽略 // 忽略
} }
// 6. 清除内存缓存
await MetadataService().clearCache(song.id); await MetadataService().clearCache(song.id);
debugPrint('🗑️ [AudioService] memory cache cleared'); debugPrint('🗑️ [AudioService] memory cache cleared');
// 7. 停止并重新播放
if (currentIndex >= 0 && currentIndex < _queue.length) { if (currentIndex >= 0 && currentIndex < _queue.length) {
stopPlay(); stopPlay();
// 确保重新播放同一首歌
_playCurrent(); _playCurrent();
debugPrint('🔄 [AudioService] song reloaded'); debugPrint('🔄 [AudioService] song reloaded');
} }
+29 -34
View File
@@ -9,16 +9,19 @@ class GlobalMiniPlayer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// ⭐ 使用 Selector 监听完整的 Song 对象(包括 artwork
final song = context.select<AudioService, Song?>((s) => s.currentSong); final song = context.select<AudioService, Song?>((s) => s.currentSong);
final isPlaying = context.select<AudioService, bool>((s) => s.isPlaying); final isPlaying = context.select<AudioService, bool>((s) => s.isPlaying);
final bottomPadding = MediaQuery.of(context).padding.bottom; final bottomPadding = MediaQuery.of(context).padding.bottom;
// 如果没有歌曲,不显示
if (song == null) {
return const SizedBox.shrink();
}
return Stack( return Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
// ⭐ 背景高度从 56 改为 60 // 背景条
Positioned( Positioned(
left: 0, left: 0,
right: 0, right: 0,
@@ -28,6 +31,7 @@ class GlobalMiniPlayer extends StatelessWidget {
color: const Color(0xFF1A1F1E), color: const Color(0xFF1A1F1E),
), ),
), ),
// 主内容
Positioned( Positioned(
left: 0, left: 0,
right: 0, right: 0,
@@ -37,58 +41,54 @@ class GlobalMiniPlayer extends StatelessWidget {
bottom: Radius.circular(16), bottom: Radius.circular(16),
), ),
child: Container( child: Container(
height: 60, // ⭐ 从 56 改为 60 height: 60,
color: const Color(0xFF1A1F1E), color: const Color(0xFF1A1F1E),
child: ClipRRect(
// ⭐ 修复涟漪圆角问题
borderRadius: BorderRadius.circular(12),
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (song != null) {
navigatorKey.currentState?.pushNamed('/player'); navigatorKey.currentState?.pushNamed('/player');
}
}, },
highlightColor: Colors.white.withOpacity(0.05), highlightColor: Colors.white.withOpacity(0.05),
splashColor: Colors.white.withOpacity(0.1), splashColor: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
child: Row( child: Row(
children: [ children: [
const SizedBox(width: 12), const SizedBox(width: 12),
// 封面图容器尺寸从 40 改为 44(适配 60px 高度) // 封面图
Container( Container(
width: 44, width: 44,
height: 44, height: 44,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
color: song != null color: const Color(0xFF2A3332),
? const Color(0xFF2A3332) image: song.artwork != null
: Colors.grey[800],
// ⭐ 如果 song.artwork 存在,显示封面图
image: song?.artwork != null
? DecorationImage( ? DecorationImage(
image: MemoryImage(song!.artwork!), image: MemoryImage(song.artwork!),
fit: BoxFit.cover, fit: BoxFit.cover,
) )
: null, : null,
), ),
child: song?.artwork == null child: song.artwork == null
? Icon( ? const Icon(
song != null Icons.music_note,
? Icons.music_note color: Colors.white38,
: Icons.music_off,
color: song != null
? Colors.white38
: Colors.grey[600],
size: 20, size: 20,
) )
: null, : null,
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
// 标题 + 艺术家
Expanded( Expanded(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
song != null ? song.title : '清听', song.title,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@@ -99,12 +99,10 @@ class GlobalMiniPlayer extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
Text( Text(
song != null ? song.artist : '未播放', song.artist,
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: song != null color: Colors.grey[400],
? Colors.grey[400]
: Colors.grey[600],
decoration: TextDecoration.none, decoration: TextDecoration.none,
), ),
maxLines: 1, maxLines: 1,
@@ -113,20 +111,18 @@ class GlobalMiniPlayer extends StatelessWidget {
], ],
), ),
), ),
// 播放/暂停按钮
IconButton( IconButton(
icon: Icon( icon: Icon(
song != null && isPlaying isPlaying ? Icons.pause : Icons.play_arrow,
? Icons.pause color: Colors.white,
: Icons.play_arrow,
color: song != null ? Colors.white : Colors.grey[600],
size: 24, size: 24,
), ),
onPressed: () { onPressed: () {
if (song != null) {
context.read<AudioService>().togglePlay(); context.read<AudioService>().togglePlay();
}
}, },
), ),
// 播放列表按钮
IconButton( IconButton(
icon: const Icon( icon: const Icon(
Icons.playlist_play_outlined, Icons.playlist_play_outlined,
@@ -134,9 +130,7 @@ class GlobalMiniPlayer extends StatelessWidget {
size: 24, size: 24,
), ),
onPressed: () { onPressed: () {
if (song != null) {
navigatorKey.currentState?.pushNamed('/playlist'); navigatorKey.currentState?.pushNamed('/playlist');
}
}, },
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
@@ -147,6 +141,7 @@ class GlobalMiniPlayer extends StatelessWidget {
), ),
), ),
), ),
),
], ],
); );
} }