v2.49.1 — Now-Playing-Fullscreen (Cover, Controls, Like, Queue, Sleep-Timer)

## NowPlayingScreen (neu: lib/screens/now_playing_screen.dart)
- Blur-Hintergrund aus dem Cover (ImageFiltered + ImageFilter.blur, Bordmittel), Abdunklungs-Verlauf
- Großes Cover: Radius 20, Glow-Effekt (BoxShadow rot), Fallback auf Noten-Icon
- Titel (groß) + Künstler, Like-Button (Herz): togglet Favoriten via FavoritenService (lokal) mit SnackBar
- Fortschritt: Slider mit Seek (Drag-State), Position/Dauer-Text
- Controls: Shuffle, Repeat (3 Zustände), Prev, Play/Pause (roter Kreis), Next (Long-Press = Play-Next-Menü), Play-Next-Button
- Play-Next-Menü: kommende Queue-Songs, Tipp = spieleAlsNaechstes + SnackBar
- Sleep-Timer: Button + Restzeit ⏱ in der Kopfzeile, Sheet mit Chips Aus/15/30/45/60
- Queue-Zugriff: Button in der Kopfzeile → WarteschlangeSheet
- Slide-up/Slide-down via PageRouteBuilder (MiniPlayer)

## MiniPlayer
- Tipp öffnet jetzt den Fullscreen-Player (statt direkt der Queue)
- Queue nur noch via Button im Fullscreen
- Keine neuen Packages, flutter analyze 0 Issues, Tests 35/35
This commit is contained in:
Dustin
2026-08-04 16:37:13 +02:00
parent 4226612065
commit 19b115b000
2 changed files with 863 additions and 9 deletions
+24 -9
View File
@@ -5,7 +5,7 @@ import 'package:just_audio/just_audio.dart';
import '../services/player_service.dart';
import '../models/song.dart';
import '../utils/farb_theme.dart';
import 'warteschlange_sheet.dart';
import '../screens/now_playing_screen.dart';
class MiniPlayer extends StatefulWidget {
const MiniPlayer({super.key});
@@ -52,13 +52,28 @@ class _MiniPlayerState extends State<MiniPlayer> {
if (mounted) setState(() {});
}
/// Öffnet die Warteschlange als Bottom-Sheet
void _zeigeWarteschlange() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: MeloTheme.dunkel1,
builder: (_) => const WarteschlangeSheet(),
/// Öffnet den Fullscreen-Player mit Slide-up/Slide-down-Animation.
/// Die Warteschlange ist von dort per Button erreichbar.
void _oeffneNowPlaying() {
Navigator.of(context).push(
PageRouteBuilder(
transitionDuration: const Duration(milliseconds: 350),
reverseTransitionDuration: const Duration(milliseconds: 280),
pageBuilder: (_, __, ___) => const NowPlayingScreen(),
transitionsBuilder: (_, animation, __, child) {
final offset = Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
),
);
return SlideTransition(position: offset, child: child);
},
),
);
}
@@ -81,7 +96,7 @@ class _MiniPlayerState extends State<MiniPlayer> {
final hatCover = song.coverPfad != null && File(song.coverPfad!).existsSync();
return GestureDetector(
onTap: _zeigeWarteschlange,
onTap: _oeffneNowPlaying,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(