feat: UI-Umbau - weiches Dunkel, Schriftgroessen-Regler, Sprechblasen umschaltbar, Einstellungen als Vollbild
Bernds Entscheidungen 2026-10-07: - theme.dart: ThemeVariant (Dunkel = neues weiches Dunkelgrau als Standard, Schwarz = bisheriges Fast-Schwarz, Hell) mit PyramidPalette; Nebentexte kontrastreicher. Alte Einstellung theme_dark wird uebernommen (dunkel -> weiches Dunkel). Fest verdrahtete Dunkelfarben im Profil-Popup und GIF-Picker folgen jetzt dem Farbmodus. - appearance.dart: Schriftgroesse = Pyramid-Regler x Systemeinstellung (Handy-Standard 115 %, PC 100 %), MessageStyle Automatisch/Sprechblasen/ Liste, gemeinsame isMobileLayout-Regel. - message_bubbles.dart: Sprechblasen-Darstellung (eigene rechts in Akzentfarbe, Gruppen mit Name + Avatar, Bilder ohne Blase in voller Groesse); Listen-Darstellung unveraendert, Weiche in MessageGroup. - Formatierte Nachrichten (u. a. Antworten) nutzten RichText ohne Schriftskalierung -> erschienen kleiner; jetzt Text.rich. - Einstellungen auf dem Handy als ganze Seite, Schalter unter dem Text. - UI-Durchlauf um Liste/Hell/Gruppe/Einstellungen erweitert; Tests fuer Theme-Varianten, Uebernahme alter Werte, Schriftfaktor, Darstellung. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a1d64ec813
commit
c5f10901e6
+123
-21
@@ -1,6 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Pyramid design tokens — matching mockup CSS variables exactly
|
||||
/// Farbschema der App (Einstellungen → Erscheinungsbild → Farbmodus).
|
||||
enum ThemeVariant {
|
||||
/// „Weiches Dunkel“ – Dunkelgrau statt Fast-Schwarz, mehr Kontrast bei
|
||||
/// Text. Standard seit 2026-10-07 (Bernds Entscheidung).
|
||||
soft,
|
||||
|
||||
/// Das bisherige Fast-Schwarz (spart auf OLED-Displays etwas Akku).
|
||||
black,
|
||||
|
||||
/// Hell.
|
||||
light;
|
||||
|
||||
bool get isDark => this != ThemeVariant.light;
|
||||
|
||||
String get label => switch (this) {
|
||||
ThemeVariant.soft => 'Dunkel',
|
||||
ThemeVariant.black => 'Schwarz',
|
||||
ThemeVariant.light => 'Hell',
|
||||
};
|
||||
|
||||
static ThemeVariant fromName(String? name) =>
|
||||
ThemeVariant.values.firstWhere((v) => v.name == name,
|
||||
orElse: () => ThemeVariant.soft);
|
||||
}
|
||||
|
||||
/// Die Flächen- und Textfarben eines [ThemeVariant].
|
||||
class PyramidPalette {
|
||||
const PyramidPalette({
|
||||
required this.bg0,
|
||||
required this.bg1,
|
||||
required this.bg2,
|
||||
required this.bg3,
|
||||
required this.bgHover,
|
||||
required this.bgActive,
|
||||
required this.border,
|
||||
required this.borderStrong,
|
||||
required this.fg,
|
||||
required this.fgMuted,
|
||||
required this.fgDim,
|
||||
});
|
||||
|
||||
final Color bg0, bg1, bg2, bg3, bgHover, bgActive, border, borderStrong;
|
||||
final Color fg, fgMuted, fgDim;
|
||||
|
||||
/// Weiches Dunkel: alle Flächen ~10 % heller als „Schwarz“, Nebentexte
|
||||
/// deutlich kontrastreicher (fgDim auf bg1 ≈ 5:1 statt ≈ 3,8:1).
|
||||
static const soft = PyramidPalette(
|
||||
bg0: Color(0xFF15161A),
|
||||
bg1: Color(0xFF1B1C21),
|
||||
bg2: Color(0xFF212228),
|
||||
bg3: Color(0xFF282A31),
|
||||
bgHover: Color(0xFF2F3139),
|
||||
bgActive: Color(0xFF373945),
|
||||
border: Color(0xFF2C2E36),
|
||||
borderStrong: Color(0xFF3A3C46),
|
||||
fg: Color(0xFFF2F2F5),
|
||||
fgMuted: Color(0xFFB7B7C2),
|
||||
fgDim: Color(0xFF8B8B99),
|
||||
);
|
||||
|
||||
static const black = PyramidPalette(
|
||||
bg0: PyramidColors.bg0,
|
||||
bg1: PyramidColors.bg1,
|
||||
bg2: PyramidColors.bg2,
|
||||
bg3: PyramidColors.bg3,
|
||||
bgHover: PyramidColors.bgHover,
|
||||
bgActive: PyramidColors.bgActive,
|
||||
border: PyramidColors.border,
|
||||
borderStrong: PyramidColors.borderStrong,
|
||||
fg: PyramidColors.fg,
|
||||
fgMuted: PyramidColors.fgMuted,
|
||||
fgDim: PyramidColors.fgDim,
|
||||
);
|
||||
|
||||
static const light = PyramidPalette(
|
||||
bg0: PyramidColors.bg0Light,
|
||||
bg1: PyramidColors.bg1Light,
|
||||
bg2: PyramidColors.bg2Light,
|
||||
bg3: PyramidColors.bg3Light,
|
||||
bgHover: PyramidColors.bgHoverLight,
|
||||
bgActive: PyramidColors.bgActiveLight,
|
||||
border: PyramidColors.borderLight,
|
||||
borderStrong: PyramidColors.borderStrongLight,
|
||||
fg: PyramidColors.fgLight,
|
||||
fgMuted: PyramidColors.fgMutedLight,
|
||||
fgDim: PyramidColors.fgDimLight,
|
||||
);
|
||||
|
||||
static PyramidPalette of(ThemeVariant variant) => switch (variant) {
|
||||
ThemeVariant.soft => soft,
|
||||
ThemeVariant.black => black,
|
||||
ThemeVariant.light => light,
|
||||
};
|
||||
}
|
||||
|
||||
// Pyramid design tokens — matching mockup CSS variables exactly.
|
||||
// Die dunklen Werte hier sind die Variante „Schwarz“; Widgets sollten die
|
||||
// Farben über PyramidTheme.of(context) holen (folgt dem gewählten Schema).
|
||||
class PyramidColors {
|
||||
const PyramidColors._();
|
||||
|
||||
@@ -49,7 +146,7 @@ class PyramidColors {
|
||||
}
|
||||
|
||||
class PyramidTheme extends InheritedWidget {
|
||||
final bool isDark;
|
||||
final ThemeVariant variant;
|
||||
final Color accent;
|
||||
final double radius;
|
||||
final double density;
|
||||
@@ -58,7 +155,7 @@ class PyramidTheme extends InheritedWidget {
|
||||
const PyramidTheme({
|
||||
super.key,
|
||||
required super.child,
|
||||
this.isDark = true,
|
||||
this.variant = ThemeVariant.soft,
|
||||
this.accent = PyramidColors.accentAmber,
|
||||
this.radius = 12,
|
||||
this.density = 1,
|
||||
@@ -69,17 +166,20 @@ class PyramidTheme extends InheritedWidget {
|
||||
return context.dependOnInheritedWidgetOfExactType<PyramidTheme>()!;
|
||||
}
|
||||
|
||||
Color get bg0 => isDark ? PyramidColors.bg0 : PyramidColors.bg0Light;
|
||||
Color get bg1 => isDark ? PyramidColors.bg1 : PyramidColors.bg1Light;
|
||||
Color get bg2 => isDark ? PyramidColors.bg2 : PyramidColors.bg2Light;
|
||||
Color get bg3 => isDark ? PyramidColors.bg3 : PyramidColors.bg3Light;
|
||||
Color get bgHover => isDark ? PyramidColors.bgHover : PyramidColors.bgHoverLight;
|
||||
Color get bgActive => isDark ? PyramidColors.bgActive : PyramidColors.bgActiveLight;
|
||||
Color get border => isDark ? PyramidColors.border : PyramidColors.borderLight;
|
||||
Color get borderStrong => isDark ? PyramidColors.borderStrong : PyramidColors.borderStrongLight;
|
||||
Color get fg => isDark ? PyramidColors.fg : PyramidColors.fgLight;
|
||||
Color get fgMuted => isDark ? PyramidColors.fgMuted : PyramidColors.fgMutedLight;
|
||||
Color get fgDim => isDark ? PyramidColors.fgDim : PyramidColors.fgDimLight;
|
||||
bool get isDark => variant.isDark;
|
||||
PyramidPalette get palette => PyramidPalette.of(variant);
|
||||
|
||||
Color get bg0 => palette.bg0;
|
||||
Color get bg1 => palette.bg1;
|
||||
Color get bg2 => palette.bg2;
|
||||
Color get bg3 => palette.bg3;
|
||||
Color get bgHover => palette.bgHover;
|
||||
Color get bgActive => palette.bgActive;
|
||||
Color get border => palette.border;
|
||||
Color get borderStrong => palette.borderStrong;
|
||||
Color get fg => palette.fg;
|
||||
Color get fgMuted => palette.fgMuted;
|
||||
Color get fgDim => palette.fgDim;
|
||||
Color get danger => PyramidColors.danger;
|
||||
Color get online => PyramidColors.online;
|
||||
Color get away => PyramidColors.away;
|
||||
@@ -104,7 +204,7 @@ class PyramidTheme extends InheritedWidget {
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(PyramidTheme oldWidget) =>
|
||||
isDark != oldWidget.isDark ||
|
||||
variant != oldWidget.variant ||
|
||||
accent != oldWidget.accent ||
|
||||
radius != oldWidget.radius ||
|
||||
density != oldWidget.density ||
|
||||
@@ -114,9 +214,11 @@ class PyramidTheme extends InheritedWidget {
|
||||
// Inter ist als Asset gebündelt (pubspec `fonts:`) — kein Laufzeit-Download.
|
||||
TextStyle _base(Color color) => TextStyle(fontFamily: 'Inter', color: color);
|
||||
|
||||
ThemeData buildPyramidThemeData(bool isDark) {
|
||||
final fg = isDark ? PyramidColors.fg : PyramidColors.fgLight;
|
||||
final bg1 = isDark ? PyramidColors.bg1 : PyramidColors.bg1Light;
|
||||
ThemeData buildPyramidThemeData(ThemeVariant variant) {
|
||||
final palette = PyramidPalette.of(variant);
|
||||
final isDark = variant.isDark;
|
||||
final fg = palette.fg;
|
||||
final bg1 = palette.bg1;
|
||||
final accent = PyramidColors.accentAmber;
|
||||
|
||||
return ThemeData(
|
||||
@@ -137,11 +239,11 @@ ThemeData buildPyramidThemeData(bool isDark) {
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
bodyMedium: _base(fg).copyWith(fontSize: 14),
|
||||
bodySmall: _base(fg).copyWith(fontSize: 12, color: isDark ? PyramidColors.fgMuted : PyramidColors.fgMutedLight),
|
||||
labelSmall: _base(fg).copyWith(fontSize: 11, color: isDark ? PyramidColors.fgDim : PyramidColors.fgDimLight),
|
||||
bodySmall: _base(fg).copyWith(fontSize: 12, color: palette.fgMuted),
|
||||
labelSmall: _base(fg).copyWith(fontSize: 11, color: palette.fgDim),
|
||||
),
|
||||
dividerTheme: DividerThemeData(
|
||||
color: isDark ? PyramidColors.border : PyramidColors.borderLight,
|
||||
color: palette.border,
|
||||
thickness: 1,
|
||||
space: 1,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user