import 'package:flutter/material.dart'; import '../../../../core/auth/services/keycloak_webview_auth_service.dart'; import '../../../../core/auth/models/auth_state.dart'; import '../../../../core/di/injection.dart'; import '../../../../shared/theme/app_theme.dart'; /// Page de connexion utilisant Keycloak OIDC class KeycloakLoginPage extends StatefulWidget { const KeycloakLoginPage({super.key}); @override State createState() => _KeycloakLoginPageState(); } class _KeycloakLoginPageState extends State { late KeycloakWebViewAuthService _authService; bool _isLoading = false; @override void initState() { super.initState(); _authService = getIt(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppTheme.backgroundLight, body: StreamBuilder( stream: _authService.authStateStream, builder: (context, snapshot) { final authState = snapshot.data ?? const AuthState.unknown(); if (authState.isAuthenticated) { // Rediriger vers la page principale si déjà connecté WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.of(context).pushReplacementNamed('/main'); }); } return SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: ConstrainedBox( constraints: BoxConstraints( minHeight: MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top - MediaQuery.of(context).padding.bottom - 48, ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Logo et titre _buildHeader(), const SizedBox(height: 48), // Message d'accueil _buildWelcomeMessage(), const SizedBox(height: 32), // Bouton de connexion _buildLoginButton(authState), const SizedBox(height: 16), // Message d'erreur si présent if (authState.errorMessage != null) _buildErrorMessage(authState.errorMessage!), const SizedBox(height: 32), // Informations sur la sécurité _buildSecurityInfo(), ], ), ), ), ); }, ), ); } Widget _buildHeader() { return Column( children: [ // Logo UnionFlow Container( width: 120, height: 120, decoration: BoxDecoration( color: AppTheme.primaryColor, borderRadius: BorderRadius.circular(60), boxShadow: [ BoxShadow( color: AppTheme.primaryColor.withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: const Icon( Icons.groups, size: 60, color: Colors.white, ), ), const SizedBox(height: 24), // Titre Text( 'UnionFlow', style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, color: AppTheme.primaryColor, ), ), const SizedBox(height: 8), // Sous-titre Text( 'Gestion d\'organisations', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Colors.grey[600], ), ), ], ); } Widget _buildWelcomeMessage() { return Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(20), child: Column( children: [ const Icon( Icons.security, size: 48, color: AppTheme.primaryColor, ), const SizedBox(height: 16), Text( 'Connexion sécurisée', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( 'Connectez-vous avec votre compte UnionFlow pour accéder à toutes les fonctionnalités de l\'application.', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey[600], ), ), ], ), ), ); } Widget _buildLoginButton(AuthState authState) { return ElevatedButton( onPressed: authState.isLoading || _isLoading ? null : _handleLogin, style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 3, ), child: authState.isLoading || _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.login, size: 24), const SizedBox(width: 12), Text( 'Se connecter avec Keycloak', style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, ), ), ], ), ); } Widget _buildErrorMessage(String errorMessage) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.errorColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: AppTheme.errorColor.withOpacity(0.3)), ), child: Row( children: [ const Icon( Icons.error_outline, color: AppTheme.errorColor, size: 24, ), const SizedBox(width: 12), Expanded( child: Text( errorMessage, style: const TextStyle( color: AppTheme.errorColor, fontWeight: FontWeight.w500, ), ), ), ], ), ); } Widget _buildSecurityInfo() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.blue.withOpacity(0.3)), ), child: Column( children: [ Row( children: [ const Icon( Icons.info_outline, color: Colors.blue, size: 20, ), const SizedBox(width: 8), Text( 'Authentification sécurisée', style: Theme.of(context).textTheme.titleSmall?.copyWith( color: Colors.blue, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 8), Text( 'Vos données sont protégées par Keycloak, une solution d\'authentification enterprise. ' 'Votre mot de passe n\'est jamais stocké sur cet appareil.', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.blue[700], ), ), ], ), ); } Future _handleLogin() async { setState(() { _isLoading = true; }); try { await _authService.loginWithWebView(context); } catch (e) { // L'erreur sera gérée par le stream AuthState print('Erreur de connexion: $e'); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } }