249 lines
6.3 KiB
Dart
249 lines
6.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../unionflow_design_system.dart';
|
|
|
|
/// Header de page compact et moderne
|
|
///
|
|
/// Composant header minimaliste pour les pages principales du BottomNavigationBar.
|
|
/// Design épuré sans gradient lourd, optimisé pour l'espace.
|
|
///
|
|
/// Usage:
|
|
/// ```dart
|
|
/// UFPageHeader(
|
|
/// title: 'Membres',
|
|
/// icon: Icons.people,
|
|
/// actions: [
|
|
/// IconButton(icon: Icon(Icons.add), onPressed: () {}),
|
|
/// ],
|
|
/// )
|
|
/// ```
|
|
class UFPageHeader extends StatelessWidget {
|
|
final String title;
|
|
final IconData icon;
|
|
final List<Widget>? actions;
|
|
final Color? iconColor;
|
|
final bool showDivider;
|
|
|
|
const UFPageHeader({
|
|
super.key,
|
|
required this.title,
|
|
required this.icon,
|
|
this.actions,
|
|
this.iconColor,
|
|
this.showDivider = true,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final effectiveIconColor = iconColor ?? ColorTokens.primary;
|
|
|
|
return Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: SpacingTokens.lg,
|
|
vertical: SpacingTokens.md,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// Icône
|
|
Container(
|
|
padding: const EdgeInsets.all(SpacingTokens.sm),
|
|
decoration: BoxDecoration(
|
|
color: effectiveIconColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: effectiveIconColor,
|
|
size: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: SpacingTokens.md),
|
|
|
|
// Titre
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: TypographyTokens.titleLarge.copyWith(
|
|
color: ColorTokens.onSurface,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
|
|
// Actions
|
|
if (actions != null) ...actions!,
|
|
],
|
|
),
|
|
),
|
|
|
|
// Divider optionnel
|
|
if (showDivider)
|
|
Divider(
|
|
height: 1,
|
|
thickness: 1,
|
|
color: ColorTokens.outline.withOpacity(0.1),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Header de page avec statistiques
|
|
///
|
|
/// Header compact avec des métriques KPI intégrées.
|
|
///
|
|
/// Usage:
|
|
/// ```dart
|
|
/// UFPageHeaderWithStats(
|
|
/// title: 'Membres',
|
|
/// icon: Icons.people,
|
|
/// stats: [
|
|
/// UFHeaderStat(label: 'Total', value: '142'),
|
|
/// UFHeaderStat(label: 'Actifs', value: '128'),
|
|
/// ],
|
|
/// )
|
|
/// ```
|
|
class UFPageHeaderWithStats extends StatelessWidget {
|
|
final String title;
|
|
final IconData icon;
|
|
final List<UFHeaderStat> stats;
|
|
final List<Widget>? actions;
|
|
final Color? iconColor;
|
|
|
|
const UFPageHeaderWithStats({
|
|
super.key,
|
|
required this.title,
|
|
required this.icon,
|
|
required this.stats,
|
|
this.actions,
|
|
this.iconColor,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final effectiveIconColor = iconColor ?? ColorTokens.primary;
|
|
|
|
return Column(
|
|
children: [
|
|
// Titre et actions
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(
|
|
SpacingTokens.lg,
|
|
SpacingTokens.md,
|
|
SpacingTokens.lg,
|
|
SpacingTokens.sm,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// Icône
|
|
Container(
|
|
padding: const EdgeInsets.all(SpacingTokens.sm),
|
|
decoration: BoxDecoration(
|
|
color: effectiveIconColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: effectiveIconColor,
|
|
size: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: SpacingTokens.md),
|
|
|
|
// Titre
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: TypographyTokens.titleLarge.copyWith(
|
|
color: ColorTokens.onSurface,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
|
|
// Actions
|
|
if (actions != null) ...actions!,
|
|
],
|
|
),
|
|
),
|
|
|
|
// Statistiques
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(
|
|
SpacingTokens.lg,
|
|
0,
|
|
SpacingTokens.lg,
|
|
SpacingTokens.md,
|
|
),
|
|
child: Row(
|
|
children: stats.map((stat) {
|
|
final isLast = stat == stats.last;
|
|
return Expanded(
|
|
child: Padding(
|
|
padding: EdgeInsets.only(
|
|
right: isLast ? 0 : SpacingTokens.sm,
|
|
),
|
|
child: _buildStatItem(stat),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
|
|
// Divider
|
|
Divider(
|
|
height: 1,
|
|
thickness: 1,
|
|
color: ColorTokens.outline.withOpacity(0.1),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildStatItem(UFHeaderStat stat) {
|
|
return UFContainer.rounded(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: SpacingTokens.md,
|
|
vertical: SpacingTokens.sm,
|
|
),
|
|
color: (stat.color ?? ColorTokens.primary).withOpacity(0.05),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
stat.value,
|
|
style: TypographyTokens.titleMedium.copyWith(
|
|
color: stat.color ?? ColorTokens.primary,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: SpacingTokens.xs),
|
|
Text(
|
|
stat.label,
|
|
style: TypographyTokens.labelSmall.copyWith(
|
|
color: ColorTokens.onSurfaceVariant,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Statistique pour UFPageHeaderWithStats
|
|
class UFHeaderStat {
|
|
final String label;
|
|
final String value;
|
|
final Color? color;
|
|
|
|
const UFHeaderStat({
|
|
required this.label,
|
|
required this.value,
|
|
this.color,
|
|
});
|
|
}
|
|
|