import 'package:auth/auth.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:legacy_theme/legacy_theme.dart'; import 'package:sf_app_platform/core/app_version_check/app_update_gate.dart'; import 'package:sf_app_platform/core/app_version_check/app_version_check.dart'; import 'package:sf_app_platform/core/config/app_mode.dart'; import 'package:sf_app_platform/navigation/app_router.dart'; import 'package:navigation/navigation.dart'; import 'package:sf_app_platform/providers/app_state_provider.dart'; import 'package:sf_app_platform/providers/permissions/permissions_provider.dart'; import 'package:sf_app_platform/providers/legacy_heartbeat_service.dart'; import 'package:sf_app_platform/providers/wallet_heartbeat_service.dart'; import 'package:get_it/get_it.dart'; import 'package:sf_infrastructure/sf_infrastructure.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:sf_tracking/sf_tracking.dart'; import 'package:sf_localizations/sf_localizations.dart'; import 'package:utils/utils.dart'; import 'package:fonts/fonts.dart'; import 'package:videocall_sdk/videocall_sdk.dart'; class SaveFamilyApp extends ConsumerStatefulWidget { const SaveFamilyApp({super.key}); @override SaveFamilyAppState createState() => SaveFamilyAppState(); } class SaveFamilyAppState extends ConsumerState with WidgetsBindingObserver { WalletHeartbeatService? _walletHeartbeat; LegacyHeartbeatService? _legacyHeartbeat; SfRouterListener? _trackingRouterListener; WebSocketService? _webSocket; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _trackingRouterListener = SfRouterListener( listenable: appRouter.routerDelegate, currentScreenName: () { final config = appRouter.routerDelegate.currentConfiguration; if (config.matches.isEmpty) return null; return config.last.route.name; }, tracking: sfTracking, ); if (isPaymentMode) { _walletHeartbeat = WalletHeartbeatService( repository: ref.read(treezorRepositoryProvider), sessionLocal: SessionLocalDatasourceImpl(), onError: () => appRouter.go(AppRoutes.scaTreezor), ); } if (isLegacyMode) { _legacyHeartbeat = LegacyHeartbeatService( ref: ref, onUnauthorized: () { clearSessionData(); appRouter.go(AppRoutes.legacyLogin); }, ); _webSocket = GetIt.I(); appRouter.routerDelegate.addListener(_onRouteChanged); } onBeforeSessionCleared = () { _walletHeartbeat?.stop(); _legacyHeartbeat?.stop(); _webSocket?.disconnect(); GetIt.I().logout(); FirebaseMessaging.instance.deleteToken().catchError((Object e) { debugPrint('[FCM] deleteToken on logout failed: $e'); }); }; } void _onRouteChanged() { final heartbeat = _legacyHeartbeat; if (heartbeat == null) return; final location = appRouter.routerDelegate.currentConfiguration.uri.path; if (location.startsWith(AppRoutes.legacyDashboard)) { heartbeat.start(); _webSocket?.connect(); } else { heartbeat.stop(); _webSocket?.disconnect(); } } @override void dispose() { if (isLegacyMode) { appRouter.routerDelegate.removeListener(_onRouteChanged); } _trackingRouterListener?.dispose(); _walletHeartbeat?.stop(); _legacyHeartbeat?.stop(); _webSocket?.disconnect(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { debugPrint('State: $state'); ref.read(appLifecycleStateProvider.notifier).setState(state); if (state == AppLifecycleState.resumed) { _walletHeartbeat?.start(); if (isLegacyMode) { _onRouteChanged(); } ref.read(permissionsProvider.notifier).checkPermissions(); ref.read(appVersionCheckProvider.notifier).refresh(); } else if (state == AppLifecycleState.paused) { _walletHeartbeat?.stop(); _legacyHeartbeat?.stop(); _webSocket?.disconnect(); } super.didChangeAppLifecycleState(state); } @override Widget build(BuildContext context) { SizeUtils.init(context: context); ref.watch(pushTokenRefreshListenerProvider); // Theme wiring: // - Legacy mode: new `legacy_theme` package (Material 3 + light/dark/system). // - Payment mode: unchanged behaviour (seed-based ColorScheme, light only). final ThemeData lightTheme; final ThemeData? darkTheme; final ThemeMode themeMode; if (isLegacyMode) { final legacyThemeState = ref.watch(legacyThemeNotifierProvider); lightTheme = LegacyAppTheme.light; darkTheme = LegacyAppTheme.dark; themeMode = legacyThemeState.themeMode; } else { lightTheme = ThemeData( fontFamily: AppFonts.stolzl, colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF329E95)), ); darkTheme = null; themeMode = ThemeMode.light; } return AppUpdateGate( child: MaterialApp.router( title: 'SaveFamily', theme: lightTheme, darkTheme: darkTheme, themeMode: themeMode, routerConfig: appRouter, debugShowCheckedModeBanner: false, localizationsDelegates: [ SFLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: [for (final lang in supportedLanguages) Locale(lang)], localeResolutionCallback: (locale, supportedLocales) { if (locale == null) return supportedLocales.first; for (var supportedLocale in supportedLocales) { if (supportedLocale.languageCode == locale.languageCode) { return supportedLocale; } } return supportedLocales.first; }, ), ); } }