Files
pyramid/lib/core/app.dart
T
Bernd SteckmeisterandClaude Opus 5.5 c5f10901e6 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]>
2026-10-07 17:16:07 +02:00

126 lines
4.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pyramid/core/app_state.dart';
import 'package:pyramid/core/appearance.dart';
import 'package:pyramid/core/notification_service.dart' show checkPendingReply, setAppForeground;
import 'package:pyramid/core/router.dart';
import 'package:pyramid/core/settings_prefs.dart';
import 'package:pyramid/core/theme.dart';
import 'package:pyramid/features/auth/login_notifier.dart';
import 'package:pyramid/widgets/pyramid_loader.dart';
class PyramidApp extends ConsumerStatefulWidget {
const PyramidApp({super.key});
@override
ConsumerState<PyramidApp> createState() => _PyramidAppState();
}
class _PyramidAppState extends ConsumerState<PyramidApp>
with WidgetsBindingObserver {
bool _prefsLoaded = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_loadPersistedPrefs();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
setAppForeground(true);
checkPendingReply(ref);
case AppLifecycleState.paused:
case AppLifecycleState.detached:
// Clears heartbeat so PushService.kt shows FCM notifications immediately.
// Background Matrix syncs must not re-set the heartbeat (see notification_service.dart).
setAppForeground(false);
default:
break;
}
}
Future<void> _loadPersistedPrefs() async {
final saved = await loadThemePrefs();
if (!mounted) return;
ref.read(themeVariantProvider.notifier).state = saved.variant;
ref.read(textScaleProvider.notifier).state = saved.textScale;
ref.read(messageStyleProvider.notifier).state = saved.messageStyle;
ref.read(accentProvider.notifier).state = saved.accentIdx;
ref.read(radiusProvider.notifier).state = saved.radius;
ref.read(motionProvider.notifier).state = saved.motion;
setState(() => _prefsLoaded = true);
}
@override
Widget build(BuildContext context) {
// Block the router from rendering until auth state is known so the
// /server page never flashes before the redirect to /app fires.
final authAsync = ref.watch(isLoggedInProvider);
if (authAsync.isLoading || !_prefsLoaded) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Color(0xFF1A1B1E),
body: Center(child: PyramidLoader(size: 80)),
),
);
}
final router = ref.watch(routerProvider);
final variant = ref.watch(themeVariantProvider);
final textScale = ref.watch(textScaleProvider);
final accentIdx = ref.watch(accentProvider);
final radius = ref.watch(radiusProvider);
final density = ref.watch(densityProvider);
final motion = ref.watch(motionProvider);
final accent = accentPresets[accentIdx].color;
return PyramidTheme(
variant: variant,
accent: accent,
radius: radius,
density: density,
motion: motion,
child: MaterialApp.router(
title: 'Pyramid',
theme: buildPyramidThemeData(ThemeVariant.light),
darkTheme: buildPyramidThemeData(
variant.isDark ? variant : ThemeVariant.soft),
themeMode: variant.isDark ? ThemeMode.dark : ThemeMode.light,
routerConfig: router,
// Schriftgröße: Pyramid-Regler × Handy-/Systemeinstellung, für alle
// Seiten und Dialoge (liegt über dem Navigator).
builder: (context, child) {
final mq = MediaQuery.of(context);
final factor = textScale ??
defaultTextScale(mobile: isMobileLayout(mq.size));
return MediaQuery(
data: mq.copyWith(
textScaler: PyramidTextScaler(mq.textScaler, factor),
),
child: child!,
);
},
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('de'), Locale('en')],
debugShowCheckedModeBanner: false,
),
);
}
}