Files
EM2_ERP/em2rp/lib/views/widgets/user_management/user_card.dart

302 lines
9.0 KiB
Dart

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<UserCard> createState() => _UserCardState();
}
class _UserCardState extends State<UserCard> {
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,
);
}
}