Better layout for user card and user management page

This commit is contained in:
2025-05-05 20:08:01 +02:00
parent 4c7ce27a0c
commit 94337581d8
2 changed files with 181 additions and 116 deletions

View File

@ -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(

View File

@ -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),
),
),
),
); );
} }