import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:geosector_app/core/services/app_info_service.dart'; import 'package:geosector_app/core/services/hive_service.dart'; import 'dart:async'; import 'dart:math' as math; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; class SplashPage extends StatefulWidget { const SplashPage({super.key}); @override State createState() => _SplashPageState(); } // Class pour dessiner les petits points blancs sur le fond class DotsPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.white.withOpacity(0.5) ..style = PaintingStyle.fill; final random = math.Random(42); // Seed fixe pour consistance final numberOfDots = (size.width * size.height) ~/ 1500; for (int i = 0; i < numberOfDots; i++) { final x = random.nextDouble() * size.width; final y = random.nextDouble() * size.height; final radius = 1.0 + random.nextDouble() * 2.0; canvas.drawCircle(Offset(x, y), radius, paint); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class _SplashPageState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _scaleAnimation; bool _isInitializing = true; String _statusMessage = "Initialisation..."; double _progress = 0.0; bool _showButtons = false; String _appVersion = ''; Future _getAppVersion() async { try { final packageInfo = await PackageInfo.fromPlatform(); if (mounted) { setState(() { _appVersion = packageInfo.version; }); } } catch (e) { debugPrint('Erreur lors de la récupération de la version: $e'); if (mounted) { setState(() { _appVersion = AppInfoService.fullVersion.split(' ').last; }); } } } @override void initState() { super.initState(); // Animation controller _animationController = AnimationController( vsync: this, duration: const Duration(seconds: 5), ); _scaleAnimation = Tween( begin: 4.0, end: 1.0, ).animate( CurvedAnimation( parent: _animationController, curve: Curves.easeOutBack, ), ); _animationController.forward(); _getAppVersion(); _startInitialization(); } @override void dispose() { _animationController.dispose(); super.dispose(); } void _startInitialization() async { try { debugPrint('🚀 Début de l\'initialisation complète de l\'application...'); // Étape 1: Initialisation complète de Hive avec HiveService if (mounted) { setState(() { _statusMessage = "Initialisation de la base de données..."; _progress = 0.1; }); } // HiveService fait TOUT le travail lourd (adaptateurs, destruction, recréation) await HiveService.instance.initializeAndResetHive(); if (mounted) { setState(() { _statusMessage = "Vérification des bases de données..."; _progress = 0.7; }); } // Étape 2: S'assurer que toutes les Box sont ouvertes await HiveService.instance.ensureBoxesAreOpen(); if (mounted) { setState(() { _statusMessage = "Finalisation..."; _progress = 0.9; }); } // Étape 3: Vérification finale final allBoxesOpen = HiveService.instance.areAllBoxesOpen(); if (!allBoxesOpen) { final diagnostic = HiveService.instance.getDiagnostic(); debugPrint('❌ Diagnostic des Box: $diagnostic'); throw Exception('Certaines bases de données ne sont pas accessibles'); } // Finalisation if (mounted) { setState(() { _statusMessage = "Application prête !"; _progress = 1.0; _isInitializing = false; _showButtons = true; }); } debugPrint('✅ Initialisation complète de l\'application terminée avec succès'); } catch (e) { debugPrint('❌ Erreur lors de l\'initialisation: $e'); if (mounted) { setState(() { _statusMessage = "Erreur d'initialisation - Redémarrage recommandé"; _progress = 1.0; _isInitializing = false; _showButtons = true; }); } } } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( body: Stack( children: [ // Fond dégradé avec petits points blancs AnimatedContainer( duration: const Duration(milliseconds: 500), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.white, Colors.blue.shade300], ), ), child: CustomPaint( painter: DotsPainter(), child: const SizedBox(width: double.infinity, height: double.infinity), ), ), // Contenu principal SafeArea( child: Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Spacer(flex: 2), // Logo avec animation AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: child, ); }, child: Image.asset( 'assets/images/logo-geosector-1024.png', height: 180, ), ), const SizedBox(height: 24), // Titre AnimatedOpacity( opacity: _isInitializing ? 0.9 : 1.0, duration: const Duration(milliseconds: 500), child: Text( 'Geosector', style: theme.textTheme.headlineLarge?.copyWith( color: theme.colorScheme.primary, fontWeight: FontWeight.bold, letterSpacing: 1.2, ), ), ), const SizedBox(height: 16), // Sous-titre AnimatedOpacity( opacity: _isInitializing ? 0.8 : 1.0, duration: const Duration(milliseconds: 500), child: Text( 'Une application puissante et intuitive de gestion de vos distributions de calendriers', textAlign: TextAlign.center, style: theme.textTheme.bodyLarge?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), fontWeight: FontWeight.w500, ), ), ), const Spacer(flex: 1), // Indicateur de chargement if (_isInitializing) ...[ Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: LinearProgressIndicator( value: _progress, backgroundColor: Colors.grey.withOpacity(0.2), valueColor: AlwaysStoppedAnimation( theme.colorScheme.primary, ), minHeight: 10, ), ), ), const SizedBox(height: 16), Text( _statusMessage, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), ), ), ], // Boutons (reste identique) if (_showButtons) ...[ // Bouton Connexion Utilisateur AnimatedOpacity( opacity: _showButtons ? 1.0 : 0.0, duration: const Duration(milliseconds: 500), child: ElevatedButton( onPressed: () { context.go('/login/user'); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 40, vertical: 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), elevation: 2, ), child: const Text( 'Connexion Utilisateur', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(height: 16), // Bouton Connexion Administrateur AnimatedOpacity( opacity: _showButtons ? 1.0 : 0.0, duration: const Duration(milliseconds: 500), child: ElevatedButton( onPressed: () { context.go('/login/admin'); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 40, vertical: 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), elevation: 2, ), child: const Text( 'Connexion Administrateur', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(height: 32), // Bouton d'inscription AnimatedOpacity( opacity: _showButtons ? 1.0 : 0.0, duration: const Duration(milliseconds: 500), child: ElevatedButton( onPressed: () { context.go('/register'); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 40, vertical: 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), elevation: 2, ), child: const Text( 'Pas encore inscrit ?', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(height: 16), // Lien vers le site web AnimatedOpacity( opacity: _showButtons ? 1.0 : 0.0, duration: const Duration(milliseconds: 500), child: TextButton.icon( onPressed: () { String webUrl = 'https://geosector.fr'; if (kIsWeb) { final host = Uri.base.host; if (host.startsWith('dapp.')) { webUrl = 'https://dev.geosector.fr'; } else if (host.startsWith('rapp.')) { webUrl = 'https://rec.geosector.fr'; } else if (host.startsWith('app.')) { webUrl = 'https://geosector.fr'; } } launchUrl( Uri.parse(webUrl), mode: LaunchMode.externalApplication, ); }, icon: Icon( Icons.language, size: 18, color: theme.colorScheme.primary, ), label: Text( 'Site web Geosector', style: TextStyle( color: theme.colorScheme.primary, fontWeight: FontWeight.w500, ), ), ), ), ], const Spacer(flex: 1), ], ), ), ), ), // Badge de version if (_appVersion.isNotEmpty) Positioned( bottom: 16, right: 16, child: AnimatedOpacity( opacity: _showButtons ? 0.7 : 0.5, duration: const Duration(milliseconds: 500), child: Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: theme.colorScheme.primary.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all( color: theme.colorScheme.primary, width: 1, ), ), child: Text( 'v$_appVersion', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.primary, fontSize: 10, fontWeight: FontWeight.w500, ), ), ), ), ), ], ), ); } }