Files
unionflow-server-api/unionflow-mobile-apps/lib/features/profile/presentation/pages/profile_page.dart

1687 lines
50 KiB
Dart

import 'package:flutter/material.dart';
import 'dart:io';
/// Page Mon Profil - UnionFlow Mobile
///
/// Page complète de gestion du profil utilisateur avec informations personnelles,
/// préférences, sécurité, et paramètres avancés.
class ProfilePage extends StatefulWidget {
const ProfilePage({super.key});
@override
State<ProfilePage> createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage>
with TickerProviderStateMixin {
late TabController _tabController;
final _formKey = GlobalKey<FormState>();
// Contrôleurs pour les champs de texte
final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController();
final _emailController = TextEditingController();
final _phoneController = TextEditingController();
final _addressController = TextEditingController();
final _cityController = TextEditingController();
final _postalCodeController = TextEditingController();
final _bioController = TextEditingController();
// État du profil
File? _profileImage;
bool _isEditing = false;
bool _isLoading = false;
String _selectedLanguage = 'Français';
String _selectedTheme = 'Système';
bool _biometricEnabled = false;
bool _twoFactorEnabled = false;
final List<String> _languages = ['Français', 'English', 'Español', 'Deutsch'];
final List<String> _themes = ['Système', 'Clair', 'Sombre'];
@override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
_loadUserProfile();
}
@override
void dispose() {
_tabController.dispose();
_firstNameController.dispose();
_lastNameController.dispose();
_emailController.dispose();
_phoneController.dispose();
_addressController.dispose();
_cityController.dispose();
_postalCodeController.dispose();
_bioController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF8F9FA),
body: Column(
children: [
// Header harmonisé
_buildHeader(),
// Onglets
_buildTabBar(),
// Contenu des onglets
Expanded(
child: TabBarView(
controller: _tabController,
children: [
_buildPersonalInfoTab(),
_buildPreferencesTab(),
_buildSecurityTab(),
_buildAdvancedTab(),
],
),
),
],
),
);
}
/// Header harmonisé avec photo de profil
Widget _buildHeader() {
return Container(
margin: const EdgeInsets.all(12),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF6C5CE7), Color(0xFF5A4FCF)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: const Color(0xFF6C5CE7).withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Column(
children: [
Row(
children: [
// Photo de profil
Stack(
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white,
width: 3,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: ClipOval(
child: _profileImage != null
? Image.file(
_profileImage!,
fit: BoxFit.cover,
)
: Container(
color: Colors.white.withOpacity(0.2),
child: const Icon(
Icons.person,
size: 40,
color: Colors.white,
),
),
),
),
Positioned(
bottom: 0,
right: 0,
child: InkWell(
onTap: _pickProfileImage,
child: Container(
padding: const EdgeInsets.all(6),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: const Icon(
Icons.camera_alt,
size: 16,
color: Color(0xFF6C5CE7),
),
),
),
),
],
),
const SizedBox(width: 16),
// Informations utilisateur
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${_firstNameController.text} ${_lastNameController.text}',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
_emailController.text.isNotEmpty
? _emailController.text
: 'utilisateur@unionflow.com',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.8),
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'Membre actif',
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
],
),
const SizedBox(height: 16),
// Statistiques rapides
Row(
children: [
Expanded(
child: _buildStatCard('Depuis', '2 ans', Icons.calendar_today),
),
const SizedBox(width: 12),
Expanded(
child: _buildStatCard('Événements', '24', Icons.event),
),
const SizedBox(width: 12),
Expanded(
child: _buildStatCard('Organisations', '3', Icons.business),
),
],
),
],
),
);
}
/// Carte de statistique
Widget _buildStatCard(String label, String value, IconData icon) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Icon(
icon,
color: Colors.white,
size: 20,
),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
label,
style: TextStyle(
fontSize: 10,
color: Colors.white.withOpacity(0.8),
),
),
],
),
);
}
/// Barre d'onglets
Widget _buildTabBar() {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: TabBar(
controller: _tabController,
labelColor: const Color(0xFF6C5CE7),
unselectedLabelColor: Colors.grey[600],
indicatorColor: const Color(0xFF6C5CE7),
indicatorWeight: 3,
indicatorSize: TabBarIndicatorSize.tab,
labelStyle: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 11,
),
unselectedLabelStyle: const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 11,
),
tabs: const [
Tab(
icon: Icon(Icons.person, size: 18),
text: 'Personnel',
),
Tab(
icon: Icon(Icons.settings, size: 18),
text: 'Préférences',
),
Tab(
icon: Icon(Icons.security, size: 18),
text: 'Sécurité',
),
Tab(
icon: Icon(Icons.tune, size: 18),
text: 'Avancé',
),
],
),
);
}
/// Onglet informations personnelles
Widget _buildPersonalInfoTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
// Section informations de base
_buildInfoSection(
'Informations personnelles',
'Vos données personnelles et de contact',
Icons.person,
[
Row(
children: [
Expanded(
child: _buildTextField(
controller: _firstNameController,
label: 'Prénom',
icon: Icons.person_outline,
enabled: _isEditing,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildTextField(
controller: _lastNameController,
label: 'Nom',
icon: Icons.person_outline,
enabled: _isEditing,
),
),
],
),
_buildTextField(
controller: _emailController,
label: 'Email',
icon: Icons.email_outlined,
enabled: _isEditing,
keyboardType: TextInputType.emailAddress,
),
_buildTextField(
controller: _phoneController,
label: 'Téléphone',
icon: Icons.phone_outlined,
enabled: _isEditing,
keyboardType: TextInputType.phone,
),
],
),
const SizedBox(height: 16),
// Section adresse
_buildInfoSection(
'Adresse',
'Votre adresse de résidence',
Icons.location_on,
[
_buildTextField(
controller: _addressController,
label: 'Adresse',
icon: Icons.home_outlined,
enabled: _isEditing,
maxLines: 2,
),
Row(
children: [
Expanded(
flex: 2,
child: _buildTextField(
controller: _cityController,
label: 'Ville',
icon: Icons.location_city_outlined,
enabled: _isEditing,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildTextField(
controller: _postalCodeController,
label: 'Code postal',
icon: Icons.markunread_mailbox_outlined,
enabled: _isEditing,
keyboardType: TextInputType.number,
),
),
],
),
],
),
const SizedBox(height: 16),
// Section biographie
_buildInfoSection(
'À propos de moi',
'Partagez quelques mots sur vous',
Icons.info,
[
_buildTextField(
controller: _bioController,
label: 'Biographie',
icon: Icons.edit_outlined,
enabled: _isEditing,
maxLines: 4,
hintText: 'Parlez-nous de vous, vos intérêts, votre parcours...',
),
],
),
const SizedBox(height: 16),
// Boutons d'action
_buildActionButtons(),
const SizedBox(height: 80),
],
),
);
}
/// Section d'informations
Widget _buildInfoSection(
String title,
String subtitle,
IconData icon,
List<Widget> children,
) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
icon,
color: Colors.grey[600],
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[800],
),
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
],
),
const SizedBox(height: 16),
...children.map((child) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: child,
)),
],
),
);
}
/// Champ de texte personnalisé
Widget _buildTextField({
required TextEditingController controller,
required String label,
required IconData icon,
bool enabled = true,
TextInputType? keyboardType,
int maxLines = 1,
String? hintText,
}) {
return TextFormField(
controller: controller,
enabled: enabled,
keyboardType: keyboardType,
maxLines: maxLines,
decoration: InputDecoration(
labelText: label,
hintText: hintText,
prefixIcon: Icon(icon, color: enabled ? const Color(0xFF6C5CE7) : Colors.grey),
filled: true,
fillColor: enabled ? Colors.grey[50] : Colors.grey[100],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[300]!),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[300]!),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFF6C5CE7), width: 2),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[300]!),
),
labelStyle: TextStyle(
color: enabled ? Colors.grey[700] : Colors.grey[500],
),
),
validator: (value) {
if (label == 'Email' && value != null && value.isNotEmpty) {
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return 'Email invalide';
}
}
return null;
},
);
}
/// Boutons d'action
Widget _buildActionButtons() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
if (_isEditing) ...[
Expanded(
child: ElevatedButton.icon(
onPressed: _isLoading ? null : _cancelEditing,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[100],
foregroundColor: Colors.grey[700],
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 12),
),
icon: const Icon(Icons.cancel, size: 18),
label: const Text('Annuler'),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: _isLoading ? null : _saveProfile,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6C5CE7),
foregroundColor: Colors.white,
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 12),
),
icon: _isLoading
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Icon(Icons.save, size: 18),
label: Text(_isLoading ? 'Sauvegarde...' : 'Sauvegarder'),
),
),
] else ...[
Expanded(
child: ElevatedButton.icon(
onPressed: _startEditing,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6C5CE7),
foregroundColor: Colors.white,
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 12),
),
icon: const Icon(Icons.edit, size: 18),
label: const Text('Modifier le profil'),
),
),
],
],
),
);
}
/// Onglet préférences
Widget _buildPreferencesTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
// Langue et région
_buildPreferenceSection(
'Langue et région',
'Personnaliser l\'affichage',
Icons.language,
[
_buildDropdownPreference(
'Langue',
'Choisir la langue de l\'interface',
_selectedLanguage,
_languages,
(value) => setState(() => _selectedLanguage = value!),
),
_buildDropdownPreference(
'Thème',
'Apparence de l\'application',
_selectedTheme,
_themes,
(value) => setState(() => _selectedTheme = value!),
),
],
),
const SizedBox(height: 16),
// Notifications
_buildPreferenceSection(
'Notifications',
'Gérer vos alertes',
Icons.notifications,
[
_buildSwitchPreference(
'Notifications push',
'Recevoir des notifications sur cet appareil',
true,
(value) => _showSuccessSnackBar('Préférence mise à jour'),
),
_buildSwitchPreference(
'Notifications email',
'Recevoir des emails de notification',
false,
(value) => _showSuccessSnackBar('Préférence mise à jour'),
),
_buildSwitchPreference(
'Sons et vibrations',
'Alertes sonores et vibrations',
true,
(value) => _showSuccessSnackBar('Préférence mise à jour'),
),
],
),
const SizedBox(height: 16),
// Confidentialité
_buildPreferenceSection(
'Confidentialité',
'Contrôler vos données',
Icons.privacy_tip,
[
_buildSwitchPreference(
'Profil public',
'Permettre aux autres de voir votre profil',
false,
(value) => _showSuccessSnackBar('Préférence mise à jour'),
),
_buildSwitchPreference(
'Partage de données',
'Partager des données anonymes pour améliorer l\'app',
true,
(value) => _showSuccessSnackBar('Préférence mise à jour'),
),
],
),
const SizedBox(height: 80),
],
),
);
}
/// Onglet sécurité
Widget _buildSecurityTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
// Authentification
_buildSecuritySection(
'Authentification',
'Sécuriser votre compte',
Icons.security,
[
_buildSecurityItem(
'Changer le mot de passe',
'Dernière modification il y a 3 mois',
Icons.lock_outline,
() => _showChangePasswordDialog(),
),
_buildSwitchPreference(
'Authentification biométrique',
'Utiliser l\'empreinte digitale ou Face ID',
_biometricEnabled,
(value) {
setState(() => _biometricEnabled = value);
_showSuccessSnackBar('Authentification biométrique ${value ? 'activée' : 'désactivée'}');
},
),
_buildSwitchPreference(
'Authentification à deux facteurs',
'Sécurité renforcée avec SMS ou app',
_twoFactorEnabled,
(value) {
setState(() => _twoFactorEnabled = value);
if (value) {
_showTwoFactorSetupDialog();
} else {
_showSuccessSnackBar('Authentification à deux facteurs désactivée');
}
},
),
],
),
const SizedBox(height: 16),
// Sessions actives
_buildSecuritySection(
'Sessions actives',
'Gérer vos connexions',
Icons.devices,
[
_buildSessionItem(
'Cet appareil',
'Android • Maintenant',
Icons.smartphone,
true,
),
_buildSessionItem(
'Navigateur Web',
'Chrome • Il y a 2 heures',
Icons.web,
false,
),
],
),
const SizedBox(height: 16),
// Actions de sécurité
_buildSecuritySection(
'Actions de sécurité',
'Gérer votre compte',
Icons.warning,
[
_buildActionItem(
'Télécharger mes données',
'Exporter toutes vos données personnelles',
Icons.download,
const Color(0xFF0984E3),
() => _exportUserData(),
),
_buildActionItem(
'Déconnecter tous les appareils',
'Fermer toutes les sessions actives',
Icons.logout,
const Color(0xFFE17055),
() => _logoutAllDevices(),
),
_buildActionItem(
'Supprimer mon compte',
'Action irréversible - toutes les données seront perdues',
Icons.delete_forever,
Colors.red,
() => _showDeleteAccountDialog(),
),
],
),
const SizedBox(height: 80),
],
),
);
}
/// Onglet avancé
Widget _buildAdvancedTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
// Données et stockage
_buildAdvancedSection(
'Données et stockage',
'Gérer l\'utilisation des données',
Icons.storage,
[
_buildStorageItem('Cache de l\'application', '45 MB', () => _clearCache()),
_buildStorageItem('Images téléchargées', '128 MB', () => _clearImages()),
_buildStorageItem('Données hors ligne', '12 MB', () => _clearOfflineData()),
],
),
const SizedBox(height: 16),
// Développeur
_buildAdvancedSection(
'Options développeur',
'Paramètres avancés',
Icons.code,
[
_buildSwitchPreference(
'Mode développeur',
'Afficher les options de débogage',
false,
(value) => _showSuccessSnackBar('Mode développeur ${value ? 'activé' : 'désactivé'}'),
),
_buildSwitchPreference(
'Logs détaillés',
'Enregistrer plus d\'informations de débogage',
false,
(value) => _showSuccessSnackBar('Logs détaillés ${value ? 'activés' : 'désactivés'}'),
),
],
),
const SizedBox(height: 16),
// Informations système
_buildAdvancedSection(
'Informations système',
'Détails techniques',
Icons.info,
[
_buildInfoItem('Version de l\'app', '2.1.0 (Build 42)'),
_buildInfoItem('Version Flutter', '3.16.0'),
_buildInfoItem('Plateforme', 'Android 13'),
_buildInfoItem('ID de l\'appareil', 'R58R34HT85V'),
],
),
const SizedBox(height: 80),
],
),
);
}
// ==================== MÉTHODES DE CONSTRUCTION DES COMPOSANTS ====================
/// Section de préférence
Widget _buildPreferenceSection(
String title,
String subtitle,
IconData icon,
List<Widget> children,
) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: Colors.grey[600], size: 20),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[800],
),
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
],
),
const SizedBox(height: 16),
...children.map((child) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: child,
)),
],
),
);
}
/// Section de sécurité
Widget _buildSecuritySection(
String title,
String subtitle,
IconData icon,
List<Widget> children,
) {
return _buildPreferenceSection(title, subtitle, icon, children);
}
/// Section avancée
Widget _buildAdvancedSection(
String title,
String subtitle,
IconData icon,
List<Widget> children,
) {
return _buildPreferenceSection(title, subtitle, icon, children);
}
/// Préférence avec dropdown
Widget _buildDropdownPreference(
String title,
String subtitle,
String value,
List<String> options,
Function(String?) onChanged,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[300]!),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: value,
isExpanded: true,
onChanged: onChanged,
items: options.map((option) {
return DropdownMenuItem<String>(
value: option,
child: Text(option),
);
}).toList(),
),
),
),
],
);
}
/// Préférence avec switch
Widget _buildSwitchPreference(
String title,
String subtitle,
bool value,
Function(bool) onChanged,
) {
return Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
Switch(
value: value,
onChanged: onChanged,
activeColor: const Color(0xFF6C5CE7),
),
],
);
}
/// Élément de sécurité
Widget _buildSecurityItem(
String title,
String subtitle,
IconData icon,
VoidCallback onTap,
) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(icon, color: const Color(0xFF6C5CE7), size: 20),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
Icon(Icons.arrow_forward_ios, color: Colors.grey[400], size: 16),
],
),
),
);
}
/// Élément de session
Widget _buildSessionItem(
String title,
String subtitle,
IconData icon,
bool isCurrentDevice,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isCurrentDevice ? const Color(0xFF6C5CE7).withOpacity(0.1) : Colors.grey[50],
borderRadius: BorderRadius.circular(12),
border: isCurrentDevice ? Border.all(color: const Color(0xFF6C5CE7).withOpacity(0.3)) : null,
),
child: Row(
children: [
Icon(
icon,
color: isCurrentDevice ? const Color(0xFF6C5CE7) : Colors.grey[600],
size: 20,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
if (isCurrentDevice) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: const Color(0xFF6C5CE7),
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'Actuel',
style: TextStyle(
fontSize: 10,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
],
],
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
if (!isCurrentDevice)
IconButton(
onPressed: () => _terminateSession(title),
icon: const Icon(Icons.close, color: Colors.red, size: 18),
tooltip: 'Terminer la session',
),
],
),
);
}
/// Élément d'action
Widget _buildActionItem(
String title,
String subtitle,
IconData icon,
Color color,
VoidCallback onTap,
) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withOpacity(0.1)),
),
child: Row(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: color,
),
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
Icon(Icons.arrow_forward_ios, color: Colors.grey[400], size: 16),
],
),
),
);
}
/// Élément de stockage
Widget _buildStorageItem(String title, String size, VoidCallback onTap) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(Icons.folder, color: Colors.grey[600], size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
),
Text(
size,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 8),
Icon(Icons.clear, color: Colors.grey[400], size: 16),
],
),
),
);
}
/// Élément d'information
Widget _buildInfoItem(String title, String value) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
),
Text(
value,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
fontWeight: FontWeight.w600,
),
),
],
),
);
}
// ==================== MÉTHODES D'ACTION ====================
/// Charger le profil utilisateur
void _loadUserProfile() {
// Simuler le chargement des données
_firstNameController.text = 'Jean';
_lastNameController.text = 'Dupont';
_emailController.text = 'jean.dupont@unionflow.com';
_phoneController.text = '+33 6 12 34 56 78';
_addressController.text = '123 Rue de la République';
_cityController.text = 'Paris';
_postalCodeController.text = '75001';
_bioController.text = 'Membre actif du syndicat depuis 2 ans, passionné par les droits des travailleurs et l\'amélioration des conditions de travail.';
}
/// Commencer l'édition
void _startEditing() {
setState(() {
_isEditing = true;
});
}
/// Annuler l'édition
void _cancelEditing() {
setState(() {
_isEditing = false;
});
_loadUserProfile(); // Recharger les données originales
}
/// Sauvegarder le profil
Future<void> _saveProfile() async {
if (!_formKey.currentState!.validate()) return;
setState(() {
_isLoading = true;
});
// Simuler la sauvegarde
await Future.delayed(const Duration(seconds: 2));
setState(() {
_isLoading = false;
_isEditing = false;
});
_showSuccessSnackBar('Profil mis à jour avec succès');
}
/// Choisir une image de profil
Future<void> _pickProfileImage() async {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Changer la photo de profil'),
content: const Text('Cette fonctionnalité sera bientôt disponible !'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Fermer'),
),
],
),
);
}
/// Terminer une session
void _terminateSession(String deviceName) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Terminer la session'),
content: Text('Êtes-vous sûr de vouloir terminer la session sur "$deviceName" ?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_showSuccessSnackBar('Session terminée sur $deviceName');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Terminer'),
),
],
),
);
}
/// Dialogue de changement de mot de passe
void _showChangePasswordDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Changer le mot de passe'),
content: const Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Mot de passe actuel',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Nouveau mot de passe',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Confirmer le nouveau mot de passe',
border: OutlineInputBorder(),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_showSuccessSnackBar('Mot de passe modifié avec succès');
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6C5CE7),
foregroundColor: Colors.white,
),
child: const Text('Modifier'),
),
],
),
);
}
/// Configuration de l'authentification à deux facteurs
void _showTwoFactorSetupDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Authentification à deux facteurs'),
content: const Text(
'L\'authentification à deux facteurs ajoute une couche de sécurité supplémentaire à votre compte. '
'Vous recevrez un code par SMS ou via une application d\'authentification.',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
setState(() => _twoFactorEnabled = false);
},
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_showSuccessSnackBar('Authentification à deux facteurs configurée');
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6C5CE7),
foregroundColor: Colors.white,
),
child: const Text('Configurer'),
),
],
),
);
}
/// Exporter les données utilisateur
void _exportUserData() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Télécharger mes données'),
content: const Text(
'Nous allons préparer un fichier contenant toutes vos données personnelles. '
'Vous recevrez un email avec le lien de téléchargement dans les 24 heures.',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_showSuccessSnackBar('Demande d\'export envoyée. Vous recevrez un email.');
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0984E3),
foregroundColor: Colors.white,
),
child: const Text('Demander l\'export'),
),
],
),
);
}
/// Déconnecter tous les appareils
void _logoutAllDevices() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Déconnecter tous les appareils'),
content: const Text(
'Cette action fermera toutes vos sessions actives sur tous les appareils. '
'Vous devrez vous reconnecter partout.',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_showSuccessSnackBar('Toutes les sessions ont été fermées');
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE17055),
foregroundColor: Colors.white,
),
child: const Text('Déconnecter tout'),
),
],
),
);
}
/// Dialogue de suppression de compte
void _showDeleteAccountDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Supprimer mon compte'),
content: const Text(
'ATTENTION : Cette action est irréversible !\n\n'
'Toutes vos données seront définitivement supprimées :\n'
'• Profil et informations personnelles\n'
'• Historique des événements\n'
'• Participations aux organisations\n'
'• Tous les paramètres et préférences',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_showFinalDeleteConfirmation();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Continuer'),
),
],
),
);
}
/// Confirmation finale de suppression
void _showFinalDeleteConfirmation() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Confirmation finale'),
content: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Tapez "SUPPRIMER" pour confirmer :'),
SizedBox(height: 16),
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'SUPPRIMER',
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_showErrorSnackBar('Fonctionnalité désactivée pour la démo');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('SUPPRIMER DÉFINITIVEMENT'),
),
],
),
);
}
/// Vider le cache
void _clearCache() {
_showSuccessSnackBar('Cache vidé (45 MB libérés)');
}
/// Vider les images
void _clearImages() {
_showSuccessSnackBar('Images supprimées (128 MB libérés)');
}
/// Vider les données hors ligne
void _clearOfflineData() {
_showSuccessSnackBar('Données hors ligne supprimées (12 MB libérés)');
}
/// Afficher un message de succès
void _showSuccessSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: const Color(0xFF00B894),
behavior: SnackBarBehavior.floating,
),
);
}
/// Afficher un message d'erreur
void _showErrorSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: const Color(0xFFE74C3C),
behavior: SnackBarBehavior.floating,
),
);
}
}