import 'package:flutter/material.dart'; import 'package:em2rp/models/user_model.dart'; import 'package:em2rp/utils/colors.dart'; class UserCard extends StatefulWidget { final UserModel user; final VoidCallback onEdit; final VoidCallback onDelete; static const double _desktopMaxWidth = 280; const UserCard({ super.key, required this.user, required this.onEdit, required this.onDelete, }); @override State createState() => _UserCardState(); } class _UserCardState extends State { ImageProvider? _profileImage; String? _lastUrl; bool _isLoadingImage = false; @override void didUpdateWidget(UserCard oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.user.profilePhotoUrl != widget.user.profilePhotoUrl) { _loadProfileImage(); } } @override void initState() { super.initState(); _loadProfileImage(); } void _loadProfileImage() { final url = widget.user.profilePhotoUrl; if (url.isNotEmpty) { setState(() { _isLoadingImage = true; _lastUrl = url; }); final image = NetworkImage(url); image.resolve(const ImageConfiguration()).addListener( ImageStreamListener( (info, _) { if (mounted) { setState(() { _profileImage = image; _isLoadingImage = false; }); } }, onError: (error, stack) { if (mounted) { setState(() { _profileImage = null; _isLoadingImage = false; }); } }, ), ); } else { setState(() { _profileImage = null; _isLoadingImage = false; _lastUrl = null; }); } } @override Widget build(BuildContext context) { final width = MediaQuery.of(context).size.width; final isMobile = width < 600; return Card( margin: EdgeInsets.zero, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), elevation: 3, child: Container( constraints: BoxConstraints( maxWidth: isMobile ? double.infinity : UserCard._desktopMaxWidth, ), padding: const EdgeInsets.all(12), child: isMobile ? _buildMobileRow(context) : _buildDesktopColumn(context), ), ); } Widget _buildMobileRow(BuildContext context) { return Row( children: [ _profileAvatar(40), const SizedBox(width: 12), Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "${widget.user.firstName} ${widget.user.lastName}", style: Theme.of(context).textTheme.titleSmall, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), Text( widget.user.email, style: Theme.of(context).textTheme.bodySmall, overflow: TextOverflow.ellipsis, ), ], ), ), Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.edit, size: 20), onPressed: widget.onEdit, color: AppColors.rouge, padding: const EdgeInsets.all(8), constraints: const BoxConstraints( minWidth: 32, minHeight: 32, ), ), IconButton( icon: const Icon(Icons.delete, size: 20), onPressed: widget.onDelete, color: AppColors.gris, padding: const EdgeInsets.all(8), constraints: const BoxConstraints( minWidth: 32, minHeight: 32, ), ), ], ), ], ); } Widget _buildDesktopColumn(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final isNarrow = constraints.maxWidth < 200; return Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( mainAxisSize: MainAxisSize.min, children: [ _profileAvatar(48), const SizedBox(height: 12), Column( mainAxisSize: MainAxisSize.min, children: [ Text( "${widget.user.firstName} ${widget.user.lastName}", style: Theme.of(context).textTheme.titleSmall, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( widget.user.email, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), if (widget.user.role.isNotEmpty) ...[ const SizedBox(height: 4), Text( widget.user.role, style: Theme.of(context).textTheme.bodySmall!.copyWith( color: AppColors.gris, fontSize: 11, ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), ], ], ), ], ), Padding( padding: const EdgeInsets.only(bottom: 4), child: isNarrow ? Column( mainAxisSize: MainAxisSize.min, children: [ _buildButton( icon: Icons.edit, label: "Modifier", onPressed: widget.onEdit, color: AppColors.rouge, isNarrow: true, ), const SizedBox(height: 4), _buildButton( icon: Icons.delete, label: "Supprimer", onPressed: widget.onDelete, color: AppColors.gris, isNarrow: true, ), ], ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildButton( icon: Icons.edit, label: "Modifier", onPressed: widget.onEdit, color: AppColors.rouge, isNarrow: false, ), const SizedBox(width: 8), _buildButton( icon: Icons.delete, label: "Supprimer", onPressed: widget.onDelete, color: AppColors.gris, isNarrow: false, ), ], ), ), ], ); }, ); } Widget _buildButton({ required IconData icon, required String label, required VoidCallback onPressed, required Color color, required bool isNarrow, }) { return SizedBox( height: 26, width: isNarrow ? double.infinity : null, child: ElevatedButton.icon( icon: Icon(icon, size: 14), label: Text( label, style: const TextStyle(fontSize: 11), overflow: TextOverflow.ellipsis, ), onPressed: onPressed, style: ElevatedButton.styleFrom( backgroundColor: color, foregroundColor: AppColors.blanc, padding: EdgeInsets.symmetric( horizontal: isNarrow ? 4 : 8, vertical: 0, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), ), ), ); } Widget _profileAvatar(double size) { if (_isLoadingImage && widget.user.profilePhotoUrl.isNotEmpty) { return CircleAvatar( radius: size / 2, backgroundColor: Colors.grey[300], child: SizedBox( width: size * 0.5, height: size * 0.5, child: const CircularProgressIndicator(strokeWidth: 2), ), ); } return CircleAvatar( radius: size / 2, backgroundImage: _profileImage, backgroundColor: Colors.grey[200], child: (widget.user.profilePhotoUrl.isEmpty || _profileImage == null) ? Icon(Icons.person, size: size * 0.6, color: AppColors.noir) : null, ); } }