UI-Politur: Kontrast, Abstaende, Typografie, Bewegung

Kein Redesign — Schwarz/Rot, 4 Tabs und Lieder/Kategorie bleiben. Grundlage
sind die Design-Skills: ui-ux-pro-max (Barrierefreiheit, Touch, Typografie,
Bewegung), ui-design/mobile-android (Material 3) und die Token-Disziplin
aus Hue.

Behobene Maengel (messbar, nicht Geschmack):
- Text in Colors.white38 an 17 Stellen = 3,4:1 Kontrast, WCAG verlangt
  4,5:1. Ersetzt durch drei benannte Stufen (text1 15,9:1 / text2 8,8:1 /
  text3 4,9:1). theme_kontrast_test.dart rechnet die Verhaeltnisse bei
  jedem Lauf nach, statt sie zu behaupten.
- SubTabs waren ~38 dp hoch (Material 3 verlangt 48), ohne Ripple und ohne
  Uebergang. Jetzt 48 dp, InkWell, AnimatedContainer, und die Auswahl wird
  Vorlesehilfen als Zustand gemeldet (Semantics.selected).
- mini_player.dart: Expanded UM eine feste Hoehe herum — zwei
  widerspruechliche Angaben. Entschaerft.
- Emoji in Bedienelementen der Einstellungen (jeweils neben einem echten
  Icon) entfernt.

Neu in shared/theme.dart: MeloSpace (8er-Raster), MeloRadius, MeloMotion,
Farbrollen text1/2/3 + border + hairline, minTouchTarget, vollstaendiges
textTheme (7 Stufen) und Themes fuer Listen, Sheets, Snackbars,
Fortschritt, Trenner.

318 Tests gruen (23 neue, 1 uebersprungen), flutter analyze ohne Befund.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpPu4nuKjKKeX1RpdDeX81
This commit is contained in:
Hermes (Server)
2026-08-21 12:40:25 +02:00
co-authored by Claude Opus 5
parent 2df85e650e
commit bc861f6087
23 changed files with 773 additions and 184 deletions
+16 -10
View File
@@ -78,7 +78,7 @@ class _MyMusicScreenState extends State<MyMusicScreen> {
Text(
'Scanne … ${lib.scanDone}'
'${lib.scanTotal > 0 ? ' / ${lib.scanTotal}' : ''}',
style: const TextStyle(color: Colors.white54, fontSize: 12),
style: const TextStyle(color: MeloTheme.text2, fontSize: 12),
),
],
),
@@ -99,7 +99,7 @@ class _MyMusicScreenState extends State<MyMusicScreen> {
if (snapshot.hasError) {
return Center(
child: Text('Fehler: ${snapshot.error}',
style: const TextStyle(color: Colors.white54)),
style: const TextStyle(color: MeloTheme.text2)),
);
}
final songs = snapshot.data ?? const <Song>[];
@@ -150,11 +150,11 @@ class _Header extends StatelessWidget {
child: const Row(
children: [
SizedBox(width: 14),
Icon(Icons.search, color: Colors.white54, size: 20),
Icon(Icons.search, color: MeloTheme.text2, size: 20),
SizedBox(width: 10),
Text(
'Titel, Künstler und Alben suchen',
style: TextStyle(color: Colors.white54, fontSize: 14),
style: TextStyle(color: MeloTheme.text2, fontSize: 14),
),
],
),
@@ -252,13 +252,19 @@ class _QuickCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
padding: const EdgeInsets.symmetric(
horizontal: MeloSpace.xs, vertical: MeloSpace.sm),
child: SizedBox(
width: 150,
child: Material(
color: MeloTheme.surfaceHigh,
borderRadius: BorderRadius.circular(12),
clipBehavior: Clip.antiAlias,
// Umriss statt Schatten — siehe MeloTheme.hairline.
// shape statt borderRadius: Material verträgt nur eines von beiden.
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(MeloRadius.card),
side: const BorderSide(color: MeloTheme.border),
),
child: InkWell(
onTap: onTap,
child: Stack(
@@ -274,7 +280,7 @@ class _QuickCard extends StatelessWidget {
),
),
Padding(
padding: const EdgeInsets.all(10),
padding: const EdgeInsets.all(MeloSpace.sm + MeloSpace.xs),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -294,7 +300,7 @@ class _QuickCard extends StatelessWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white54, fontSize: 11)),
color: MeloTheme.text2, fontSize: 11)),
],
),
],
@@ -323,7 +329,7 @@ class _Empty extends StatelessWidget {
const Icon(Icons.library_music, size: 64, color: Colors.white24),
const SizedBox(height: 12),
const Text('Noch keine Musik',
style: TextStyle(color: Colors.white54)),
style: TextStyle(color: MeloTheme.text2)),
const SizedBox(height: 16),
FilledButton.icon(
icon: const Icon(Icons.create_new_folder_outlined),
@@ -352,7 +358,7 @@ class RecentlyAddedScreen extends StatelessWidget {
if (songs.isEmpty) {
return const Center(
child: Text('Noch nichts hinzugefügt',
style: TextStyle(color: Colors.white54)),
style: TextStyle(color: MeloTheme.text2)),
);
}
return SongList(songs);