From fc9a6abdd7ca8143fd8824acd0f95c9b1a355f21 Mon Sep 17 00:00:00 2001 From: "Hermes (Server)" Date: Tue, 18 Aug 2026 17:19:29 +0200 Subject: [PATCH] =?UTF-8?q?feat(playlists):=20Songs=20per=20Bottom-Sheet?= =?UTF-8?q?=20zu=20Playlisten=20hinzuf=C3=BCgen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ohne Eintrittspunkt wären Playlisten unbenutzbar, da sie sonst nie Songs erhalten könnten. SongList bekommt einen zusätzlichen Trailing-Button (Icons.playlist_add) neben dem Favoriten-Herz, der ein Bottom-Sheet mit allen Playlisten öffnet ("Neue Playlist" oben, dann bestehende). Antippen hängt den Song ans Ende der gewählten Playlist an und zeigt eine SnackBar. Co-Authored-By: Claude Haiku 4.5 --- lib/library/song_list.dart | 71 +++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/lib/library/song_list.dart b/lib/library/song_list.dart index af63727..60ab526 100644 --- a/lib/library/song_list.dart +++ b/lib/library/song_list.dart @@ -2,9 +2,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../player/audio_handler.dart'; +import '../playlists/create_playlist_dialog.dart'; import '../shared/cover.dart'; import '../shared/favorite_button.dart'; import 'database.dart'; +import 'playlist_service.dart'; import 'song_media.dart'; /// Scrollbare Songliste; Tippen spielt die ganze Liste ab dem Song ab. @@ -12,6 +14,63 @@ class SongList extends StatelessWidget { const SongList(this.songs, {super.key}); final List songs; + /// Öffnet ein Bottom-Sheet mit allen Playlisten, um [song] hinzuzufügen. + Future _showAddToPlaylist(BuildContext context, Song song) async { + final db = context.read(); + final service = context.read(); + await showModalBottomSheet( + context: context, + builder: (sheetContext) => SafeArea( + child: StreamBuilder>( + stream: db.watchPlaylists(), + builder: (context, snapshot) { + final playlists = snapshot.data ?? const []; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: const Icon(Icons.add), + title: const Text('Neue Playlist'), + onTap: () async { + final newId = await showDialog( + context: sheetContext, + builder: (_) => const CreatePlaylistDialog(), + ); + if (newId == null) return; + final position = (await db.watchPlaylistSongs(newId).first).length; + await service.addSongToPlaylist(newId, song.id, position); + if (sheetContext.mounted) { + Navigator.pop(sheetContext); + ScaffoldMessenger.of(sheetContext).showSnackBar( + const SnackBar(content: Text('Zu Playlist hinzugefügt')), + ); + } + }, + ), + for (final playlist in playlists) + ListTile( + leading: const Icon(Icons.playlist_play), + title: Text(playlist.name), + onTap: () async { + final position = + (await db.watchPlaylistSongs(playlist.id).first).length; + await service.addSongToPlaylist(playlist.id, song.id, position); + if (sheetContext.mounted) { + Navigator.pop(sheetContext); + ScaffoldMessenger.of(sheetContext).showSnackBar( + SnackBar(content: Text('Zu ${playlist.name} hinzugefügt')), + ); + } + }, + ), + ], + ); + }, + ), + ), + ); + } + @override Widget build(BuildContext context) { final handler = context.read(); @@ -28,7 +87,17 @@ class SongList extends StatelessWidget { title: Text(s.title, maxLines: 1, overflow: TextOverflow.ellipsis), subtitle: Text(s.artist ?? 'Unbekannt', maxLines: 1, overflow: TextOverflow.ellipsis), - trailing: FavoriteButton(songId: s.id), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + FavoriteButton(songId: s.id), + IconButton( + tooltip: 'Zu Playlist hinzufügen', + icon: const Icon(Icons.playlist_add), + onPressed: () => _showAddToPlaylist(context, s), + ), + ], + ), onTap: () async { try { await playSongs(handler, songs, i);