Better layout for user card and user management page
This commit is contained in:
@ -40,33 +40,32 @@ class _UserManagementPageState extends State<UserManagementPage> {
|
|||||||
return const Center(child: Text("Aucun utilisateur trouvé"));
|
return const Center(child: Text("Aucun utilisateur trouvé"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Détermine le nombre de colonnes selon la largeur
|
|
||||||
final width = MediaQuery.of(context).size.width;
|
final width = MediaQuery.of(context).size.width;
|
||||||
int crossAxisCount = 1;
|
int crossAxisCount;
|
||||||
if (width > 1200)
|
|
||||||
|
// Ajustement du nombre de colonnes selon la taille d'écran
|
||||||
|
if (width > 1200) {
|
||||||
crossAxisCount = 4;
|
crossAxisCount = 4;
|
||||||
else if (width > 800)
|
} else if (width > 800) {
|
||||||
crossAxisCount = 3;
|
crossAxisCount = 3;
|
||||||
else if (width > 600) crossAxisCount = 2;
|
} else if (width > 600) {
|
||||||
|
crossAxisCount = 2;
|
||||||
|
} else {
|
||||||
|
crossAxisCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
itemCount: users.length,
|
itemCount: users.length,
|
||||||
// Dans GridView.builder
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: width > 1200
|
crossAxisCount: crossAxisCount,
|
||||||
? 4
|
|
||||||
: width > 800
|
|
||||||
? 3
|
|
||||||
: width > 600
|
|
||||||
? 2
|
|
||||||
: 1,
|
|
||||||
crossAxisSpacing: 16,
|
crossAxisSpacing: 16,
|
||||||
mainAxisSpacing: 16,
|
mainAxisSpacing: 16,
|
||||||
// childAspectRatio fixé pour desktop ; mobile ignore car row layout
|
mainAxisExtent: width < 600
|
||||||
|
? 80
|
||||||
|
: 180, // Augmenté de 170 à 180 pour le desktop
|
||||||
),
|
),
|
||||||
|
|
||||||
itemBuilder: (context, i) {
|
itemBuilder: (context, i) {
|
||||||
final user = users[i];
|
final user = users[i];
|
||||||
return UserCard(
|
return UserCard(
|
||||||
|
@ -7,9 +7,7 @@ class UserCard extends StatelessWidget {
|
|||||||
final VoidCallback onEdit;
|
final VoidCallback onEdit;
|
||||||
final VoidCallback onDelete;
|
final VoidCallback onDelete;
|
||||||
|
|
||||||
// Hauteurs fixes selon le device
|
static const double _desktopMaxWidth = 280;
|
||||||
static const double _mobileHeight = 150;
|
|
||||||
static const double _desktopHeight = 280;
|
|
||||||
|
|
||||||
const UserCard({
|
const UserCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -22,19 +20,18 @@ class UserCard extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final width = MediaQuery.of(context).size.width;
|
final width = MediaQuery.of(context).size.width;
|
||||||
final isMobile = width < 600;
|
final isMobile = width < 600;
|
||||||
final cardHeight = isMobile ? _mobileHeight : _desktopHeight;
|
|
||||||
|
|
||||||
return SizedBox(
|
return Card(
|
||||||
height: cardHeight, // Hauteur fixe
|
margin: EdgeInsets.zero,
|
||||||
child: Card(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
elevation: 3,
|
||||||
elevation: 3,
|
child: Container(
|
||||||
child: Padding(
|
constraints: BoxConstraints(
|
||||||
padding: const EdgeInsets.all(12),
|
maxWidth: isMobile ? double.infinity : _desktopMaxWidth,
|
||||||
child: isMobile
|
|
||||||
? _buildMobileRow(context)
|
|
||||||
: _buildDesktopColumn(context),
|
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child:
|
||||||
|
isMobile ? _buildMobileRow(context) : _buildDesktopColumn(context),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -42,112 +39,181 @@ class UserCard extends StatelessWidget {
|
|||||||
Widget _buildMobileRow(BuildContext context) {
|
Widget _buildMobileRow(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
_profileAvatar(48),
|
_profileAvatar(40),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center, // Centré verticalement
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("${user.firstName} ${user.lastName}",
|
Text(
|
||||||
style: Theme.of(context).textTheme.titleSmall),
|
"${user.firstName} ${user.lastName}",
|
||||||
Text(user.email, style: Theme.of(context).textTheme.bodySmall),
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
const SizedBox(height: 8),
|
overflow: TextOverflow.ellipsis,
|
||||||
Row(
|
),
|
||||||
children: [
|
const SizedBox(height: 2),
|
||||||
Flexible(
|
Text(
|
||||||
child: ElevatedButton.icon(
|
user.email,
|
||||||
icon: const Icon(Icons.edit, size: 14),
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
label: const Text("Modifier"),
|
overflow: TextOverflow.ellipsis,
|
||||||
onPressed: onEdit,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: AppColors.rouge,
|
|
||||||
foregroundColor: AppColors.blanc,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 8, vertical: 4),
|
|
||||||
textStyle: const TextStyle(fontSize: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(6)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Flexible(
|
|
||||||
child: ElevatedButton.icon(
|
|
||||||
icon: const Icon(Icons.delete, size: 14),
|
|
||||||
label: const Text("Supprimer"),
|
|
||||||
onPressed: onDelete,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: AppColors.gris,
|
|
||||||
foregroundColor: AppColors.blanc,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 8, vertical: 4),
|
|
||||||
textStyle: const TextStyle(fontSize: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(6)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.edit, size: 20),
|
||||||
|
onPressed: 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: onDelete,
|
||||||
|
color: AppColors.gris,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 32,
|
||||||
|
minHeight: 32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDesktopColumn(BuildContext context) {
|
Widget _buildDesktopColumn(BuildContext context) {
|
||||||
return Column(
|
return LayoutBuilder(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Écarte haut/bas
|
builder: (context, constraints) {
|
||||||
children: [
|
final isNarrow = constraints.maxWidth < 200;
|
||||||
_profileAvatar(80),
|
|
||||||
Column(
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text("${user.firstName} ${user.lastName}",
|
Column(
|
||||||
style: Theme.of(context).textTheme.titleSmall),
|
mainAxisSize: MainAxisSize.min,
|
||||||
const SizedBox(height: 4),
|
children: [
|
||||||
Text(user.email, style: Theme.of(context).textTheme.bodySmall),
|
_profileAvatar(48),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 12),
|
||||||
Text(user.role,
|
Column(
|
||||||
style: Theme.of(context)
|
mainAxisSize: MainAxisSize.min,
|
||||||
.textTheme
|
children: [
|
||||||
.bodySmall!
|
Text(
|
||||||
.copyWith(color: AppColors.gris)),
|
"${user.firstName} ${user.lastName}",
|
||||||
],
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
Row(
|
overflow: TextOverflow.ellipsis,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
),
|
||||||
children: [
|
const SizedBox(height: 4),
|
||||||
ElevatedButton.icon(
|
Text(
|
||||||
icon: const Icon(Icons.edit, size: 18),
|
user.email,
|
||||||
label: const Text("Modifier"),
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
onPressed: onEdit,
|
textAlign: TextAlign.center,
|
||||||
style: ElevatedButton.styleFrom(
|
overflow: TextOverflow.ellipsis,
|
||||||
backgroundColor: AppColors.rouge,
|
),
|
||||||
foregroundColor: AppColors.blanc,
|
if (user.role.isNotEmpty) ...[
|
||||||
padding:
|
const SizedBox(height: 4),
|
||||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
Text(
|
||||||
shape: RoundedRectangleBorder(
|
user.role,
|
||||||
borderRadius: BorderRadius.circular(8)),
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
),
|
color: AppColors.gris,
|
||||||
|
fontSize: 11,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
ElevatedButton.icon(
|
Padding(
|
||||||
icon: const Icon(Icons.delete, size: 18),
|
padding: const EdgeInsets.only(bottom: 4),
|
||||||
label: const Text("Supprimer"),
|
child: isNarrow
|
||||||
onPressed: onDelete,
|
? Column(
|
||||||
style: ElevatedButton.styleFrom(
|
mainAxisSize: MainAxisSize.min,
|
||||||
backgroundColor: AppColors.gris,
|
children: [
|
||||||
foregroundColor: AppColors.blanc,
|
_buildButton(
|
||||||
padding:
|
icon: Icons.edit,
|
||||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
label: "Modifier",
|
||||||
shape: RoundedRectangleBorder(
|
onPressed: onEdit,
|
||||||
borderRadius: BorderRadius.circular(8)),
|
color: AppColors.rouge,
|
||||||
),
|
isNarrow: true,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
_buildButton(
|
||||||
|
icon: Icons.delete,
|
||||||
|
label: "Supprimer",
|
||||||
|
onPressed: onDelete,
|
||||||
|
color: AppColors.gris,
|
||||||
|
isNarrow: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_buildButton(
|
||||||
|
icon: Icons.edit,
|
||||||
|
label: "Modifier",
|
||||||
|
onPressed: onEdit,
|
||||||
|
color: AppColors.rouge,
|
||||||
|
isNarrow: false,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
_buildButton(
|
||||||
|
icon: Icons.delete,
|
||||||
|
label: "Supprimer",
|
||||||
|
onPressed: 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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user