import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:em2rp/providers/event_provider.dart'; import 'package:em2rp/models/event_model.dart'; import 'package:intl/intl.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:em2rp/views/widgets/inputs/int_stepper_field.dart'; import 'package:em2rp/models/user_model.dart'; import 'package:em2rp/views/widgets/image/profile_picture.dart'; import 'package:flutter/services.dart'; import 'package:file_picker/file_picker.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:path/path.dart' as p; import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:firebase_auth/firebase_auth.dart'; import 'package:em2rp/providers/local_user_provider.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_dropzone/flutter_dropzone.dart'; class EventAddPage extends StatefulWidget { const EventAddPage({super.key}); @override State createState() => _EventAddPageState(); } class _EventAddPageState extends State { final _formKey = GlobalKey(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _descriptionController = TextEditingController(); final TextEditingController _priceController = TextEditingController(); final TextEditingController _installationController = TextEditingController(); final TextEditingController _disassemblyController = TextEditingController(); final TextEditingController _addressController = TextEditingController(); DateTime? _startDateTime; DateTime? _endDateTime; bool _isLoading = false; String? _error; String? _success; String? _selectedEventType; final List _eventTypes = ['Bal', 'Mariage', 'Anniversaire']; int _descriptionMaxLines = 3; List _selectedUserIds = []; List _allUsers = []; bool _isLoadingUsers = true; List> _uploadedFiles = []; DropzoneViewController? _dropzoneController; bool _isDropzoneHighlighted = false; @override void initState() { super.initState(); _descriptionController.addListener(_handleDescriptionChange); _fetchUsers(); } void _handleDescriptionChange() { final lines = '\n'.allMatches(_descriptionController.text).length + 1; setState(() { _descriptionMaxLines = lines.clamp(3, 6); }); } Future _fetchUsers() async { final snapshot = await FirebaseFirestore.instance.collection('users').get(); setState(() { _allUsers = snapshot.docs .map((doc) => UserModel.fromMap(doc.data(), doc.id)) .toList(); _isLoadingUsers = false; }); } @override void dispose() { _nameController.dispose(); _descriptionController.dispose(); _priceController.dispose(); _installationController.dispose(); _disassemblyController.dispose(); _addressController.dispose(); super.dispose(); } Future _pickAndUploadFiles() async { final result = await FilePicker.platform .pickFiles(allowMultiple: true, withData: true); if (result != null && result.files.isNotEmpty) { setState(() => _isLoading = true); try { List> files = []; for (final file in result.files) { final fileBytes = file.bytes; final fileName = file.name; if (fileBytes != null) { final ref = FirebaseStorage.instance.ref().child( 'events/temp/${DateTime.now().millisecondsSinceEpoch}_$fileName'); final uploadTask = await ref.putData(fileBytes); final url = await uploadTask.ref.getDownloadURL(); files.add({'name': fileName, 'url': url}); } else { setState(() { _error = "Impossible de lire le fichier ${file.name}"; }); } } setState(() { _uploadedFiles.addAll(files); }); } catch (e) { setState(() { _error = 'Erreur lors de l\'upload : $e'; }); } finally { setState(() => _isLoading = false); } } } Future moveEventFileHttp({ required String sourcePath, required String destinationPath, }) async { final url = Uri.parse( 'https://us-central1-em2rp-951dc.cloudfunctions.net/moveEventFileV2'); final user = FirebaseAuth.instance.currentUser; final idToken = await user?.getIdToken(); final response = await http.post( url, headers: { 'Content-Type': 'application/json', if (idToken != null) 'Authorization': 'Bearer $idToken', }, body: jsonEncode({ 'data': { 'sourcePath': sourcePath, 'destinationPath': destinationPath, } }), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); if (data['url'] != null) { return data['url'] as String; } else if (data['result'] != null && data['result']['url'] != null) { return data['result']['url'] as String; } return null; } else { print('Erreur Cloud Function: \\n${response.body}'); return null; } } Future _submit() async { if (!_formKey.currentState!.validate() || _startDateTime == null || _endDateTime == null || _selectedEventType == null || _addressController.text.isEmpty) return; if (_endDateTime!.isBefore(_startDateTime!) || _endDateTime!.isAtSameMomentAs(_startDateTime!)) { setState(() { _error = "La date de fin doit être postérieure à la date de début."; }); return; } setState(() { _isLoading = true; _error = null; _success = null; }); try { final eventProvider = Provider.of(context, listen: false); final newEvent = EventModel( id: '', name: _nameController.text.trim(), description: _descriptionController.text.trim(), startDateTime: _startDateTime!, endDateTime: _endDateTime!, price: double.tryParse(_priceController.text) ?? 0.0, installationTime: int.tryParse(_installationController.text) ?? 0, disassemblyTime: int.tryParse(_disassemblyController.text) ?? 0, eventTypeId: _selectedEventType!, customerId: '', address: _addressController.text.trim(), workforce: _selectedUserIds .map((id) => FirebaseFirestore.instance.collection('users').doc(id)) .toList(), latitude: 0.0, longitude: 0.0, documents: _uploadedFiles, ); final docRef = await FirebaseFirestore.instance .collection('events') .add(newEvent.toMap()); final eventId = docRef.id; List> newFiles = []; for (final file in _uploadedFiles) { final fileName = file['name']!; final oldUrl = file['url']!; String sourcePath; final tempPattern = RegExp(r'events/temp/[^?]+'); final match = tempPattern.firstMatch(oldUrl); if (match != null) { sourcePath = match.group(0)!; } else { final tempFileName = Uri.decodeComponent(oldUrl.split('/').last.split('?').first); sourcePath = tempFileName; } final destinationPath = 'events/$eventId/$fileName'; final newUrl = await moveEventFileHttp( sourcePath: sourcePath, destinationPath: destinationPath, ); if (newUrl != null) { newFiles.add({'name': fileName, 'url': newUrl}); } else { newFiles.add({'name': fileName, 'url': oldUrl}); } } await docRef.update({'documents': newFiles}); final localUserProvider = Provider.of(context, listen: false); final userId = localUserProvider.uid; final canViewAllEvents = localUserProvider.hasPermission('view_all_events'); if (userId != null) { await eventProvider.loadUserEvents(userId, canViewAllEvents: canViewAllEvents); } setState(() { _success = "Événement créé avec succès !"; }); if (context.mounted) Navigator.of(context).pop(); } catch (e) { setState(() { _error = "Erreur lors de la création : $e"; }); } finally { setState(() { _isLoading = false; }); } } Widget _buildSectionTitle(String title) { return Padding( padding: const EdgeInsets.only(top: 16.0, bottom: 8.0), child: Align( alignment: Alignment.centerLeft, child: Text( title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar( title: const Text('Créer un événement'), ), body: Center( child: SingleChildScrollView( child: Card( elevation: 6, margin: const EdgeInsets.all(24), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 32), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.only(top: 0.0, bottom: 4.0), child: Align( alignment: Alignment.centerLeft, child: Text( 'Informations principales', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold), ), ), ), TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Nom de l\'événement', border: OutlineInputBorder(), prefixIcon: Icon(Icons.event), ), validator: (v) => v == null || v.isEmpty ? 'Champ requis' : null, ), const SizedBox(height: 16), DropdownButtonFormField( value: _selectedEventType, items: _eventTypes .map((type) => DropdownMenuItem( value: type, child: Text(type), )) .toList(), onChanged: (val) => setState(() => _selectedEventType = val), decoration: const InputDecoration( labelText: 'Type d\'événement', border: OutlineInputBorder(), prefixIcon: Icon(Icons.category), ), validator: (v) => v == null ? 'Sélectionnez un type' : null, ), const SizedBox(height: 16), Row( children: [ Expanded( child: GestureDetector( onTap: () async { final picked = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2020), lastDate: DateTime(2099), ); if (picked != null) { final time = await showTimePicker( context: context, initialTime: TimeOfDay.now(), ); if (time != null) { setState(() { _startDateTime = DateTime( picked.year, picked.month, picked.day, time.hour, time.minute, ); if (_endDateTime != null && (_endDateTime! .isBefore(_startDateTime!) || _endDateTime!.isAtSameMomentAs( _startDateTime!))) { _endDateTime = null; } }); } } }, child: AbsorbPointer( child: TextFormField( readOnly: true, decoration: InputDecoration( labelText: 'Début', border: const OutlineInputBorder(), prefixIcon: const Icon(Icons.calendar_today), suffixIcon: const Icon(Icons.edit_calendar), ), controller: TextEditingController( text: _startDateTime == null ? '' : DateFormat('dd/MM/yyyy HH:mm') .format(_startDateTime!), ), validator: (v) => _startDateTime == null ? 'Champ requis' : null, ), ), ), ), const SizedBox(width: 16), Expanded( child: GestureDetector( onTap: _startDateTime == null ? null : () async { final picked = await showDatePicker( context: context, initialDate: _startDateTime! .add(const Duration(hours: 1)), firstDate: _startDateTime!, lastDate: DateTime(2099), ); if (picked != null) { final time = await showTimePicker( context: context, initialTime: TimeOfDay.now(), ); if (time != null) { setState(() { _endDateTime = DateTime( picked.year, picked.month, picked.day, time.hour, time.minute, ); }); } } }, child: AbsorbPointer( child: TextFormField( readOnly: true, decoration: InputDecoration( labelText: 'Fin', border: const OutlineInputBorder(), prefixIcon: const Icon(Icons.calendar_today), suffixIcon: const Icon(Icons.edit_calendar), ), controller: TextEditingController( text: _endDateTime == null ? '' : DateFormat('dd/MM/yyyy HH:mm') .format(_endDateTime!), ), validator: (v) => _endDateTime == null ? 'Champ requis' : (_startDateTime != null && _endDateTime != null && (_endDateTime!.isBefore( _startDateTime!) || _endDateTime!.isAtSameMomentAs( _startDateTime!))) ? 'La date de fin doit être après la date de début' : null, ), ), ), ), ], ), const SizedBox(height: 16), TextFormField( controller: _priceController, decoration: const InputDecoration( labelText: 'Prix (€)', border: OutlineInputBorder(), prefixIcon: Icon(Icons.euro), hintText: '1050.50', ), keyboardType: const TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow( RegExp(r'^\d*\.?\d{0,2}')), ], validator: (value) { if (value == null || value.isEmpty) { return 'Le prix est requis'; } final price = double.tryParse(value.replaceAll(',', '.')); if (price == null) { return 'Veuillez entrer un nombre valide'; } if (price < 0) { return 'Le prix ne peut pas être négatif'; } return null; }, ), _buildSectionTitle('Détails'), AnimatedContainer( duration: const Duration(milliseconds: 200), constraints: BoxConstraints( minHeight: 48, maxHeight: 48.0 * 10, ), child: TextFormField( controller: _descriptionController, minLines: 1, maxLines: _descriptionMaxLines > 10 ? 10 : _descriptionMaxLines, decoration: const InputDecoration( labelText: 'Description', border: OutlineInputBorder(), prefixIcon: Icon(Icons.description), ), ), ), const SizedBox(height: 20), Row( children: [ Expanded( child: IntStepperField( label: 'Installation (h)', controller: _installationController, min: 0, max: 99, ), ), const SizedBox(width: 16), Expanded( child: IntStepperField( label: 'Démontage (h)', controller: _disassemblyController, min: 0, max: 99, ), ), ], ), _buildSectionTitle('Adresse'), TextFormField( controller: _addressController, decoration: const InputDecoration( labelText: 'Adresse', border: OutlineInputBorder(), prefixIcon: Icon(Icons.location_on), ), validator: (v) => v == null || v.isEmpty ? 'Champ requis' : null, ), _buildSectionTitle('Personnel'), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: _isLoadingUsers ? const CircularProgressIndicator() : UserMultiSelect( allUsers: _allUsers, selectedUserIds: _selectedUserIds, onChanged: (ids) => setState(() => _selectedUserIds = ids), ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSectionTitle('Documents'), if (kIsWeb) Container( constraints: const BoxConstraints( minHeight: 180, minWidth: 200), decoration: BoxDecoration( border: Border.all( color: _isDropzoneHighlighted ? Colors.blue : Colors.grey, width: 2, ), borderRadius: BorderRadius.circular(8), color: _isDropzoneHighlighted ? Colors.blue.withOpacity(0.1) : null, ), child: Stack( children: [ DropzoneView( onCreated: (controller) => _dropzoneController = controller, onDrop: (ev) async { setState(() => _isLoading = true); try { final name = await _dropzoneController! .getFilename(ev); final bytes = await _dropzoneController! .getFileData(ev); if (bytes != null) { final ref = FirebaseStorage .instance .ref() .child( 'events/temp/${DateTime.now().millisecondsSinceEpoch}_$name'); final uploadTask = await ref.putData(bytes); final url = await uploadTask.ref .getDownloadURL(); setState(() { _uploadedFiles.add( {'name': name, 'url': url}); }); } } catch (e) { setState(() { _error = 'Erreur lors de l\'upload : $e'; }); } finally { setState(() { _isLoading = false; _isDropzoneHighlighted = false; }); } }, onHover: () => setState(() => _isDropzoneHighlighted = true), onLeave: () => setState(() => _isDropzoneHighlighted = false), onDropMultiple: (evs) async { setState(() => _isLoading = true); try { if (evs != null) { for (final ev in evs) { final name = await _dropzoneController! .getFilename(ev); final bytes = await _dropzoneController! .getFileData(ev); if (bytes != null) { final ref = FirebaseStorage .instance .ref() .child( 'events/temp/${DateTime.now().millisecondsSinceEpoch}_$name'); final uploadTask = await ref.putData(bytes); final url = await uploadTask .ref .getDownloadURL(); setState(() { _uploadedFiles.add({ 'name': name, 'url': url }); }); } } } } catch (e) { setState(() { _error = 'Erreur lors de l\'upload : $e'; }); } finally { setState(() { _isLoading = false; _isDropzoneHighlighted = false; }); } }, ), Positioned.fill( child: IgnorePointer( child: _uploadedFiles.isEmpty ? Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ const Icon( Icons.cloud_upload, size: 48, color: Colors.grey), const SizedBox(height: 12), const Text( 'Glissez-déposez des fichiers ici ou cliquez sur "Ajouter"', textAlign: TextAlign.center, style: TextStyle( color: Colors.grey, fontSize: 16), ), const SizedBox(height: 16), ], ) : null, ), ), if (_uploadedFiles.isNotEmpty) Padding( padding: const EdgeInsets.all(8.0), child: _buildFileListAndButton(), ), if (_uploadedFiles.isEmpty) Positioned( bottom: 16, left: 0, right: 0, child: Center( child: SizedBox( width: 160, child: ElevatedButton.icon( icon: const Icon( Icons.attach_file, size: 18), label: const Text('Ajouter'), style: ElevatedButton.styleFrom( padding: const EdgeInsets .symmetric( horizontal: 8, vertical: 8), minimumSize: const Size(80, 36), textStyle: const TextStyle( fontSize: 14), ), onPressed: _isLoading ? null : _pickAndUploadFiles, ), ), ), ), if (_error != null) Positioned( bottom: 0, left: 0, right: 0, child: Padding( padding: const EdgeInsets.only(top: 8.0), child: Text(_error!, style: const TextStyle( color: Colors.red)), ), ), if (_success != null) Positioned( bottom: 0, left: 0, right: 0, child: Padding( padding: const EdgeInsets.only( top: 32.0), child: Text(_success!, style: const TextStyle( color: Colors.green)), ), ), ], ), ) else if ([ TargetPlatform.windows, TargetPlatform.macOS, TargetPlatform.linux ].contains(defaultTargetPlatform)) DragTarget( onWillAccept: (data) => true, onAccept: (file) async { setState(() => _isLoading = true); try { final fileBytes = file.bytes; final fileName = file.name; if (fileBytes != null) { final ref = FirebaseStorage.instance .ref() .child( 'events/temp/${DateTime.now().millisecondsSinceEpoch}_$fileName'); final uploadTask = await ref.putData(fileBytes); final url = await uploadTask.ref .getDownloadURL(); setState(() { _uploadedFiles.add( {'name': fileName, 'url': url}); }); } } catch (e) { setState(() { _error = 'Erreur lors de l\'upload : $e'; }); } finally { setState(() => _isLoading = false); } }, builder: (context, candidateData, rejectedData) { final hasFiles = _uploadedFiles.isNotEmpty; return Container( constraints: const BoxConstraints( minHeight: 180, minWidth: 200), padding: const EdgeInsets.all(8), decoration: BoxDecoration( border: Border.all( color: candidateData.isNotEmpty ? Colors.blue : Colors.grey, width: 2, ), borderRadius: BorderRadius.circular(8), color: candidateData.isNotEmpty ? Colors.blue.withOpacity(0.1) : null, ), child: hasFiles ? _buildFileListAndButton() : Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ const Icon(Icons.cloud_upload, size: 48, color: Colors.grey), const SizedBox(height: 12), const Text( 'Glissez-déposez des fichiers ici ou cliquez sur "Ajouter"', textAlign: TextAlign.center, style: TextStyle( color: Colors.grey, fontSize: 16), ), const SizedBox(height: 16), SizedBox( width: 160, child: ElevatedButton.icon( icon: const Icon( Icons.attach_file, size: 18), label: const Text('Ajouter'), style: ElevatedButton .styleFrom( padding: const EdgeInsets .symmetric( horizontal: 8, vertical: 8), minimumSize: const Size(80, 36), textStyle: const TextStyle( fontSize: 14), ), onPressed: _isLoading ? null : _pickAndUploadFiles, ), ), if (_error != null) Padding( padding: const EdgeInsets.only( top: 8.0), child: Text(_error!, style: const TextStyle( color: Colors.red)), ), if (_success != null) Padding( padding: const EdgeInsets.only( top: 8.0), child: Text(_success!, style: const TextStyle( color: Colors.green)), ), ], ), ); }, ) else _buildFileListAndButton(), ], ), ), ], ), const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: _isLoading ? null : () => Navigator.of(context).pop(), child: const Text('Annuler'), ), const SizedBox(width: 8), ElevatedButton.icon( icon: const Icon(Icons.check), onPressed: _isLoading ? null : _submit, label: _isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Créer'), ), ], ), ], ), ), ), ), ), ), ); } Widget _buildFileListAndButton() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ..._uploadedFiles.map((file) { final fileName = file['name']!; final ext = p.extension(fileName).toLowerCase(); IconData icon; if ([".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"] .contains(ext)) { icon = Icons.image; } else if (ext == ".pdf") { icon = Icons.picture_as_pdf; } else if ([".txt", ".md", ".csv", ".json", ".xml"].contains(ext)) { icon = Icons.description; } else { icon = Icons.attach_file; } return ListTile( leading: Icon(icon, color: Colors.blueGrey), title: Text(fileName, overflow: TextOverflow.ellipsis), trailing: IconButton( icon: const Icon(Icons.close), onPressed: () { setState(() { _uploadedFiles.remove(file); }); }, ), contentPadding: EdgeInsets.zero, dense: true, ); }).toList(), SizedBox( width: 160, child: ElevatedButton.icon( icon: const Icon(Icons.attach_file, size: 18), label: const Text('Ajouter'), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), minimumSize: const Size(80, 36), textStyle: const TextStyle(fontSize: 14), ), onPressed: _isLoading ? null : _pickAndUploadFiles, ), ), if (_error != null) Padding( padding: const EdgeInsets.only(top: 8.0), child: Text(_error!, style: const TextStyle(color: Colors.red)), ), if (_success != null) Padding( padding: const EdgeInsets.only(top: 8.0), child: Text(_success!, style: const TextStyle(color: Colors.green)), ), ], ); } } class UserMultiSelect extends StatefulWidget { final List allUsers; final List selectedUserIds; final ValueChanged> onChanged; const UserMultiSelect({ super.key, required this.allUsers, required this.selectedUserIds, required this.onChanged, }); @override State createState() => _UserMultiSelectState(); } class _UserMultiSelectState extends State { void _openUserPicker() async { final result = await showDialog>( context: context, builder: (context) => _UserPickerDialog( allUsers: widget.allUsers, initiallySelected: widget.selectedUserIds, ), ); if (result != null) { widget.onChanged(result); } } @override Widget build(BuildContext context) { final selectedUsers = widget.allUsers .where((u) => widget.selectedUserIds.contains(u.uid)) .toList(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Wrap( spacing: 12, runSpacing: 12, children: selectedUsers .map((user) => Chip( avatar: ProfilePictureWidget(userId: user.uid, radius: 28), label: Text('${user.firstName} ${user.lastName}', style: const TextStyle(fontSize: 16)), labelPadding: const EdgeInsets.symmetric(horizontal: 8), deleteIcon: const Icon(Icons.close, size: 20), onDeleted: () { final newList = List.from(widget.selectedUserIds) ..remove(user.uid); widget.onChanged(newList); }, backgroundColor: Colors.grey[200], padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), )) .toList(), ), const SizedBox(height: 16), ElevatedButton.icon( icon: const Icon(Icons.add), label: const Text('Ajouter'), onPressed: _openUserPicker, ), ], ); } } class _UserPickerDialog extends StatefulWidget { final List allUsers; final List initiallySelected; const _UserPickerDialog( {required this.allUsers, required this.initiallySelected}); @override State<_UserPickerDialog> createState() => _UserPickerDialogState(); } class _UserPickerDialogState extends State<_UserPickerDialog> { String _search = ''; late List _selected; @override void initState() { super.initState(); _selected = List.from(widget.initiallySelected); } @override Widget build(BuildContext context) { final filteredUsers = widget.allUsers.where((u) { final query = _search.toLowerCase(); return ('${u.firstName} ${u.lastName}').toLowerCase().contains(query); }).toList(); return AlertDialog( title: const Text('Ajouter du personnel'), content: SizedBox( width: 400, height: 400, child: Column( children: [ TextField( decoration: const InputDecoration( labelText: 'Rechercher', prefixIcon: Icon(Icons.search), ), onChanged: (v) => setState(() => _search = v), ), const SizedBox(height: 12), Expanded( child: filteredUsers.isEmpty ? const Center(child: Text('Aucun utilisateur trouvé')) : ListView.builder( itemCount: filteredUsers.length, itemBuilder: (context, i) { final user = filteredUsers[i]; final isChecked = _selected.contains(user.uid); return CheckboxListTile( value: isChecked, onChanged: (checked) { setState(() { if (checked == true) { _selected.add(user.uid); } else { _selected.remove(user.uid); } }); }, title: Text('${user.firstName} ${user.lastName}'), subtitle: Text(user.email), secondary: ProfilePictureWidget( userId: user.uid, radius: 20), ); }, ), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Annuler'), ), ElevatedButton( onPressed: () => Navigator.pop(context, _selected), child: const Text('Ajouter'), ), ], ); } }