import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:geosector_app/core/theme/app_theme.dart'; import 'package:geosector_app/core/services/theme_service.dart'; import 'package:go_router/go_router.dart'; import 'package:geosector_app/core/services/current_user_service.dart'; import 'package:geosector_app/core/repositories/user_repository.dart'; import 'package:geosector_app/core/repositories/operation_repository.dart'; import 'package:geosector_app/core/repositories/passage_repository.dart'; import 'package:geosector_app/core/repositories/sector_repository.dart'; import 'package:geosector_app/core/repositories/membre_repository.dart'; import 'package:geosector_app/core/repositories/amicale_repository.dart'; import 'package:geosector_app/core/services/sync_service.dart'; import 'package:geosector_app/core/services/connectivity_service.dart'; import 'package:geosector_app/core/services/chat_manager.dart'; import 'package:geosector_app/presentation/auth/splash_page.dart'; import 'package:geosector_app/presentation/auth/login_page.dart'; import 'package:geosector_app/presentation/auth/register_page.dart'; import 'package:geosector_app/presentation/admin/admin_dashboard_page.dart'; import 'package:geosector_app/presentation/user/user_dashboard_page.dart'; // Instances globales des repositories (plus besoin d'injecter ApiService) final operationRepository = OperationRepository(); final passageRepository = PassageRepository(); final userRepository = UserRepository(); final sectorRepository = SectorRepository(); final membreRepository = MembreRepository(); final amicaleRepository = AmicaleRepository(); final syncService = SyncService(userRepository: userRepository); final connectivityService = ConnectivityService(); final themeService = ThemeService.instance; class GeosectorApp extends StatefulWidget { const GeosectorApp({super.key}); @override State createState() => _GeosectorAppState(); } class _GeosectorAppState extends State with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { // Arrêter le chat quand l'app se ferme ChatManager.instance.dispose(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.resumed: // App revenue au premier plan - relancer les syncs debugPrint('📱 App au premier plan - Reprise des syncs chat'); ChatManager.instance.resumeSyncs(); break; case AppLifecycleState.paused: // App mise en arrière-plan - arrêter les syncs pour économiser la batterie debugPrint('⏸️ App en arrière-plan - Pause des syncs chat'); ChatManager.instance.pauseSyncs(); break; case AppLifecycleState.inactive: // État transitoire (ex: appel entrant) - ne rien faire debugPrint('💤 App inactive temporairement'); break; case AppLifecycleState.detached: // App vraiment fermée (rare sur mobile) - nettoyer complètement debugPrint('🛑 App fermée complètement - Arrêt total du chat'); ChatManager.instance.dispose(); break; case AppLifecycleState.hidden: // État masqué (Flutter 3.13+) - traiter comme paused debugPrint('👻 App masquée'); ChatManager.instance.pauseSyncs(); break; } } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: themeService, builder: (context, child) { return MaterialApp.router( title: 'GeoSector', theme: AppTheme.lightTheme, darkTheme: AppTheme.darkTheme, themeMode: themeService.themeMode, routerConfig: _createRouter(), debugShowCheckedModeBanner: false, // Builder pour appliquer le theme responsive à toute l'app builder: (context, child) { return LayoutBuilder( builder: (context, constraints) { // Récupérer le theme actuel (clair ou sombre) final brightness = Theme.of(context).brightness; final textColor = brightness == Brightness.light ? AppTheme.textLightColor : AppTheme.textDarkColor; // Débogage en mode développement final width = constraints.maxWidth; final scaleFactor = AppTheme.getFontScaleFactor(width); // Afficher le debug uniquement lors du changement de taille if (width < AppTheme.breakpointMobileSmall) { debugPrint('📱 Mode: Très petit mobile (${width.toStringAsFixed(0)}px) → Facteur: ×$scaleFactor'); } else if (width < AppTheme.breakpointMobile) { debugPrint('📱 Mode: Mobile (${width.toStringAsFixed(0)}px) → Facteur: ×$scaleFactor'); } else if (width < AppTheme.breakpointTablet) { debugPrint('📱 Mode: Tablette (${width.toStringAsFixed(0)}px) → Facteur: ×$scaleFactor'); } else { debugPrint('🖥️ Mode: Desktop (${width.toStringAsFixed(0)}px) → Facteur: ×$scaleFactor'); } // Appliquer le TextTheme responsive return Theme( data: Theme.of(context).copyWith( textTheme: AppTheme.getResponsiveTextTheme(context, textColor), ), child: child ?? const SizedBox.shrink(), ); }, ); }, // Configuration des localisations pour le français localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: const [ Locale('fr', 'FR'), // Français comme langue principale Locale('en', 'US'), // Anglais en fallback ], locale: const Locale('fr', 'FR'), // Forcer le français par défaut ); }, ); } /// Création du routeur avec configuration pour URLs propres GoRouter _createRouter() { return GoRouter( initialLocation: '/', routes: [ GoRoute( path: '/', name: 'splash', builder: (context, state) { // Récupérer les paramètres de query pour redirection automatique final action = state.uri.queryParameters['action']; final type = state.uri.queryParameters['type']; debugPrint('GoRoute: Affichage de SplashPage avec action=$action, type=$type'); return SplashPage(action: action, type: type); }, ), GoRoute( path: '/login', name: 'login', builder: (context, state) { // Récupérer le type depuis les query parameters ou extra data final type = state.uri.queryParameters['type'] ?? (state.extra as Map?)?['type'] as String?; debugPrint('GoRoute: Affichage de LoginPage avec type: $type'); return LoginPage(loginType: type); }, ), // Routes spécifiques pour chaque type de login GoRoute( path: '/login/user', name: 'login-user', builder: (context, state) { debugPrint('GoRoute: Affichage de LoginPage pour utilisateur'); return const LoginPage(loginType: 'user'); }, ), GoRoute( path: '/login/admin', name: 'login-admin', builder: (context, state) { debugPrint('GoRoute: Affichage de LoginPage pour admin'); return const LoginPage(loginType: 'admin'); }, ), GoRoute( path: '/register', name: 'register', builder: (context, state) { debugPrint('GoRoute: Affichage de RegisterPage'); return const RegisterPage(); }, ), GoRoute( path: '/user', name: 'user', builder: (context, state) { debugPrint('GoRoute: Affichage de UserDashboardPage'); return const UserDashboardPage(); }, ), GoRoute( path: '/admin', name: 'admin', builder: (context, state) { debugPrint('GoRoute: Affichage de AdminDashboardPage'); return const AdminDashboardPage(); }, ), ], redirect: (context, state) { final currentPath = state.uri.path; debugPrint('GoRouter.redirect: currentPath = $currentPath'); // Pour la page racine, toujours autoriser l'affichage de la splash page if (currentPath == '/') { debugPrint('GoRouter.redirect: Autorisation splash page'); return null; } // Pages publiques qui ne nécessitent pas d'authentification final publicPaths = ['/login', '/login/user', '/login/admin', '/register']; if (publicPaths.any((path) => currentPath.startsWith(path))) { debugPrint('GoRouter.redirect: Page publique autorisée: $currentPath'); return null; } // Vérifier l'authentification pour les pages protégées try { // Utiliser le nouveau CurrentUserService final userService = CurrentUserService.instance; final isAuthenticated = userService.isLoggedIn; final currentUser = userService.currentUser; debugPrint('GoRouter.redirect: isAuthenticated = $isAuthenticated'); debugPrint('GoRouter.redirect: currentUser = ${currentUser?.email}'); // Si pas authentifié, rediriger vers la splash page if (!isAuthenticated) { debugPrint('GoRouter.redirect: Non authentifié, redirection vers /'); return '/'; } // Vérifier les permissions pour les pages admin if (currentPath.startsWith('/admin')) { final userRole = userService.userRole; final isAdmin = userService.canAccessAdmin; debugPrint('GoRouter.redirect: userRole = $userRole, canAccessAdmin = $isAdmin'); if (!isAdmin) { debugPrint('GoRouter.redirect: Pas admin, redirection vers /user'); return '/user'; } } // Si on arrive ici, l'utilisateur a les permissions nécessaires debugPrint('GoRouter.redirect: Accès autorisé à $currentPath'); return null; } catch (e) { debugPrint('GoRouter.redirect: Erreur lors de la vérification auth: $e'); // En cas d'erreur, rediriger vers la splash page pour sécurité return '/'; } }, // Listener pour déboguer les changements de route refreshListenable: CurrentUserService.instance, // Écouter les changements dans CurrentUserService debugLogDiagnostics: true, // Activer les logs de débogage errorBuilder: (context, state) { debugPrint('GoRouter.errorBuilder: Erreur pour ${state.uri.path}'); return Scaffold( appBar: AppBar( title: const Text('Erreur de navigation'), backgroundColor: Colors.red, foregroundColor: Colors.white, ), body: Center( child: Padding( padding: const EdgeInsets.all(24.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 64, color: Colors.red), const SizedBox(height: 16), Text( 'Page non trouvée', style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 8), Text( 'Chemin: ${state.uri.path}', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey[600], ), ), const SizedBox(height: 24), ElevatedButton.icon( onPressed: () { debugPrint('GoRouter.errorBuilder: Retour vers /'); context.go('/'); }, icon: const Icon(Icons.home), label: const Text('Retour à l\'accueil'), ), ], ), ), ), ); }, ); } }