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 'login_screen.dart'; class RegisterScreen extends StatefulWidget { const RegisterScreen({super.key}); @override State createState() => _RegisterScreenState(); } class _RegisterScreenState extends State with TickerProviderStateMixin { final _formKey = GlobalKey(); final _firstNameController = TextEditingController(); final _lastNameController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _confirmPasswordController = TextEditingController(); late AnimationController _fadeController; late AnimationController _slideController; late Animation _fadeAnimation; late Animation _slideAnimation; bool _isLoading = false; bool _obscurePassword = true; bool _obscureConfirmPassword = true; bool _acceptTerms = false; bool _acceptNewsletter = 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() { _firstNameController.dispose(); _lastNameController.dispose(); _emailController.dispose(); _passwordController.dispose(); _confirmPasswordController.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: 32), _buildRegistrationForm(), const SizedBox(height: 24), _buildTermsAndConditions(), const SizedBox(height: 32), _buildRegisterButton(), const SizedBox(height: 24), _buildLoginLink(), ], ), ), ), ); }, ), ), ); } 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.person_add_rounded, color: Colors.white, size: 30, ), ), const SizedBox(height: 24), // Titre const Text( 'Créer un compte', style: TextStyle( fontSize: 32, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), const SizedBox(height: 8), // Sous-titre Text( 'Rejoignez UnionFlow et gérez votre association', style: TextStyle( fontSize: 16, color: AppTheme.textSecondary, ), ), ], ); } Widget _buildRegistrationForm() { return Form( key: _formKey, child: Column( children: [ // Nom et Prénom Row( children: [ Expanded( child: CustomTextField( controller: _firstNameController, label: 'Prénom', hintText: 'Jean', prefixIcon: Icons.person_outline, textInputAction: TextInputAction.next, validator: _validateFirstName, onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(), ), ), const SizedBox(width: 16), Expanded( child: CustomTextField( controller: _lastNameController, label: 'Nom', hintText: 'Dupont', prefixIcon: Icons.person_outline, textInputAction: TextInputAction.next, validator: _validateLastName, onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(), ), ), ], ), const SizedBox(height: 16), // Email CustomTextField( controller: _emailController, label: 'Adresse email', hintText: 'jean.dupont@exemple.com', prefixIcon: Icons.email_outlined, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, validator: _validateEmail, onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(), ), const SizedBox(height: 16), // Mot de passe CustomTextField( controller: _passwordController, label: 'Mot de passe', hintText: 'Minimum 8 caractères', prefixIcon: Icons.lock_outline, obscureText: _obscurePassword, textInputAction: TextInputAction.next, validator: _validatePassword, onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off : Icons.visibility, color: AppTheme.textHint, ), onPressed: () { setState(() { _obscurePassword = !_obscurePassword; }); }, ), ), const SizedBox(height: 16), // Confirmer mot de passe CustomTextField( controller: _confirmPasswordController, label: 'Confirmer le mot de passe', hintText: 'Retapez votre mot de passe', prefixIcon: Icons.lock_outline, obscureText: _obscureConfirmPassword, textInputAction: TextInputAction.done, validator: _validateConfirmPassword, onFieldSubmitted: (_) => _handleRegister(), suffixIcon: IconButton( icon: Icon( _obscureConfirmPassword ? Icons.visibility_off : Icons.visibility, color: AppTheme.textHint, ), onPressed: () { setState(() { _obscureConfirmPassword = !_obscureConfirmPassword; }); }, ), ), const SizedBox(height: 16), // Indicateur de force du mot de passe _buildPasswordStrengthIndicator(), ], ), ); } Widget _buildPasswordStrengthIndicator() { final password = _passwordController.text; final strength = _calculatePasswordStrength(password); Color strengthColor; String strengthText; if (strength < 0.3) { strengthColor = AppTheme.errorColor; strengthText = 'Faible'; } else if (strength < 0.7) { strengthColor = AppTheme.warningColor; strengthText = 'Moyen'; } else { strengthColor = AppTheme.successColor; strengthText = 'Fort'; } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Force du mot de passe', style: TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), if (password.isNotEmpty) Text( strengthText, style: TextStyle( fontSize: 12, color: strengthColor, fontWeight: FontWeight.w600, ), ), ], ), const SizedBox(height: 8), Container( height: 4, decoration: BoxDecoration( color: AppTheme.borderColor, borderRadius: BorderRadius.circular(2), ), child: FractionallySizedBox( alignment: Alignment.centerLeft, widthFactor: password.isEmpty ? 0 : strength, child: Container( decoration: BoxDecoration( color: strengthColor, borderRadius: BorderRadius.circular(2), ), ), ), ), ], ); } double _calculatePasswordStrength(String password) { if (password.isEmpty) return 0.0; double strength = 0.0; // Longueur if (password.length >= 8) strength += 0.25; if (password.length >= 12) strength += 0.25; // Contient des minuscules if (password.contains(RegExp(r'[a-z]'))) strength += 0.15; // Contient des majuscules if (password.contains(RegExp(r'[A-Z]'))) strength += 0.15; // Contient des chiffres if (password.contains(RegExp(r'[0-9]'))) strength += 0.1; // Contient des caractères spéciaux if (password.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) strength += 0.1; return strength.clamp(0.0, 1.0); } Widget _buildTermsAndConditions() { return Column( children: [ // Accepter les conditions Row( children: [ Checkbox( value: _acceptTerms, onChanged: (value) { setState(() { _acceptTerms = value ?? false; }); }, activeColor: AppTheme.primaryColor, ), Expanded( child: RichText( text: TextSpan( style: const TextStyle( color: AppTheme.textSecondary, fontSize: 14, ), children: [ const TextSpan(text: 'J\'accepte les '), TextSpan( text: 'Conditions d\'utilisation', style: const TextStyle( color: AppTheme.primaryColor, fontWeight: FontWeight.w600, decoration: TextDecoration.underline, ), ), const TextSpan(text: ' et la '), TextSpan( text: 'Politique de confidentialité', style: const TextStyle( color: AppTheme.primaryColor, fontWeight: FontWeight.w600, decoration: TextDecoration.underline, ), ), ], ), ), ), ], ), // Newsletter (optionnel) Row( children: [ Checkbox( value: _acceptNewsletter, onChanged: (value) { setState(() { _acceptNewsletter = value ?? false; }); }, activeColor: AppTheme.primaryColor, ), const Expanded( child: Text( 'Je souhaite recevoir des actualités et conseils par email (optionnel)', style: TextStyle( color: AppTheme.textSecondary, fontSize: 14, ), ), ), ], ), ], ); } Widget _buildRegisterButton() { return LoadingButton( onPressed: _acceptTerms ? _handleRegister : null, isLoading: _isLoading, text: 'Créer mon compte', width: double.infinity, height: 56, enabled: _acceptTerms, ); } Widget _buildLoginLink() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Déjà un compte ? ', style: TextStyle( color: AppTheme.textSecondary, fontSize: 14, ), ), TextButton( onPressed: () => _navigateToLogin(), child: const Text( 'Se connecter', style: TextStyle( color: AppTheme.primaryColor, fontWeight: FontWeight.w600, fontSize: 14, ), ), ), ], ); } String? _validateFirstName(String? value) { if (value == null || value.isEmpty) { return 'Veuillez saisir votre prénom'; } if (value.length < 2) { return 'Le prénom doit contenir au moins 2 caractères'; } return null; } String? _validateLastName(String? value) { if (value == null || value.isEmpty) { return 'Veuillez saisir votre nom'; } if (value.length < 2) { return 'Le nom doit contenir au moins 2 caractères'; } return null; } 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 un mot de passe'; } if (value.length < 8) { return 'Le mot de passe doit contenir au moins 8 caractères'; } if (!value.contains(RegExp(r'[A-Z]'))) { return 'Le mot de passe doit contenir au moins une majuscule'; } if (!value.contains(RegExp(r'[a-z]'))) { return 'Le mot de passe doit contenir au moins une minuscule'; } if (!value.contains(RegExp(r'[0-9]'))) { return 'Le mot de passe doit contenir au moins un chiffre'; } return null; } String? _validateConfirmPassword(String? value) { if (value == null || value.isEmpty) { return 'Veuillez confirmer votre mot de passe'; } if (value != _passwordController.text) { return 'Les mots de passe ne correspondent pas'; } return null; } Future _handleRegister() async { if (!_formKey.currentState!.validate()) { return; } if (!_acceptTerms) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Veuillez accepter les conditions d\'utilisation'), backgroundColor: AppTheme.errorColor, behavior: SnackBarBehavior.floating, ), ); return; } setState(() { _isLoading = true; }); try { // Simulation d'inscription await Future.delayed(const Duration(seconds: 2)); // Vibration de succès HapticFeedback.lightImpact(); // Afficher message de succès if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Compte créé avec succès ! Vérifiez votre email.'), backgroundColor: AppTheme.successColor, behavior: SnackBarBehavior.floating, ), ); // Navigation vers l'écran de connexion _navigateToLogin(); } } catch (e) { // Gestion d'erreur if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur lors de la création du compte: ${e.toString()}'), backgroundColor: AppTheme.errorColor, behavior: SnackBarBehavior.floating, ), ); } } finally { if (mounted) { setState(() { _isLoading = false; }); } } } void _navigateToLogin() { Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const LoginScreen(), 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, ); }, ), ); } }