Files
geo/app/lib/app.dart
Pierre f7baa7492c feat: Mise à jour des interfaces mobiles v3.2.3
- Amélioration des interfaces utilisateur sur mobile
- Optimisation de la responsivité des composants Flutter
- Mise à jour des widgets de chat et communication
- Amélioration des formulaires et tableaux
- Ajout de nouveaux composants pour l'administration
- Optimisation des thèmes et styles visuels

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 20:35:40 +02:00

320 lines
12 KiB
Dart
Executable File
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: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<GeosectorApp> createState() => _GeosectorAppState();
}
class _GeosectorAppState extends State<GeosectorApp> 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 MediaQuery(
// Conserver les données MediaQuery existantes
data: MediaQuery.of(context),
child: Builder(
builder: (context) {
// 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 = MediaQuery.of(context).size.width;
final scaleFactor = AppTheme.getFontScaleFactor(width);
debugPrint('📱 Largeur écran: ${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<String, dynamic>?)?['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'),
),
],
),
),
),
);
},
);
}
}