feat(playlist): 歌单变更与歌单内元数据实时刷新
两个边界场景: 1) 新建/重命名/删除歌单、加入/移除歌曲后,主页歌单页不刷新 2) 歌单详情页里歌曲补全元数据后,标题/封面不更新 - 新增 PlaylistEvents(void 事件) - PlaylistRepository 的创建/重命名/删除/批量加入/移除后 publish - 主页订阅后重新拉取歌单摘要 - 歌单详情页订阅 MetadataEvents,按 path 就地更新行
This commit is contained in:
@@ -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<HomePage> {
|
||||
final PageController _homePageController = PageController();
|
||||
StreamSubscription<MetadataUpdate>? _metaSub;
|
||||
StreamSubscription<String>? _favSub;
|
||||
StreamSubscription<void>? _plSub;
|
||||
List<PlaylistSummary> _playlistSummaries = [];
|
||||
|
||||
// ========== 滚动控制 ==========
|
||||
@@ -207,6 +209,7 @@ class _HomePageState extends BaseState<HomePage> {
|
||||
_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<HomePage> {
|
||||
void dispose() {
|
||||
_metaSub?.cancel();
|
||||
_favSub?.cancel();
|
||||
_plSub?.cancel();
|
||||
_homePageController.dispose();
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
@@ -249,6 +253,12 @@ class _HomePageState extends BaseState<HomePage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 歌单数据变化后刷新歌单页
|
||||
Future<void> _onPlaylistChanged() async {
|
||||
await _loadPlaylistSummaries();
|
||||
if (mounted) safeSetState(() {});
|
||||
}
|
||||
|
||||
/// 任何位置收藏/取消收藏后,重新拉一次收藏列表
|
||||
Future<void> _onFavoriteChanged() async {
|
||||
await _loadFavoritesFromDb();
|
||||
|
||||
@@ -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<PlaylistDetailPage> {
|
||||
final SongDatabase _db = SongDatabase();
|
||||
|
||||
List<_SongRow> _songs = [];
|
||||
StreamSubscription<MetadataUpdate>? _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<void> _load() async {
|
||||
setState(() => _loading = true);
|
||||
try {
|
||||
|
||||
@@ -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<Playlist> 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<void> 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<void> deletePlaylist(String id) async {
|
||||
await _db.deletePlaylist(id);
|
||||
PlaylistEvents().publish();
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
@@ -95,6 +100,7 @@ class PlaylistRepository {
|
||||
|
||||
Future<void> removeSong(String playlistId, String songId) async {
|
||||
await _db.removeSongFromPlaylist(playlistId, songId);
|
||||
PlaylistEvents().publish();
|
||||
}
|
||||
|
||||
Future<void> reorderSongs(
|
||||
|
||||
@@ -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<void> _controller =
|
||||
StreamController<void>.broadcast();
|
||||
|
||||
Stream<void> get stream => _controller.stream;
|
||||
|
||||
void publish() {
|
||||
_controller.add(null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user