diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index c8ac778..be1c3ae 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -4,6 +4,7 @@ import 'package:provider/provider.dart'; import '../services/audio_service.dart'; import '../services/favorite_events.dart'; import '../services/metadata_events.dart'; +import '../services/playlist_events.dart'; import '../services/webdav_service.dart'; import '../widgets/magnetic_scroll_physics.dart'; import 'webdav_setup_page.dart'; @@ -168,6 +169,7 @@ class _HomePageState extends BaseState { final PageController _homePageController = PageController(); StreamSubscription? _metaSub; StreamSubscription? _favSub; + StreamSubscription? _plSub; List _playlistSummaries = []; // ========== 滚动控制 ========== @@ -207,6 +209,7 @@ class _HomePageState extends BaseState { _scrollController = ScrollController()..addListener(_onScroll); _metaSub = MetadataEvents().stream.listen(_onMetadataUpdate); _favSub = FavoriteEvents().stream.listen((_) => _onFavoriteChanged()); + _plSub = PlaylistEvents().stream.listen((_) => _onPlaylistChanged()); _initWebDAV(); WidgetsBinding.instance.addPostFrameCallback((_) { @@ -218,6 +221,7 @@ class _HomePageState extends BaseState { void dispose() { _metaSub?.cancel(); _favSub?.cancel(); + _plSub?.cancel(); _homePageController.dispose(); _scrollController.dispose(); super.dispose(); @@ -249,6 +253,12 @@ class _HomePageState extends BaseState { } } + /// 歌单数据变化后刷新歌单页 + Future _onPlaylistChanged() async { + await _loadPlaylistSummaries(); + if (mounted) safeSetState(() {}); + } + /// 任何位置收藏/取消收藏后,重新拉一次收藏列表 Future _onFavoriteChanged() async { await _loadFavoritesFromDb(); diff --git a/lib/pages/playlist_detail_page.dart b/lib/pages/playlist_detail_page.dart index 3c7bf13..4b3c879 100644 --- a/lib/pages/playlist_detail_page.dart +++ b/lib/pages/playlist_detail_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; // lib/pages/playlist_detail_page.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -5,6 +6,7 @@ import 'package:provider/provider.dart'; import '../database/song_database.dart'; import '../repositories/playlist_repository.dart'; import '../services/audio_service.dart'; +import '../services/metadata_events.dart'; import '../widgets/song_artwork.dart'; /// 歌单详情:展示歌单内歌曲,支持播放全部 / 单曲播放 / 移除 @@ -41,14 +43,40 @@ class _PlaylistDetailPageState extends State { final SongDatabase _db = SongDatabase(); List<_SongRow> _songs = []; + StreamSubscription? _metaSub; bool _loading = true; @override void initState() { super.initState(); + _metaSub = MetadataEvents().stream.listen(_onMetadataUpdate); _load(); } + @override + void dispose() { + _metaSub?.cancel(); + super.dispose(); + } + + /// 歌单里的歌补全元数据后,就地更新对应行 + void _onMetadataUpdate(MetadataUpdate update) { + final index = _songs.indexWhere((item) => item.path == update.path); + if (index < 0 || !mounted) return; + final row = update.row; + final title = (row['title'] as String?)?.trim() ?? ''; + final artist = (row['artist'] as String?)?.trim() ?? ''; + final artwork = (row['artwork_path'] as String?)?.trim() ?? ''; + final old = _songs[index]; + setState(() { + _songs[index] = _SongRow( + path: old.path, + title: title.isNotEmpty ? title : old.title, + artist: artist.isNotEmpty ? artist : old.artist, + artworkPath: artwork.isNotEmpty ? artwork : old.artworkPath, + ); + }); + } Future _load() async { setState(() => _loading = true); try { diff --git a/lib/repositories/playlist_repository.dart b/lib/repositories/playlist_repository.dart index b7667a1..da21fe7 100644 --- a/lib/repositories/playlist_repository.dart +++ b/lib/repositories/playlist_repository.dart @@ -3,6 +3,7 @@ import 'dart:math'; import '../database/song_database.dart'; import '../models/playlist.dart'; import '../models/playlist_summary.dart'; +import '../services/playlist_events.dart'; import '../services/audio_service.dart'; class PlaylistRepository { @@ -35,6 +36,7 @@ class PlaylistRepository { Future createPlaylist(String name) async { final playlist = Playlist(id: _generateId(), name: name); await _db.insertPlaylist(playlist.toJson()); + PlaylistEvents().publish(); return playlist; } @@ -55,6 +57,7 @@ class PlaylistRepository { existing.add(id); added++; } + if (added > 0) PlaylistEvents().publish(); return added; } Future updatePlaylistName(String id, String newName) async { @@ -62,6 +65,7 @@ class PlaylistRepository { 'name': newName, 'updated_at': DateTime.now().millisecondsSinceEpoch, }); + PlaylistEvents().publish(); } // ⭐ 新增:更新播放模式 @@ -78,6 +82,7 @@ class PlaylistRepository { Future deletePlaylist(String id) async { await _db.deletePlaylist(id); + PlaylistEvents().publish(); } // ════════════════════════════════════════════════════════════ @@ -95,6 +100,7 @@ class PlaylistRepository { Future removeSong(String playlistId, String songId) async { await _db.removeSongFromPlaylist(playlistId, songId); + PlaylistEvents().publish(); } Future reorderSongs( diff --git a/lib/services/playlist_events.dart b/lib/services/playlist_events.dart new file mode 100644 index 0000000..2494c3a --- /dev/null +++ b/lib/services/playlist_events.dart @@ -0,0 +1,21 @@ +// lib/services/playlist_events.dart +import 'dart:async'; + +/// 歌单数据变更事件(新建/重命名/删除/加歌/删歌/排序)。 +/// +/// 主页的歌单页、歌单详情页都靠它实时刷新, +/// 不用等页面重建。 +class PlaylistEvents { + static final PlaylistEvents _instance = PlaylistEvents._internal(); + factory PlaylistEvents() => _instance; + PlaylistEvents._internal(); + + final StreamController _controller = + StreamController.broadcast(); + + Stream get stream => _controller.stream; + + void publish() { + _controller.add(null); + } +}