// lib/class/class.dart import 'package:flutter/material.dart'; /// Gestionnaire de connectivité class ConnectivityManager { final BuildContext context; ConnectivityManager(this.context); /// Initialiser la connectivité void get initConnectivity { print('Initialisation de la connectivité'); } } /// Gestionnaire d'overlays personnalisés class CustomOverlay { static OverlayEntry? _currentOverlay; /// Afficher un message d'erreur static void showError(BuildContext context, {required String message}) { hide(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ Icon(Icons.error, color: Colors.white, size: 20), SizedBox(width: 8), Expanded( child: Text( message, style: TextStyle(color: Colors.white, fontSize: 14), ), ), ], ), backgroundColor: Colors.red, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), duration: Duration(seconds: 4), ), ); } /// Afficher un message de succès static void showSuccess(BuildContext context, {required String message}) { hide(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ Icon(Icons.check_circle, color: Colors.white, size: 20), SizedBox(width: 8), Expanded( child: Text( message, style: TextStyle(color: Colors.white, fontSize: 14), ), ), ], ), backgroundColor: Colors.green, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), duration: Duration(seconds: 3), ), ); } /// Afficher un message d'information static void showInfo(BuildContext context, {required String message}) { hide(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ Icon(Icons.info_outline, color: Colors.white, size: 20), SizedBox(width: 8), Expanded( child: Text( message, style: TextStyle(color: Colors.white, fontSize: 14), ), ), ], ), backgroundColor: Color(0xFF006699), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), duration: Duration(seconds: 3), ), ); } /// Afficher un avertissement static void showWarning(BuildContext context, {required String message}) { hide(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ Icon(Icons.warning_amber, color: Colors.white, size: 20), SizedBox(width: 8), Expanded( child: Text( message, style: TextStyle(color: Colors.white, fontSize: 14), ), ), ], ), backgroundColor: Colors.orange, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), duration: Duration(seconds: 4), ), ); } /// Afficher un indicateur de chargement static void showLoading( BuildContext context, { String message = 'Chargement...', }) { hide(); _currentOverlay = OverlayEntry( builder: (context) => Material( color: Colors.black54, child: Center( child: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Color(0xFF006699), ), ), SizedBox(height: 16), Text( message, style: TextStyle(fontSize: 16, color: Colors.black87), ), ], ), ), ), ), ); Overlay.of(context).insert(_currentOverlay!); } /// Masquer l'overlay static void hide() { if (_currentOverlay != null) { _currentOverlay!.remove(); _currentOverlay = null; } } } /// Classe utilitaire pour les widgets personnalisés class CustomWidgets { /// Créer un champ de saisie stylé static Widget buildStyledTextField({ required String label, required String hint, required IconData icon, TextEditingController? controller, bool obscureText = false, String? Function(String?)? validator, void Function(String)? onChanged, bool readOnly = false, VoidCallback? onTap, TextInputType? keyboardType, int? maxLength, String? suffixText, Widget? suffix, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF2C3E50), ), ), SizedBox(height: 8), TextFormField( controller: controller, validator: validator, obscureText: obscureText, readOnly: readOnly, onTap: onTap, onChanged: onChanged, keyboardType: keyboardType, maxLength: maxLength, decoration: InputDecoration( hintText: hint, prefixIcon: Icon(icon, color: Color(0xFF006699)), suffixText: suffixText, suffix: suffix, filled: true, fillColor: Colors.white, counterText: '', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Color(0xFFE0E7FF)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Color(0xFFE0E7FF)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Color(0xFF006699), width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.red), ), ), ), ], ); } /// Créer un bouton stylé static Widget buildStyledButton({ required String text, required VoidCallback onPressed, bool isLoading = false, Color? backgroundColor, Color? textColor, IconData? icon, double? height, double? width, bool enabled = true, }) { return SizedBox( width: width ?? double.infinity, height: height ?? 48, child: ElevatedButton( onPressed: (isLoading || !enabled) ? null : onPressed, style: ElevatedButton.styleFrom( backgroundColor: backgroundColor ?? Color(0xFF006699), foregroundColor: textColor ?? Colors.white, elevation: 2, disabledBackgroundColor: Colors.grey[300], disabledForegroundColor: Colors.grey[600], shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), child: isLoading ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ Icon(icon, size: 18), SizedBox(width: 8), ], Text( text, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), ), ); } /// ===== NOUVEAU: Badge de type de compte ===== static Widget buildAccountTypeBadge({ required bool isEnterprise, double fontSize = 12, EdgeInsets? padding, }) { return Container( padding: padding ?? EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: isEnterprise ? Color(0xFF8B5CF6).withOpacity(0.1) : Color(0xFF006699).withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all( color: isEnterprise ? Color(0xFF8B5CF6).withOpacity(0.3) : Color(0xFF006699).withOpacity(0.3), width: 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isEnterprise ? Icons.business : Icons.person, color: isEnterprise ? Color(0xFF8B5CF6) : Color(0xFF006699), size: fontSize + 2, ), SizedBox(width: 6), Text( isEnterprise ? 'Entreprise' : 'Agent', style: TextStyle( color: isEnterprise ? Color(0xFF8B5CF6) : Color(0xFF006699), fontSize: fontSize, fontWeight: FontWeight.w600, ), ), ], ), ); } /// ===== NOUVEAU: Carte d'information avec icône ===== static Widget buildInfoCard({ required String title, required String value, required IconData icon, Color? iconColor, Color? backgroundColor, VoidCallback? onTap, }) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: backgroundColor ?? Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: (iconColor ?? Color(0xFF006699)).withOpacity(0.2), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Row( children: [ Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: (iconColor ?? Color(0xFF006699)).withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Icon( icon, color: iconColor ?? Color(0xFF006699), size: 24, ), ), SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 12, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), SizedBox(height: 4), Text( value, style: TextStyle( fontSize: 16, color: Colors.black87, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, ), ], ), ), if (onTap != null) Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey[400]), ], ), ), ); } /// ===== NOUVEAU: Carte de statistique ===== static Widget buildStatCard({ required String title, required String value, required IconData icon, Color? color, String? subtitle, }) { final cardColor = color ?? Color(0xFF006699); return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [cardColor.withOpacity(0.8), cardColor], ), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: cardColor.withOpacity(0.3), blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(icon, color: Colors.white, size: 32), Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Icon(Icons.trending_up, color: Colors.white, size: 16), ), ], ), SizedBox(height: 12), Text( value, style: TextStyle( color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold, ), ), SizedBox(height: 4), Text( title, style: TextStyle( color: Colors.white.withOpacity(0.9), fontSize: 14, fontWeight: FontWeight.w500, ), ), if (subtitle != null) ...[ SizedBox(height: 4), Text( subtitle, style: TextStyle( color: Colors.white.withOpacity(0.7), fontSize: 12, ), ), ], ], ), ); } /// ===== NOUVEAU: Séparateur avec texte ===== static Widget buildDividerWithText(String text) { return Row( children: [ Expanded(child: Divider(color: Colors.grey[300], thickness: 1)), Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text( text, style: TextStyle( color: Colors.grey[600], fontSize: 12, fontWeight: FontWeight.w500, ), ), ), Expanded(child: Divider(color: Colors.grey[300], thickness: 1)), ], ); } /// ===== NOUVEAU: Liste vide stylée ===== static Widget buildEmptyState({ required String message, String? subtitle, IconData? icon, VoidCallback? onRefresh, }) { return Center( child: Padding( padding: EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon ?? Icons.inbox_outlined, size: 80, color: Colors.grey[400], ), SizedBox(height: 16), Text( message, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.grey[700], ), textAlign: TextAlign.center, ), if (subtitle != null) ...[ SizedBox(height: 8), Text( subtitle, style: TextStyle(fontSize: 14, color: Colors.grey[500]), textAlign: TextAlign.center, ), ], if (onRefresh != null) ...[ SizedBox(height: 24), ElevatedButton.icon( onPressed: onRefresh, icon: Icon(Icons.refresh), label: Text('Actualiser'), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF006699), foregroundColor: Colors.white, padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), ), ], ], ), ), ); } /// ===== NOUVEAU: Dialog de confirmation ===== static Future showConfirmDialog({ required BuildContext context, required String title, required String message, String confirmText = 'Confirmer', String cancelText = 'Annuler', Color? confirmColor, IconData? icon, }) async { final result = await showDialog( context: context, builder: (context) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), title: Row( children: [ if (icon != null) ...[ Icon(icon, color: confirmColor ?? Color(0xFF006699)), SizedBox(width: 12), ], Expanded(child: Text(title, style: TextStyle(fontSize: 18))), ], ), content: Text( message, style: TextStyle(fontSize: 14, color: Colors.grey[700]), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text( cancelText, style: TextStyle(color: Colors.grey[600]), ), ), ElevatedButton( onPressed: () => Navigator.of(context).pop(true), style: ElevatedButton.styleFrom( backgroundColor: confirmColor ?? Color(0xFF006699), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: Text(confirmText), ), ], ), ); return result ?? false; } /// ===== NOUVEAU: Badge de statut ===== static Widget buildStatusBadge({ required String text, required bool isActive, double fontSize = 12, }) { return Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: isActive ? Colors.green.withOpacity(0.1) : Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all( color: isActive ? Colors.green.withOpacity(0.3) : Colors.orange.withOpacity(0.3), width: 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: isActive ? Colors.green : Colors.orange, shape: BoxShape.circle, ), ), SizedBox(width: 6), Text( text, style: TextStyle( color: isActive ? Colors.green[700] : Colors.orange[700], fontSize: fontSize, fontWeight: FontWeight.w600, ), ), ], ), ); } } /// ===== NOUVELLE CLASSE: Utilitaires pour entreprises ===== class EnterpriseWidgets { /// Badge entreprise static Widget buildEnterpriseBadge({ required String enterpriseName, double fontSize = 12, EdgeInsets? padding, }) { return Container( padding: padding ?? EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF8B5CF6).withOpacity(0.8), Color(0xFF8B5CF6)], ), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Color(0xFF8B5CF6).withOpacity(0.3), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.business, color: Colors.white, size: fontSize + 2), SizedBox(width: 6), Flexible( child: Text( enterpriseName, style: TextStyle( color: Colors.white, fontSize: fontSize, fontWeight: FontWeight.w600, ), overflow: TextOverflow.ellipsis, ), ), ], ), ); } /// Carte d'information entreprise static Widget buildEnterpriseInfoCard({ required String enterpriseId, required String enterpriseName, required double balance, int? totalMembers, VoidCallback? onTap, }) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(16), child: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF8B5CF6).withOpacity(0.8), Color(0xFF8B5CF6)], ), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Color(0xFF8B5CF6).withOpacity(0.3), blurRadius: 15, offset: Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.business, color: Colors.white, size: 32), Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Text( enterpriseId, style: TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ], ), SizedBox(height: 16), Text( enterpriseName, style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, ), SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Solde disponible', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 12, ), ), SizedBox(height: 4), Text( '${balance.toStringAsFixed(0)} FCFA', style: TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, ), ), ], ), if (totalMembers != null) Column( children: [ Icon(Icons.people, color: Colors.white, size: 24), SizedBox(height: 4), Text( '$totalMembers', style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), Text( 'Membres', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 10, ), ), ], ), ], ), ], ), ), ); } }