v2.56 — UI/Design-Rework: Melo Dark Fusion (neue Startseite, 2-stufige Einstellungen, Design-System)
- Design-System: MeloDesignTokens ThemeExtension, Outfit-Font, Farben #0B0B10/#14141C/#1C1C26 - Akzentfarbe-User-Einstellung behalten (MeloTheme.akzentNotifier), Gradient aus Akzent - Startseite: Hero-Weiterhören-Karte mit Cover, Titel, Glow-Play-Button - 2-stufige Einstellungen: Standard + Expertenmodus (SharedPreferences Key 'experten_modus') - Komponenten: Pill-Buttons (radius 999), Card-Radius 20, Glow-Shadow, PressScale-Widget - Login: Gradient-Button mit Akzent-Glow - 199 Tests grün (194 bestehend + 5 neu): ThemeExtension, Expertenmodus, Akzent, PressScale, Hero - flutter analyze lib/ + test/ = 0 Issues - KEIN APK
This commit is contained in:
@@ -40,11 +40,9 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
||||
});
|
||||
}
|
||||
});
|
||||
// Sofortige UI-Aktualisierung bei jedem Songwechsel
|
||||
_songSub = _player.onSongWechsel.listen((_) {
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
// Shuffle-/Repeat-/Sleep-Zustand live halten
|
||||
_player.addListener(_onPlayerChanged);
|
||||
}
|
||||
|
||||
@@ -52,8 +50,6 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
/// Ö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(
|
||||
@@ -94,15 +90,23 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
||||
final progress = _dauer.inSeconds > 0
|
||||
? _position.inSeconds / _dauer.inSeconds : 0.0;
|
||||
final hatCover = song.coverPfad != null && File(song.coverPfad!).existsSync();
|
||||
final akzent = MeloTheme.akzent;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: _oeffneNowPlaying,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A0A0A),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: const Color(0xFF2A1515)),
|
||||
color: MeloTheme.dunkel1,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: MeloTheme.dunkel2),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: akzent.withValues(alpha: 0.15),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -113,13 +117,17 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
||||
children: [
|
||||
// Cover mit Fallback auf Notensymbol
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
width: 36, height: 36,
|
||||
color: MeloTheme.rot,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [akzent, Color.lerp(akzent, Colors.black, 0.5)!],
|
||||
),
|
||||
),
|
||||
child: hatCover
|
||||
? Image.file(File(song.coverPfad!), fit: BoxFit.cover)
|
||||
: const Center(child: Text('♪', style: TextStyle(fontSize: 16))),
|
||||
: const Center(child: Text('♪', style: TextStyle(fontSize: 16, color: Colors.white))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
@@ -151,8 +159,8 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: LinearProgressIndicator(
|
||||
value: progress,
|
||||
backgroundColor: const Color(0xFF2A2A2A),
|
||||
valueColor: const AlwaysStoppedAnimation(MeloTheme.rot),
|
||||
backgroundColor: MeloTheme.dunkel2,
|
||||
valueColor: AlwaysStoppedAnimation(akzent),
|
||||
minHeight: 2,
|
||||
),
|
||||
),
|
||||
@@ -163,23 +171,21 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Shuffle-Icon — rot wenn aktiv
|
||||
Widget _shuffleBtn() {
|
||||
final aktiv = _player.zufallsmodus;
|
||||
return _btn(
|
||||
Icons.shuffle,
|
||||
() => _player.setZufallsmodus(!aktiv),
|
||||
farbe: aktiv ? MeloTheme.rot : Colors.white,
|
||||
farbe: aktiv ? MeloTheme.akzent : Colors.white,
|
||||
);
|
||||
}
|
||||
|
||||
/// Repeat-Icon — 3 Zustände (aus → Titel → Playlist), Wechsel bei Tipp
|
||||
Widget _repeatBtn() {
|
||||
final modus = _player.wiederholmodus;
|
||||
final (icon, farbe) = switch (modus) {
|
||||
Wiederholmodus.aus => (Icons.repeat, Colors.white),
|
||||
Wiederholmodus.titel => (Icons.repeat_one, MeloTheme.rot),
|
||||
Wiederholmodus.playlist => (Icons.repeat, MeloTheme.rot),
|
||||
Wiederholmodus.titel => (Icons.repeat_one, MeloTheme.akzent),
|
||||
Wiederholmodus.playlist => (Icons.repeat, MeloTheme.akzent),
|
||||
};
|
||||
return _btn(icon, _naechsterWiederholmodus, farbe: farbe);
|
||||
}
|
||||
@@ -210,7 +216,7 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
||||
|
||||
Widget _playBtn() {
|
||||
return Material(
|
||||
color: MeloTheme.rot,
|
||||
color: MeloTheme.akzent,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
|
||||
Reference in New Issue
Block a user