feat(ui): finales 5-Tab-Layout (Player/Bibliothek/Playlisten/Suche/Settings)
- "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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
be1248d7ba
commit
3a040b4661
@@ -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<MeloDb>();
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Melo',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 26)),
|
||||
),
|
||||
body: StreamBuilder<List<Song>>(
|
||||
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<LibraryService>();
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<List<Song>>(
|
||||
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<List<Song>>(
|
||||
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<Song> songs;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final handler = context.read<MeloAudioHandler>();
|
||||
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();
|
||||
|
||||
|
||||
+10
-9
@@ -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<HomeShell> {
|
||||
int _index = 0;
|
||||
|
||||
static const _tabs = <Widget>[
|
||||
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<HomeShell> {
|
||||
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'),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user