import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:url_launcher/url_launcher.dart'; import '../bloc/auth_bloc.dart'; import '../../../../core/config/environment.dart'; import '../../../../shared/widgets/core_text_field.dart'; import '../../../../shared/widgets/dynamic_fab.dart'; import '../../../../shared/design_system/tokens/app_typography.dart'; import '../../../../shared/design_system/tokens/app_colors.dart'; /// UnionFlow Mobile - Écran de connexion (Mode DRY & Minimaliste) class LoginPage extends StatefulWidget { const LoginPage({Key? key}) : super(key: key); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } Future _openForgotPassword(BuildContext context) async { final url = Uri.parse( '${AppConfig.keycloakRealmUrl}/protocol/openid-connect/auth' '?client_id=unionflow-mobile' '&redirect_uri=${Uri.encodeComponent('http://localhost')}' '&response_type=code' '&scope=openid' '&kc_action=reset_credentials', ); try { if (await canLaunchUrl(url)) { await launchUrl(url, mode: LaunchMode.externalApplication); } else { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Impossible d\'ouvrir la page de réinitialisation')), ); } } } catch (e) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Erreur lors de l\'ouverture du lien')), ); } } } void _onLogin() { final email = _emailController.text; final password = _passwordController.text; if (email.isNotEmpty && password.isNotEmpty) { context.read().add(AuthLoginRequested(email, password)); } } @override Widget build(BuildContext context) { return Scaffold( body: BlocConsumer( listener: (context, state) { if (state is AuthAuthenticated) { // Navigator 1.0 : Le BlocBuilder dans AppRouter gérera la transition vers MainNavigationLayout } else if (state is AuthError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message, style: AppTypography.bodyTextSmall), backgroundColor: AppColors.error, ), ); } }, builder: (context, state) { final isLoading = state is AuthLoading; return SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Logo minimaliste (Texte seul) Center( child: Text( 'UnionFlow', style: AppTypography.headerSmall.copyWith( fontSize: 24, // Exception unique pour le logo color: AppColors.primaryGreen, letterSpacing: 1.2, ), ), ), const SizedBox(height: 8), Center( child: Text( 'Connexion à votre espace.', style: AppTypography.subtitleSmall, ), ), const SizedBox(height: 48), // Champs de texte DRY CoreTextField( controller: _emailController, hintText: 'Email ou Identifiant', prefixIcon: Icons.person_outline, keyboardType: TextInputType.emailAddress, ), const SizedBox(height: 16), CoreTextField( controller: _passwordController, hintText: 'Mot de passe', prefixIcon: Icons.lock_outline, obscureText: true, ), const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: TextButton( onPressed: () => _openForgotPassword(context), style: TextButton.styleFrom( padding: EdgeInsets.zero, minimumSize: const Size(0, 0), tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), child: Text( 'Oublié ?', style: AppTypography.subtitleSmall.copyWith( color: AppColors.primaryGreen, ), ), ), ), const SizedBox(height: 32), // Bouton centralisé avec chargement intégré Center( child: isLoading ? const CircularProgressIndicator(color: AppColors.primaryGreen) : DynamicFAB( icon: Icons.arrow_forward, label: 'Se Connecter', onPressed: _onLogin, ), ), ], ), ), ), ); }, ), ); } }