import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../../../shared/theme/app_theme.dart'; import '../../../../shared/widgets/custom_text_field.dart'; import '../../../../shared/widgets/loading_button.dart'; import '../../../navigation/presentation/pages/main_navigation.dart'; import 'forgot_password_screen.dart'; import 'register_screen.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State with TickerProviderStateMixin { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); late AnimationController _fadeController; late AnimationController _slideController; late Animation _fadeAnimation; late Animation _slideAnimation; bool _isLoading = false; bool _obscurePassword = true; bool _rememberMe = false; @override void initState() { super.initState(); _initializeAnimations(); _startAnimations(); } void _initializeAnimations() { _fadeController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _slideController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _fadeController, curve: Curves.easeInOut, )); _slideAnimation = Tween( begin: const Offset(0, 0.3), end: Offset.zero, ).animate(CurvedAnimation( parent: _slideController, curve: Curves.easeOutCubic, )); } void _startAnimations() async { await Future.delayed(const Duration(milliseconds: 100)); _fadeController.forward(); _slideController.forward(); } @override void dispose() { _emailController.dispose(); _passwordController.dispose(); _fadeController.dispose(); _slideController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppTheme.backgroundLight, appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back_ios, color: AppTheme.textPrimary), onPressed: () => Navigator.of(context).pop(), ), ), body: SafeArea( child: AnimatedBuilder( animation: _fadeAnimation, builder: (context, child) { return Opacity( opacity: _fadeAnimation.value, child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: SlideTransition( position: _slideAnimation, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), const SizedBox(height: 40), _buildLoginForm(), const SizedBox(height: 24), _buildForgotPassword(), const SizedBox(height: 32), _buildLoginButton(), const SizedBox(height: 24), _buildDivider(), const SizedBox(height: 24), _buildSocialLogin(), const SizedBox(height: 32), _buildSignUpLink(), ], ), ), ), ); }, ), ), ); } Widget _buildHeader() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Logo petit Container( width: 60, height: 60, decoration: BoxDecoration( color: AppTheme.primaryColor, borderRadius: BorderRadius.circular(15), ), child: const Icon( Icons.groups_rounded, color: Colors.white, size: 30, ), ), const SizedBox(height: 24), // Titre const Text( 'Bienvenue !', style: TextStyle( fontSize: 32, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), const SizedBox(height: 8), // Sous-titre const Text( 'Connectez-vous à votre compte UnionFlow', style: TextStyle( fontSize: 16, color: AppTheme.textSecondary, ), ), ], ); } Widget _buildLoginForm() { return Form( key: _formKey, child: Column( children: [ // Champ Email CustomTextField( controller: _emailController, label: 'Adresse email', hintText: 'votre.email@exemple.com', prefixIcon: Icons.email_outlined, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, validator: _validateEmail, onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(), ), const SizedBox(height: 16), // Champ Mot de passe CustomTextField( controller: _passwordController, label: 'Mot de passe', hintText: 'Votre mot de passe', prefixIcon: Icons.lock_outline, obscureText: _obscurePassword, textInputAction: TextInputAction.done, validator: _validatePassword, onFieldSubmitted: (_) => _handleLogin(), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off : Icons.visibility, color: AppTheme.textHint, ), onPressed: () { setState(() { _obscurePassword = !_obscurePassword; }); }, ), ), const SizedBox(height: 16), // Remember me Row( children: [ Checkbox( value: _rememberMe, onChanged: (value) { setState(() { _rememberMe = value ?? false; }); }, activeColor: AppTheme.primaryColor, ), const Text( 'Se souvenir de moi', style: TextStyle( color: AppTheme.textSecondary, fontSize: 14, ), ), ], ), ], ), ); } Widget _buildForgotPassword() { return Align( alignment: Alignment.centerRight, child: TextButton( onPressed: () => _navigateToForgotPassword(), child: const Text( 'Mot de passe oublié ?', style: TextStyle( color: AppTheme.primaryColor, fontWeight: FontWeight.w600, ), ), ), ); } Widget _buildLoginButton() { return LoadingButton( onPressed: _handleLogin, isLoading: _isLoading, text: 'Se connecter', width: double.infinity, height: 56, ); } Widget _buildDivider() { return const Row( children: [ Expanded(child: Divider()), Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text( 'ou', style: TextStyle( color: AppTheme.textHint, fontSize: 14, ), ), ), Expanded(child: Divider()), ], ); } Widget _buildSocialLogin() { return Column( children: [ // Google Login SizedBox( width: double.infinity, height: 56, child: OutlinedButton.icon( onPressed: () => _handleGoogleLogin(), icon: Image.asset( 'assets/icons/google.png', width: 20, height: 20, errorBuilder: (context, error, stackTrace) => const Icon( Icons.g_mobiledata, color: Colors.red, size: 20, ), ), label: const Text('Continuer avec Google'), style: OutlinedButton.styleFrom( foregroundColor: AppTheme.textPrimary, side: const BorderSide(color: AppTheme.borderColor), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), const SizedBox(height: 12), // Microsoft Login SizedBox( width: double.infinity, height: 56, child: OutlinedButton.icon( onPressed: () => _handleMicrosoftLogin(), icon: const Icon( Icons.business, color: Color(0xFF00A4EF), size: 20, ), label: const Text('Continuer avec Microsoft'), style: OutlinedButton.styleFrom( foregroundColor: AppTheme.textPrimary, side: const BorderSide(color: AppTheme.borderColor), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), ], ); } Widget _buildSignUpLink() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Pas encore de compte ? ', style: TextStyle( color: AppTheme.textSecondary, fontSize: 14, ), ), TextButton( onPressed: () => _navigateToRegister(), child: const Text( 'S\'inscrire', style: TextStyle( color: AppTheme.primaryColor, fontWeight: FontWeight.w600, fontSize: 14, ), ), ), ], ); } String? _validateEmail(String? value) { if (value == null || value.isEmpty) { return 'Veuillez saisir votre adresse email'; } if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) { return 'Veuillez saisir une adresse email valide'; } return null; } String? _validatePassword(String? value) { if (value == null || value.isEmpty) { return 'Veuillez saisir votre mot de passe'; } if (value.length < 6) { return 'Le mot de passe doit contenir au moins 6 caractères'; } return null; } Future _handleLogin() async { if (!_formKey.currentState!.validate()) { return; } setState(() { _isLoading = true; }); try { // Simulation d'authentification await Future.delayed(const Duration(seconds: 2)); // Vibration de succès HapticFeedback.lightImpact(); // Navigation vers le dashboard if (mounted) { Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const MainNavigation(), transitionDuration: const Duration(milliseconds: 600), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation, child: SlideTransition( position: Tween( begin: const Offset(1.0, 0.0), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )), child: child, ), ); }, ), ); } } catch (e) { // Gestion d'erreur if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur de connexion: ${e.toString()}'), backgroundColor: AppTheme.errorColor, behavior: SnackBarBehavior.floating, ), ); } } finally { if (mounted) { setState(() { _isLoading = false; }); } } } void _handleGoogleLogin() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Connexion Google - En cours de développement'), backgroundColor: AppTheme.infoColor, behavior: SnackBarBehavior.floating, ), ); } void _handleMicrosoftLogin() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Connexion Microsoft - En cours de développement'), backgroundColor: AppTheme.infoColor, behavior: SnackBarBehavior.floating, ), ); } void _navigateToForgotPassword() { Navigator.of(context).push( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const ForgotPasswordScreen(), transitionDuration: const Duration(milliseconds: 400), transitionsBuilder: (context, animation, secondaryAnimation, child) { return SlideTransition( position: Tween( begin: const Offset(1.0, 0.0), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )), child: child, ); }, ), ); } void _navigateToRegister() { Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const RegisterScreen(), transitionDuration: const Duration(milliseconds: 400), transitionsBuilder: (context, animation, secondaryAnimation, child) { return SlideTransition( position: Tween( begin: const Offset(1.0, 0.0), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )), child: child, ); }, ), ); } }