import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import '../../../../core/di/injection.dart'; import '../../../../core/models/membre_model.dart'; import '../../../../shared/theme/app_theme.dart'; import '../../../../shared/widgets/custom_text_field.dart'; import '../../../../shared/widgets/buttons/buttons.dart'; import '../../../../core/auth/services/permission_service.dart'; import '../../../../shared/widgets/permission_widget.dart'; import '../bloc/membres_bloc.dart'; import '../bloc/membres_event.dart'; import '../bloc/membres_state.dart'; /// Page de modification d'un membre existant class MembreEditPage extends StatefulWidget { const MembreEditPage({ super.key, required this.membre, }); final MembreModel membre; @override State createState() => _MembreEditPageState(); } class _MembreEditPageState extends State with SingleTickerProviderStateMixin, PermissionMixin { late MembresBloc _membresBloc; late TabController _tabController; final _formKey = GlobalKey(); // Controllers pour les champs du formulaire final _nomController = TextEditingController(); final _prenomController = TextEditingController(); final _emailController = TextEditingController(); final _telephoneController = TextEditingController(); final _adresseController = TextEditingController(); final _villeController = TextEditingController(); final _codePostalController = TextEditingController(); final _paysController = TextEditingController(); final _professionController = TextEditingController(); final _numeroMembreController = TextEditingController(); // Variables d'état DateTime? _dateNaissance; DateTime _dateAdhesion = DateTime.now(); bool _actif = true; bool _isLoading = false; int _currentStep = 0; bool _hasChanges = false; @override void initState() { super.initState(); // Vérification des permissions d'accès WidgetsBinding.instance.addPostFrameCallback((_) { if (!permissionService.canEditMembers) { showPermissionError(context, 'Vous n\'avez pas les permissions pour modifier les membres'); Navigator.of(context).pop(); return; } }); _membresBloc = getIt(); _tabController = TabController(length: 3, vsync: this); // Pré-remplir les champs avec les données existantes _populateFields(); // Écouter les changements pour détecter les modifications _setupChangeListeners(); } @override void dispose() { _tabController.dispose(); _nomController.dispose(); _prenomController.dispose(); _emailController.dispose(); _telephoneController.dispose(); _adresseController.dispose(); _villeController.dispose(); _codePostalController.dispose(); _paysController.dispose(); _professionController.dispose(); _numeroMembreController.dispose(); super.dispose(); } void _populateFields() { _numeroMembreController.text = widget.membre.numeroMembre; _nomController.text = widget.membre.nom; _prenomController.text = widget.membre.prenom; _emailController.text = widget.membre.email; _telephoneController.text = widget.membre.telephone; _adresseController.text = widget.membre.adresse ?? ''; _villeController.text = widget.membre.ville ?? ''; _codePostalController.text = widget.membre.codePostal ?? ''; _paysController.text = widget.membre.pays ?? 'Côte d\'Ivoire'; _professionController.text = widget.membre.profession ?? ''; _dateNaissance = widget.membre.dateNaissance; _dateAdhesion = widget.membre.dateAdhesion; _actif = widget.membre.actif; } void _setupChangeListeners() { final controllers = [ _nomController, _prenomController, _emailController, _telephoneController, _adresseController, _villeController, _codePostalController, _paysController, _professionController ]; for (final controller in controllers) { controller.addListener(_onFieldChanged); } } void _onFieldChanged() { if (!_hasChanges) { setState(() { _hasChanges = true; }); } } @override Widget build(BuildContext context) { return BlocProvider.value( value: _membresBloc, child: WillPopScope( onWillPop: _onWillPop, child: Scaffold( backgroundColor: AppTheme.backgroundLight, appBar: _buildAppBar(), body: BlocConsumer( listener: (context, state) { if (state is MembreUpdated) { setState(() { _isLoading = false; _hasChanges = false; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Membre modifié avec succès !'), backgroundColor: AppTheme.successColor, ), ); Navigator.of(context).pop(true); // Retourner true pour indiquer le succès } else if (state is MembresError) { setState(() { _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: AppTheme.errorColor, ), ); } }, builder: (context, state) { return Column( children: [ _buildProgressIndicator(), Expanded( child: _buildFormContent(), ), _buildBottomActions(), ], ); }, ), ), ), ); } PreferredSizeWidget _buildAppBar() { return AppBar( backgroundColor: AppTheme.primaryColor, foregroundColor: Colors.white, title: Text( 'Modifier ${widget.membre.prenom} ${widget.membre.nom}', style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 18, ), ), actions: [ if (_hasChanges) PermissionIconButton( permission: () => permissionService.canEditMembers, icon: const Icon(Icons.save), onPressed: _submitForm, tooltip: 'Sauvegarder', disabledMessage: 'Vous n\'avez pas les permissions pour modifier ce membre', ), IconButton( icon: const Icon(Icons.help_outline), onPressed: _showHelp, tooltip: 'Aide', ), ], ); } Widget _buildProgressIndicator() { return Container( padding: const EdgeInsets.all(16), color: Colors.white, child: Column( children: [ Row( children: [ _buildStepIndicator(0, 'Informations\npersonnelles', Icons.person), _buildStepConnector(0), _buildStepIndicator(1, 'Contact &\nAdresse', Icons.contact_mail), _buildStepConnector(1), _buildStepIndicator(2, 'Finalisation', Icons.check_circle), ], ), const SizedBox(height: 8), LinearProgressIndicator( value: (_currentStep + 1) / 3, backgroundColor: AppTheme.backgroundLight, valueColor: const AlwaysStoppedAnimation(AppTheme.primaryColor), ), ], ), ); } Widget _buildStepIndicator(int step, String label, IconData icon) { final isActive = step == _currentStep; final isCompleted = step < _currentStep; Color color; if (isCompleted) { color = AppTheme.successColor; } else if (isActive) { color = AppTheme.primaryColor; } else { color = AppTheme.textHint; } return Expanded( child: Column( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: isCompleted ? AppTheme.successColor : isActive ? AppTheme.primaryColor : AppTheme.backgroundLight, shape: BoxShape.circle, border: Border.all(color: color, width: 2), ), child: Icon( isCompleted ? Icons.check : icon, color: isCompleted || isActive ? Colors.white : color, size: 20, ), ), const SizedBox(height: 8), Text( label, textAlign: TextAlign.center, style: TextStyle( fontSize: 10, color: color, fontWeight: isActive ? FontWeight.w600 : FontWeight.normal, ), ), ], ), ); } Widget _buildStepConnector(int step) { final isCompleted = step < _currentStep; return Expanded( child: Container( height: 2, margin: const EdgeInsets.only(bottom: 32), color: isCompleted ? AppTheme.successColor : AppTheme.backgroundLight, ), ); } Widget _buildFormContent() { return Form( key: _formKey, child: PageView( controller: PageController(initialPage: _currentStep), onPageChanged: (index) { setState(() { _currentStep = index; }); }, children: [ _buildPersonalInfoStep(), _buildContactStep(), _buildFinalizationStep(), ], ), ); } Widget _buildContactStep() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSectionHeader( 'Contact & Adresse', 'Modifiez les informations de contact et adresse', ), const SizedBox(height: 24), // Email CustomTextField( controller: _emailController, label: 'Email *', hintText: 'exemple@email.com', prefixIcon: Icons.email_outlined, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, validator: (value) { if (value == null || value.trim().isEmpty) { return 'L\'email est requis'; } if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) { return 'Format d\'email invalide'; } return null; }, ), const SizedBox(height: 16), // Téléphone CustomTextField( controller: _telephoneController, label: 'Téléphone *', hintText: '+225 XX XX XX XX XX', prefixIcon: Icons.phone_outlined, keyboardType: TextInputType.phone, textInputAction: TextInputAction.next, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[0-9+\-\s\(\)]')), ], validator: (value) { if (value == null || value.trim().isEmpty) { return 'Le téléphone est requis'; } if (value.trim().length < 8) { return 'Numéro de téléphone invalide'; } return null; }, ), const SizedBox(height: 24), // Section Adresse const Text( 'Adresse', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), const SizedBox(height: 16), // Adresse CustomTextField( controller: _adresseController, label: 'Adresse', hintText: 'Rue, quartier, etc.', prefixIcon: Icons.location_on_outlined, textInputAction: TextInputAction.next, maxLines: 2, ), const SizedBox(height: 16), // Ville et Code postal Row( children: [ Expanded( flex: 2, child: CustomTextField( controller: _villeController, label: 'Ville', hintText: 'Abidjan', prefixIcon: Icons.location_city_outlined, textInputAction: TextInputAction.next, ), ), const SizedBox(width: 16), Expanded( child: CustomTextField( controller: _codePostalController, label: 'Code postal', hintText: '00225', prefixIcon: Icons.markunread_mailbox_outlined, keyboardType: TextInputType.number, textInputAction: TextInputAction.next, ), ), ], ), const SizedBox(height: 16), // Pays CustomTextField( controller: _paysController, label: 'Pays', prefixIcon: Icons.flag_outlined, textInputAction: TextInputAction.done, ), ], ), ); } Widget _buildFinalizationStep() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSectionHeader( 'Finalisation', 'Vérifiez les modifications et finalisez', ), const SizedBox(height: 24), // Résumé des modifications _buildChangesCard(), const SizedBox(height: 24), // Date d'adhésion _buildDateField( label: 'Date d\'adhésion', value: _dateAdhesion, onTap: _selectDateAdhesion, icon: Icons.calendar_today_outlined, ), const SizedBox(height: 16), // Statut actif Card( elevation: 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: SwitchListTile( title: const Text('Membre actif'), subtitle: Text( _actif ? 'Le membre peut accéder aux services' : 'Le membre est désactivé', ), value: _actif, onChanged: (value) { setState(() { _actif = value; _hasChanges = true; }); }, activeColor: AppTheme.primaryColor, ), ), const SizedBox(height: 16), // Informations de version _buildVersionInfo(), ], ), ); } Widget _buildSectionHeader(String title, String subtitle) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), const SizedBox(height: 8), Text( subtitle, style: const TextStyle( fontSize: 14, color: AppTheme.textSecondary, ), ), ], ); } Widget _buildDateField({ required String label, required DateTime? value, required VoidCallback onTap, required IconData icon, }) { return InkWell( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), decoration: BoxDecoration( border: Border.all(color: AppTheme.borderColor), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(icon, color: AppTheme.textSecondary), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), Text( value != null ? DateFormat('dd/MM/yyyy').format(value) : 'Sélectionner une date', style: TextStyle( fontSize: 16, color: value != null ? AppTheme.textPrimary : AppTheme.textHint, ), ), ], ), ), const Icon(Icons.edit, color: AppTheme.textSecondary), ], ), ), ); } Widget _buildPersonalInfoStep() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSectionHeader( 'Informations personnelles', 'Modifiez les informations de base du membre', ), const SizedBox(height: 24), // Numéro de membre (non modifiable) CustomTextField( controller: _numeroMembreController, label: 'Numéro de membre', prefixIcon: Icons.badge, enabled: false, validator: (value) { if (value == null || value.isEmpty) { return 'Le numéro de membre est requis'; } return null; }, ), const SizedBox(height: 16), // Nom et Prénom Row( children: [ Expanded( child: CustomTextField( controller: _prenomController, label: 'Prénom *', hintText: 'Jean', prefixIcon: Icons.person_outline, textInputAction: TextInputAction.next, validator: (value) { if (value == null || value.trim().isEmpty) { return 'Le prénom est requis'; } if (value.trim().length < 2) { return 'Le prénom doit contenir au moins 2 caractères'; } return null; }, ), ), const SizedBox(width: 16), Expanded( child: CustomTextField( controller: _nomController, label: 'Nom *', hintText: 'Dupont', prefixIcon: Icons.person_outline, textInputAction: TextInputAction.next, validator: (value) { if (value == null || value.trim().isEmpty) { return 'Le nom est requis'; } if (value.trim().length < 2) { return 'Le nom doit contenir au moins 2 caractères'; } return null; }, ), ), ], ), const SizedBox(height: 16), // Date de naissance _buildDateField( label: 'Date de naissance', value: _dateNaissance, onTap: _selectDateNaissance, icon: Icons.cake_outlined, ), const SizedBox(height: 16), // Profession CustomTextField( controller: _professionController, label: 'Profession', hintText: 'Enseignant, Commerçant, etc.', prefixIcon: Icons.work_outline, textInputAction: TextInputAction.next, ), ], ), ); } Widget _buildChangesCard() { if (!_hasChanges) { return Card( elevation: 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: const Padding( padding: EdgeInsets.all(16), child: Row( children: [ Icon(Icons.info_outline, color: AppTheme.textSecondary), SizedBox(width: 12), Expanded( child: Text( 'Aucune modification détectée', style: TextStyle( fontSize: 14, color: AppTheme.textSecondary, ), ), ), ], ), ), ); } return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.edit, color: AppTheme.warningColor), SizedBox(width: 8), Text( 'Modifications détectées', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ], ), const SizedBox(height: 16), _buildSummaryRow('Nom complet', '${_prenomController.text} ${_nomController.text}'), _buildSummaryRow('Email', _emailController.text), _buildSummaryRow('Téléphone', _telephoneController.text), if (_dateNaissance != null) _buildSummaryRow('Date de naissance', DateFormat('dd/MM/yyyy').format(_dateNaissance!)), if (_professionController.text.isNotEmpty) _buildSummaryRow('Profession', _professionController.text), if (_adresseController.text.isNotEmpty) _buildSummaryRow('Adresse', _adresseController.text), _buildSummaryRow('Statut', _actif ? 'Actif' : 'Inactif'), ], ), ), ); } Widget _buildSummaryRow(String label, String value) { if (value.trim().isEmpty) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Text( label, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, fontWeight: FontWeight.w500, ), ), ), Expanded( child: Text( value, style: const TextStyle( fontSize: 14, color: AppTheme.textPrimary, ), ), ), ], ), ); } Widget _buildVersionInfo() { return Card( elevation: 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.info_outline, color: AppTheme.textSecondary), SizedBox(width: 8), Text( 'Informations de version', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ], ), const SizedBox(height: 12), Text( 'Version actuelle : ${widget.membre.version}', style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), const SizedBox(height: 4), Text( 'Créé le : ${DateFormat('dd/MM/yyyy à HH:mm').format(widget.membre.dateCreation)}', style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), if (widget.membre.dateModification != null) ...[ const SizedBox(height: 4), Text( 'Modifié le : ${DateFormat('dd/MM/yyyy à HH:mm').format(widget.membre.dateModification!)}', style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ], ], ), ), ); } Widget _buildBottomActions() { return Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 4, offset: Offset(0, -2), ), ], ), child: Row( children: [ if (_currentStep > 0) Expanded( child: OutlinedButton( onPressed: _previousStep, style: OutlinedButton.styleFrom( foregroundColor: AppTheme.primaryColor, side: const BorderSide(color: AppTheme.primaryColor), padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('Précédent'), ), ), if (_currentStep > 0) const SizedBox(width: 16), Expanded( flex: _currentStep == 0 ? 1 : 1, child: ElevatedButton( onPressed: _isLoading ? null : _handleNextOrSubmit, style: ElevatedButton.styleFrom( backgroundColor: _hasChanges ? AppTheme.primaryColor : AppTheme.textHint, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: _isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text(_currentStep == 2 ? 'Sauvegarder' : 'Suivant'), ), ), ], ), ); } void _previousStep() { if (_currentStep > 0) { setState(() { _currentStep--; }); } } void _handleNextOrSubmit() { if (_currentStep < 2) { if (_validateCurrentStep()) { setState(() { _currentStep++; }); } } else { _submitForm(); } } bool _validateCurrentStep() { switch (_currentStep) { case 0: return _validatePersonalInfo(); case 1: return _validateContactInfo(); case 2: return true; // Pas de validation spécifique pour la finalisation default: return false; } } bool _validatePersonalInfo() { bool isValid = true; if (_prenomController.text.trim().isEmpty) { _showFieldError('Le prénom est requis'); isValid = false; } if (_nomController.text.trim().isEmpty) { _showFieldError('Le nom est requis'); isValid = false; } return isValid; } bool _validateContactInfo() { bool isValid = true; if (_emailController.text.trim().isEmpty) { _showFieldError('L\'email est requis'); isValid = false; } else if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(_emailController.text)) { _showFieldError('Format d\'email invalide'); isValid = false; } if (_telephoneController.text.trim().isEmpty) { _showFieldError('Le téléphone est requis'); isValid = false; } return isValid; } void _showFieldError(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: AppTheme.errorColor, duration: const Duration(seconds: 2), ), ); } void _submitForm() { // Vérification des permissions if (!permissionService.canEditMembers) { showPermissionError(context, 'Vous n\'avez pas les permissions pour modifier ce membre'); return; } if (!_formKey.currentState!.validate()) { return; } if (!_hasChanges) { _showFieldError('Aucune modification à sauvegarder'); return; } // Log de l'action pour audit permissionService.logAction('Modification membre', details: { 'membreId': widget.membre.id, 'nom': '${widget.membre.prenom} ${widget.membre.nom}', }); setState(() { _isLoading = true; }); // Créer le modèle membre modifié final membreModifie = widget.membre.copyWith( nom: _nomController.text.trim(), prenom: _prenomController.text.trim(), email: _emailController.text.trim(), telephone: _telephoneController.text.trim(), dateNaissance: _dateNaissance, adresse: _adresseController.text.trim().isNotEmpty ? _adresseController.text.trim() : null, ville: _villeController.text.trim().isNotEmpty ? _villeController.text.trim() : null, codePostal: _codePostalController.text.trim().isNotEmpty ? _codePostalController.text.trim() : null, pays: _paysController.text.trim().isNotEmpty ? _paysController.text.trim() : null, profession: _professionController.text.trim().isNotEmpty ? _professionController.text.trim() : null, dateAdhesion: _dateAdhesion, actif: _actif, version: widget.membre.version + 1, dateModification: DateTime.now(), ); // Envoyer l'événement de modification final memberId = widget.membre.id; if (memberId != null && memberId.isNotEmpty) { _membresBloc.add(UpdateMembre(memberId, membreModifie)); } else { _showFieldError('Erreur : ID du membre manquant'); setState(() { _isLoading = false; }); } } Future _selectDateNaissance() async { final date = await showDatePicker( context: context, initialDate: _dateNaissance ?? DateTime.now().subtract(const Duration(days: 365 * 25)), firstDate: DateTime(1900), lastDate: DateTime.now(), locale: const Locale('fr', 'FR'), ); if (date != null && date != _dateNaissance) { setState(() { _dateNaissance = date; _hasChanges = true; }); } } Future _selectDateAdhesion() async { final date = await showDatePicker( context: context, initialDate: _dateAdhesion, firstDate: DateTime(2000), lastDate: DateTime.now().add(const Duration(days: 365)), locale: const Locale('fr', 'FR'), ); if (date != null && date != _dateAdhesion) { setState(() { _dateAdhesion = date; _hasChanges = true; }); } } Future _onWillPop() async { if (!_hasChanges) { return true; } final result = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Modifications non sauvegardées'), content: const Text( 'Vous avez des modifications non sauvegardées. ' 'Voulez-vous vraiment quitter sans sauvegarder ?', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('Annuler'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), style: TextButton.styleFrom( foregroundColor: AppTheme.errorColor, ), child: const Text('Quitter sans sauvegarder'), ), ], ), ); return result ?? false; } void _showHelp() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Aide - Modification de membre'), content: const SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Modification en 3 étapes :', style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text('1. Informations personnelles : Nom, prénom, date de naissance'), Text('2. Contact & Adresse : Email, téléphone, adresse'), Text('3. Finalisation : Vérification et sauvegarde'), SizedBox(height: 16), Text( 'Fonctionnalités :', style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text('• Détection automatique des modifications'), Text('• Validation en temps réel'), Text('• Confirmation avant sortie si modifications non sauvées'), Text('• Gestion de version automatique'), SizedBox(height: 16), Text( 'Le numéro de membre ne peut pas être modifié pour des raisons de traçabilité.', style: TextStyle(fontSize: 12, color: Colors.grey), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Fermer'), ), ], ), ); } }