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/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 createState() => _PyramidAppState(); } class _PyramidAppState extends ConsumerState 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 _loadPersistedPrefs() async { final saved = await loadThemePrefs(); if (!mounted) return; ref.read(themeModeProvider.notifier).state = saved.isDark; 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 isDark = ref.watch(themeModeProvider); 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( isDark: isDark, accent: accent, radius: radius, density: density, motion: motion, child: MaterialApp.router( title: 'Pyramid', theme: buildPyramidThemeData(false), darkTheme: buildPyramidThemeData(true), themeMode: isDark ? ThemeMode.dark : ThemeMode.light, routerConfig: router, localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: const [Locale('de'), Locale('en')], debugShowCheckedModeBanner: false, ), ); } }