import 'package:audio_service/audio_service.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../library/category_service.dart'; import '../library/database.dart'; import '../library/playlist_service.dart'; import '../library/song_media.dart'; import '../player/audio_handler.dart'; import '../settings/app_settings.dart'; import '../shared/cover.dart'; import '../shared/lauf_balken.dart'; import '../shared/theme.dart'; /// Playlist-Detailansicht: Songs anzeigen, per Drag & Drop umsortieren, entfernen. class PlaylistDetailScreen extends StatelessWidget { const PlaylistDetailScreen({super.key, required this.playlist}); final Playlist playlist; @override Widget build(BuildContext context) { final db = context.read(); final service = context.read(); return Scaffold( appBar: AppBar(title: Text(playlist.name)), body: StreamBuilder>( stream: db.watchPlaylistSongs(playlist.id), builder: (context, snapshot) { if (snapshot.hasError) { return Center( child: Text('Fehler: ${snapshot.error}', style: const TextStyle(color: MeloTheme.text2)), ); } final songs = snapshot.data ?? const []; if (songs.isEmpty) { return const Center( child: Text('Playlist ist leer — füge Songs aus der Bibliothek hinzu', style: TextStyle(color: MeloTheme.text2)), ); } return ReorderableListView.builder( itemCount: songs.length, onReorderItem: (oldIndex, newIndex) { final reordered = List.from(songs); final moved = reordered.removeAt(oldIndex); reordered.insert(newIndex, moved); service.reorderAll(playlist.id, reordered.map((s) => s.id).toList()); }, itemBuilder: (context, i) => _Zeile( key: ValueKey(songs[i].id), song: songs[i], alleSongs: songs, index: i, onEntfernen: () => service.removeSongFromPlaylist(playlist.id, songs[i].id), ), ); }, ), ); } } /// Eine Zeile der Wiedergabeliste. /// /// Nicht [SongZeile]: hier steht rechts „Aus Playlist entfernen" statt Herz /// und Menü. Die Lauf-Markierung ist dieselbe — der Nutzer soll dasselbe /// Zeichen sehen wie in der Bibliothek. class _Zeile extends StatelessWidget { const _Zeile({ super.key, required this.song, required this.alleSongs, required this.index, required this.onEntfernen, }); final Song song; final List alleSongs; final int index; final VoidCallback onEntfernen; @override Widget build(BuildContext context) { final handler = context.read(); final categories = context.watch(); final settings = context.watch(); final cover = categories.coverFor(song, groupByCategory: settings.groupCoversByCategory); return StreamBuilder( stream: handler.mediaItem, initialData: handler.mediaItem.valueOrNull, builder: (context, laufSnap) { final markiert = istAmLaufen(laufSnap.data, song.id); return StreamBuilder( stream: markiert ? handler.playbackState : null, initialData: handler.playbackState.valueOrNull, builder: (context, zustandSnap) => ListTile( leading: LaufMarkierung( markiert: markiert, spielt: markiert && (zustandSnap.data?.playing ?? false), radius: 6, child: CoverImage( artUri: cover != null ? Uri.file(cover) : null, size: 48, radius: 6, ), ), title: Text( song.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: markiert ? const TextStyle( color: MeloTheme.red, fontWeight: FontWeight.w600) : null, ), subtitle: Text(song.artist ?? 'Unbekannt', maxLines: 1, overflow: TextOverflow.ellipsis), trailing: IconButton( tooltip: 'Aus Playlist entfernen', icon: const Icon(Icons.remove_circle_outline), onPressed: onEntfernen, ), onTap: () async { final messenger = ScaffoldMessenger.of(context); try { final uebersprungen = await playSongs( handler, alleSongs, index, coverOf: (s) => categories.coverFor(s, groupByCategory: settings.groupCoversByCategory), ); if (uebersprungen > 0) { messenger.showSnackBar( SnackBar(content: Text(offlineHinweis(uebersprungen))), ); } } catch (e) { messenger.showSnackBar( SnackBar(content: Text('Wiedergabe fehlgeschlagen: $e')), ); } }, ), ); }, ); } }