From 3a040b4661c83b0e9c1f3471ba03261a552692c6 Mon Sep 17 00:00:00 2001 From: "Hermes (Server)" Date: Tue, 18 Aug 2026 17:03:24 +0200 Subject: [PATCH] feat(ui): finales 5-Tab-Layout (Player/Bibliothek/Playlisten/Suche/Settings) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "Zuletzt hinzugefügt" aus dem alten HomeScreen in die Bibliothek verschoben: horizontal scrollende Leiste über der vollen Songliste, nur sichtbar wenn nicht leer und nicht gerade gescannt wird - home_screen.dart entfernt (Funktionalität jetzt Teil von LibraryScreen) - HomeShell-Tabs auf die finale Struktur umgestellt: NowPlayingScreen an Position 0, Playlisten- und Settings-Platzhalter an ihren finalen Positionen (werden in späteren Tasks durch echte Screens ersetzt) - BottomNavigationBar-Icons/Labels an die neue Reihenfolge angepasst - MiniPlayer wird ausgeblendet, solange der Now-Playing-Tab aktiv ist (redundant, da der Tab selbst schon die volle Wiedergabe-Ansicht zeigt) Co-Authored-By: Claude Haiku 4.5 --- lib/home_screen.dart | 76 ------------------------------- lib/library/library_screen.dart | 81 +++++++++++++++++++++++++++++++++ lib/main.dart | 19 ++++---- 3 files changed, 91 insertions(+), 85 deletions(-) delete mode 100644 lib/home_screen.dart diff --git a/lib/home_screen.dart b/lib/home_screen.dart deleted file mode 100644 index cf1e709..0000000 --- a/lib/home_screen.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import 'library/database.dart'; -import 'library/library_service.dart'; -import 'library/song_list.dart'; - -/// Startseite: zuletzt hinzugefügte Songs, sonst ein Willkommens-Hinweis. -class HomeScreen extends StatelessWidget { - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - final db = context.read(); - return Scaffold( - appBar: AppBar( - title: const Text('Melo', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 26)), - ), - body: StreamBuilder>( - stream: db.watchRecent(), - builder: (context, snapshot) { - if (snapshot.hasError) { - return Center( - child: Text('Fehler: ${snapshot.error}', - style: const TextStyle(color: Colors.white54)), - ); - } - final songs = snapshot.data ?? const []; - if (songs.isEmpty) return const _Welcome(); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Padding( - padding: EdgeInsets.fromLTRB(16, 8, 16, 8), - child: Text('Zuletzt hinzugefügt', - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.w600)), - ), - Expanded(child: SongList(songs)), - ], - ); - }, - ), - ); - } -} - -class _Welcome extends StatelessWidget { - const _Welcome(); - - @override - Widget build(BuildContext context) { - final lib = context.read(); - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.music_note, size: 72, color: Colors.white24), - const SizedBox(height: 12), - const Text('Willkommen bei Melo', - style: TextStyle(fontSize: 18)), - const SizedBox(height: 4), - const Text('Füge deine Musik hinzu, um loszulegen', - style: TextStyle(color: Colors.white54)), - const SizedBox(height: 20), - FilledButton.icon( - icon: const Icon(Icons.create_new_folder_outlined), - label: Text(addMusicLabel), - onPressed: lib.pickFolderAndScan, - ), - ], - ), - ); - } -} diff --git a/lib/library/library_screen.dart b/lib/library/library_screen.dart index 354e4cd..308378e 100644 --- a/lib/library/library_screen.dart +++ b/lib/library/library_screen.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../player/audio_handler.dart'; +import '../shared/cover.dart'; import 'database.dart'; import 'library_service.dart'; import 'permissions.dart'; import 'song_list.dart'; +import 'song_media.dart'; class LibraryScreen extends StatelessWidget { const LibraryScreen({super.key}); @@ -69,6 +72,14 @@ class LibraryScreen extends StatelessWidget { ], ), ), + StreamBuilder>( + stream: db.watchRecent(limit: 10), + builder: (context, snapshot) { + final recent = snapshot.data ?? const []; + if (recent.isEmpty || lib.scanning) return const SizedBox.shrink(); + return _RecentlyAdded(songs: recent); + }, + ), Expanded( child: StreamBuilder>( stream: db.watchSongs(), @@ -93,6 +104,76 @@ class LibraryScreen extends StatelessWidget { } } +/// Horizontal scrollende Leiste mit den zuletzt hinzugefügten Songs. +class _RecentlyAdded extends StatelessWidget { + const _RecentlyAdded({required this.songs}); + final List songs; + + @override + Widget build(BuildContext context) { + final handler = context.read(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.fromLTRB(16, 8, 16, 8), + child: Text('Zuletzt hinzugefügt', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)), + ), + SizedBox( + height: 140, + child: ListView.builder( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 16), + itemCount: songs.length, + itemBuilder: (context, i) { + final s = songs[i]; + return Padding( + padding: const EdgeInsets.only(right: 12), + child: SizedBox( + width: 96, + child: InkWell( + onTap: () async { + try { + await playSongs(handler, songs, i); + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Wiedergabe fehlgeschlagen: $e')), + ); + } + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CoverImage( + artUri: + s.coverPath != null ? Uri.file(s.coverPath!) : null, + size: 96, + radius: 8, + ), + const SizedBox(height: 4), + Text( + s.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ], + ); + } +} + class _Empty extends StatelessWidget { const _Empty(); diff --git a/lib/main.dart b/lib/main.dart index 2c16f5c..db5c8e4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,6 @@ import 'package:audio_service/audio_service.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'home_screen.dart'; import 'library/database.dart'; import 'library/library_screen.dart'; import 'library/library_service.dart'; @@ -10,6 +9,7 @@ import 'library/playlist_service.dart'; import 'library/search_screen.dart'; import 'player/audio_handler.dart'; import 'player/mini_player.dart'; +import 'player/now_playing_screen.dart'; import 'shared/theme.dart'; late final MeloAudioHandler _handler; @@ -66,11 +66,11 @@ class _HomeShellState extends State { int _index = 0; static const _tabs = [ - HomeScreen(), + NowPlayingScreen(), LibraryScreen(), + _Placeholder(label: 'Playlisten', hint: 'Kommt in Kürze'), SearchScreen(), - _Placeholder(label: 'Playlists', hint: 'Playlists & Favoriten (P3)'), - _Placeholder(label: 'Recaps', hint: 'Wöchentlich/Monatlich/Jährlich (P5)'), + _Placeholder(label: 'Settings', hint: 'Kommt in Kürze'), ]; @override @@ -79,20 +79,21 @@ class _HomeShellState extends State { body: Column( children: [ Expanded(child: IndexedStack(index: _index, children: _tabs)), - const MiniPlayer(), + if (_index != 0) const MiniPlayer(), ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: _index, onTap: (i) => setState(() => _index = i), items: const [ - BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), + BottomNavigationBarItem( + icon: Icon(Icons.play_circle_outline), label: 'Player'), BottomNavigationBarItem( icon: Icon(Icons.library_music), label: 'Bibliothek'), - BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Suche'), BottomNavigationBarItem( - icon: Icon(Icons.playlist_play), label: 'Playlists'), - BottomNavigationBarItem(icon: Icon(Icons.bar_chart), label: 'Recaps'), + icon: Icon(Icons.playlist_play), label: 'Playlisten'), + BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Suche'), + BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'), ], ), );