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]>
126 lines
4.3 KiB
Dart
126 lines
4.3 KiB
Dart
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,
|
||
),
|
||
);
|
||
}
|
||
}
|