refactoring

This commit is contained in:
dahoud
2026-03-31 09:14:47 +00:00
parent 9bfffeeebe
commit 5383df6dcb
200 changed files with 11192 additions and 7063 deletions

View File

@@ -1,6 +1,6 @@
/// UnionFlow Primary Button - Bouton principal
///
/// Bouton primaire avec la couleur Bleu Roi (#4169E1)
///
/// Bouton primaire Vert Forêt (#2E7D32)
/// Utilisé pour les actions principales (connexion, enregistrer, valider, etc.)
library uf_primary_button;
@@ -59,22 +59,22 @@ class UFPrimaryButton extends StatelessWidget {
Widget build(BuildContext context) {
return SizedBox(
width: isFullWidth ? double.infinity : null,
height: height ?? SpacingTokens.buttonHeightLarge,
height: height ?? SpacingTokens.buttonHeightMedium,
child: ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor ?? AppColors.primaryGreen,
foregroundColor: textColor ?? Colors.white,
backgroundColor: backgroundColor ?? AppColors.primaryGreen,
foregroundColor: textColor ?? Colors.white,
disabledBackgroundColor: (backgroundColor ?? AppColors.primaryGreen).withOpacity(0.5),
disabledForegroundColor: (textColor ?? Colors.white).withOpacity(0.7),
elevation: SpacingTokens.elevationSm,
shadowColor: AppColors.darkBorder.withOpacity(0.1),
padding: const EdgeInsets.symmetric(
horizontal: SpacingTokens.buttonPaddingHorizontal,
vertical: SpacingTokens.buttonPaddingVertical,
vertical: 10,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
),
),
child: isLoading

View File

@@ -1,6 +1,6 @@
/// UnionFlow Secondary Button - Bouton secondaire
///
/// Bouton secondaire avec la couleur Indigo (#6366F1)
///
/// Bouton secondaire Vert Menthe (#4CAF50)
/// Utilisé pour les actions secondaires (annuler, retour, etc.)
library uf_secondary_button;
@@ -30,22 +30,22 @@ class UFSecondaryButton extends StatelessWidget {
Widget build(BuildContext context) {
return SizedBox(
width: isFullWidth ? double.infinity : null,
height: height ?? SpacingTokens.buttonHeightLarge,
height: height ?? SpacingTokens.buttonHeightMedium,
child: ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.brandGreen,
foregroundColor: Colors.white,
backgroundColor: AppColors.brandGreen,
foregroundColor: Colors.white,
disabledBackgroundColor: AppColors.brandGreen.withOpacity(0.5),
disabledForegroundColor: Colors.white.withOpacity(0.7),
elevation: SpacingTokens.elevationSm,
shadowColor: AppColors.darkBorder.withOpacity(0.1),
padding: const EdgeInsets.symmetric(
horizontal: SpacingTokens.buttonPaddingHorizontal,
vertical: SpacingTokens.buttonPaddingVertical,
vertical: 10,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
),
),
child: isLoading

View File

@@ -83,9 +83,9 @@ class UFCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final effectivePadding = padding ?? const EdgeInsets.all(SpacingTokens.cardPadding);
final effectivePadding = padding ?? const EdgeInsets.all(SpacingTokens.lg);
final effectiveMargin = margin ?? EdgeInsets.zero;
final effectiveBorderRadius = borderRadius ?? SpacingTokens.radiusLg;
final effectiveBorderRadius = borderRadius ?? SpacingTokens.radiusMd;
Widget content = Container(
padding: effectivePadding,
@@ -114,15 +114,6 @@ class UFCard extends StatelessWidget {
return BoxDecoration(
color: color ?? AppColors.lightSurface,
borderRadius: BorderRadius.circular(radius),
boxShadow: elevation != null
? [
BoxShadow(
color: AppColors.darkBorder.withOpacity(0.1),
blurRadius: elevation!,
offset: const Offset(0, 2),
),
]
: ShadowTokens.sm,
);
case UFCardStyle.outlined:

View File

@@ -49,15 +49,15 @@ class UFInfoCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
final effectivePadding = padding ?? const EdgeInsets.all(SpacingTokens.xl);
final effectivePadding = padding ?? const EdgeInsets.all(SpacingTokens.lg);
return Container(
padding: effectivePadding,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
boxShadow: ShadowTokens.sm,
color: isDark ? AppColors.darkSurface : Colors.white,
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -65,20 +65,20 @@ class UFInfoCard extends StatelessWidget {
// Header avec titre et trailing
Row(
children: [
Icon(icon, color: effectiveIconColor, size: 20),
Icon(icon, color: effectiveIconColor, size: 16),
const SizedBox(width: SpacingTokens.md),
Expanded(
child: Text(
title,
style: AppTypography.headerSmall.copyWith(
color: AppColors.textPrimaryLight,
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
),
),
),
if (trailing != null) trailing!,
],
),
const SizedBox(height: SpacingTokens.xl),
const SizedBox(height: SpacingTokens.lg),
// Contenu
child,
],

View File

@@ -45,7 +45,7 @@ class UFMetricCard extends StatelessWidget {
padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
),
child: Column(
children: [

View File

@@ -55,21 +55,22 @@ class UFStatCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
final effectiveIconBgColor = iconBackgroundColor ??
final effectiveIconBgColor = iconBackgroundColor ??
effectiveIconColor.withOpacity(0.1);
return Card(
elevation: SpacingTokens.elevationSm,
shadowColor: AppColors.darkBorder.withOpacity(0.1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
child: Padding(
padding: const EdgeInsets.all(SpacingTokens.cardPadding),
padding: const EdgeInsets.all(SpacingTokens.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
@@ -79,55 +80,55 @@ class UFStatCard extends StatelessWidget {
children: [
// Icône avec background coloré
Container(
padding: const EdgeInsets.all(SpacingTokens.md),
padding: const EdgeInsets.all(SpacingTokens.sm),
decoration: BoxDecoration(
color: effectiveIconBgColor,
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
),
child: Icon(
icon,
color: effectiveIconColor,
size: 24,
size: 18,
),
),
const Spacer(),
// Flèche si cliquable
if (onTap != null)
const Icon(
Icon(
Icons.arrow_forward_ios,
size: 16,
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
),
],
),
const SizedBox(height: SpacingTokens.lg),
const SizedBox(height: SpacingTokens.md),
// Titre
Text(
title,
style: AppTypography.badgeText.copyWith(
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
),
),
const SizedBox(height: SpacingTokens.sm),
// Valeur
Text(
value,
style: AppTypography.headerSmall.copyWith(
color: AppColors.textPrimaryLight,
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
),
),
// Sous-titre optionnel
if (subtitle != null) ...[
const SizedBox(height: SpacingTokens.sm),
Text(
subtitle!,
style: AppTypography.subtitleSmall.copyWith(
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
),
),
],

View File

@@ -0,0 +1,107 @@
import 'package:flutter/material.dart';
import '../tokens/unionflow_colors.dart';
/// Ligne d'activité récente — style fintech compact identique au super_admin
/// Icône dans carré arrondi 28×28 + titre + description + timestamp
class DashboardActivityRow extends StatelessWidget {
final String title;
final String description;
final String timeAgo;
final IconData icon;
final Color color;
const DashboardActivityRow({
super.key,
required this.title,
required this.description,
required this.timeAgo,
required this.icon,
required this.color,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 6),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: UnionFlowColors.border),
),
child: Row(
children: [
Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: color.withOpacity(0.12),
borderRadius: BorderRadius.circular(6),
),
child: Icon(icon, size: 14, color: color),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textPrimary,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
description,
style: const TextStyle(
fontSize: 10,
color: UnionFlowColors.textSecondary,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SizedBox(width: 8),
Text(
timeAgo,
style: const TextStyle(fontSize: 10, color: UnionFlowColors.textTertiary),
),
],
),
);
}
/// Icône selon le type d'activité
static IconData iconFor(String type) {
switch (type) {
case 'member':
return Icons.person_add_rounded;
case 'event':
return Icons.event_rounded;
case 'contribution':
return Icons.payments_rounded;
default:
return Icons.circle_notifications_rounded;
}
}
/// Couleur selon le type d'activité
static Color colorFor(String type) {
switch (type) {
case 'member':
return UnionFlowColors.unionGreen;
case 'event':
return UnionFlowColors.info;
case 'contribution':
return UnionFlowColors.gold;
default:
return UnionFlowColors.textSecondary;
}
}
}

View File

@@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import '../tokens/unionflow_colors.dart';
/// Ligne d'événement à venir — style fintech avec bordure gauche verte
/// Titre + date + countdown optionnel + participants optionnel
class DashboardEventRow extends StatelessWidget {
final String title;
final String date;
final String? daysUntil;
final String? participants;
const DashboardEventRow({
super.key,
required this.title,
required this.date,
this.daysUntil,
this.participants,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 6),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(8),
border: const Border(
left: BorderSide(color: UnionFlowColors.unionGreen, width: 3),
),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textPrimary,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
date,
style: const TextStyle(
fontSize: 10,
color: UnionFlowColors.textSecondary,
),
),
],
),
),
if (daysUntil != null || participants != null)
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (daysUntil != null)
Text(
daysUntil!,
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: UnionFlowColors.unionGreen,
),
),
if (participants != null) ...[
const SizedBox(height: 2),
Text(
participants!,
style: const TextStyle(
fontSize: 10,
color: UnionFlowColors.textTertiary,
),
),
],
],
),
],
),
);
}
}

View File

@@ -48,7 +48,9 @@ class UFDropdownTile<T> extends StatelessWidget {
@override
Widget build(BuildContext context) {
final effectiveBgColor = backgroundColor ?? AppColors.lightSurface;
final isDark = Theme.of(context).brightness == Brightness.dark;
final effectiveBgColor = backgroundColor ??
(isDark ? AppColors.darkSurface : AppColors.lightSurface);
final effectiveItemBuilder = itemBuilder ?? (item) => item.toString();
return Container(
@@ -65,16 +67,16 @@ class UFDropdownTile<T> extends StatelessWidget {
title,
style: AppTypography.bodyTextSmall.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: SpacingTokens.lg),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? AppColors.darkBackground : Colors.white,
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
border: Border.all(color: AppColors.lightBorder),
border: Border.all(color: isDark ? AppColors.darkBorder : AppColors.lightBorder),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(

View File

@@ -44,7 +44,9 @@ class UFSwitchTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
final effectiveBgColor = backgroundColor ?? AppColors.lightSurface;
final isDark = Theme.of(context).brightness == Brightness.dark;
final effectiveBgColor = backgroundColor ??
(isDark ? AppColors.darkSurface : AppColors.lightSurface);
return Container(
margin: const EdgeInsets.only(bottom: SpacingTokens.lg),
@@ -63,13 +65,13 @@ class UFSwitchTile extends StatelessWidget {
title,
style: AppTypography.bodyTextSmall.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
),
),
Text(
subtitle,
style: AppTypography.subtitleSmall.copyWith(
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
),
),
],

View File

@@ -65,7 +65,7 @@ class UFContainer extends StatelessWidget {
this.gradient,
this.border,
this.boxShadow,
}) : borderRadius = SpacingTokens.radiusLg;
}) : borderRadius = SpacingTokens.radiusMd;
/// Container très arrondi
const UFContainer.extraRounded({
@@ -81,7 +81,7 @@ class UFContainer extends StatelessWidget {
this.gradient,
this.border,
this.boxShadow,
}) : borderRadius = SpacingTokens.radiusXl;
}) : borderRadius = SpacingTokens.radiusLg;
/// Container avec ombre
UFContainer.elevated({
@@ -96,8 +96,8 @@ class UFContainer extends StatelessWidget {
this.constraints,
this.gradient,
this.border,
}) : borderRadius = SpacingTokens.radiusLg,
boxShadow = ShadowTokens.sm;
}) : borderRadius = SpacingTokens.radiusMd,
boxShadow = null;
/// Container circulaire
const UFContainer.circular({

View File

@@ -28,13 +28,12 @@ class UFHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(SpacingTokens.xl),
padding: const EdgeInsets.all(SpacingTokens.lg),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.primaryGreen, AppColors.brandGreenLight],
),
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
boxShadow: ShadowTokens.primary,
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
),
child: Row(
children: [
@@ -43,12 +42,12 @@ class UFHeader extends StatelessWidget {
padding: const EdgeInsets.all(SpacingTokens.sm),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
),
child: Icon(
icon,
color: Colors.white,
size: 24,
size: 18,
),
),
const SizedBox(width: SpacingTokens.lg),
@@ -95,7 +94,7 @@ class UFHeader extends StatelessWidget {
Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
borderRadius: BorderRadius.circular(SpacingTokens.radiusXs),
),
child: IconButton(
onPressed: onNotificationTap,
@@ -111,7 +110,7 @@ class UFHeader extends StatelessWidget {
Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
borderRadius: BorderRadius.circular(SpacingTokens.radiusXs),
),
child: IconButton(
onPressed: onSettingsTap,

View File

@@ -34,6 +34,7 @@ class UFPageHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
return Column(
@@ -59,30 +60,30 @@ class UFPageHeader extends StatelessWidget {
),
),
const SizedBox(width: SpacingTokens.md),
// Titre
Expanded(
child: Text(
title,
style: AppTypography.headerSmall.copyWith(
color: AppColors.textPrimaryLight,
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
fontWeight: FontWeight.w600,
),
),
),
// Actions
if (actions != null) ...actions!,
],
),
),
// Divider optionnel
if (showDivider)
const Divider(
Divider(
height: 1,
thickness: 1,
color: AppColors.lightBorder,
color: isDark ? AppColors.darkBorder : AppColors.lightBorder,
),
],
);
@@ -110,6 +111,7 @@ class UFPageHeaderWithStats extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
return Column(
@@ -138,18 +140,18 @@ class UFPageHeaderWithStats extends StatelessWidget {
),
),
const SizedBox(width: SpacingTokens.md),
// Titre
Expanded(
child: Text(
title,
style: AppTypography.headerSmall.copyWith(
color: AppColors.textPrimaryLight,
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
fontWeight: FontWeight.w600,
),
),
),
// Actions
if (actions != null) ...actions!,
],
@@ -172,7 +174,7 @@ class UFPageHeaderWithStats extends StatelessWidget {
padding: EdgeInsets.only(
right: isLast ? 0 : SpacingTokens.sm,
),
child: _buildStatItem(stat),
child: _buildStatItem(stat, isDark),
),
);
}).toList(),
@@ -180,16 +182,16 @@ class UFPageHeaderWithStats extends StatelessWidget {
),
// Divider
const Divider(
Divider(
height: 1,
thickness: 1,
color: AppColors.lightBorder,
color: isDark ? AppColors.darkBorder : AppColors.lightBorder,
),
],
);
}
Widget _buildStatItem(UFHeaderStat stat) {
Widget _buildStatItem(UFHeaderStat stat, bool isDark) {
final effectiveColor = stat.color ?? AppColors.primaryGreen;
return UFContainer.rounded(
padding: const EdgeInsets.symmetric(
@@ -211,7 +213,7 @@ class UFPageHeaderWithStats extends StatelessWidget {
Text(
stat.label,
style: AppTypography.badgeText.copyWith(
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import '../tokens/unionflow_colors.dart';
/// En-tête de section dashboard — titre 13px w700 avec trailing optionnel
/// Style de référence : super_admin_dashboard._buildSectionHeader
class UFSectionHeader extends StatelessWidget {
final String title;
/// Texte secondaire affiché à droite : "· trailing"
final String? trailing;
const UFSectionHeader(this.title, {this.trailing, super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
Text(
title,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: UnionFlowColors.textPrimary,
),
),
if (trailing != null && trailing!.isNotEmpty) ...[
const SizedBox(width: 6),
Text(
'· $trailing',
style: const TextStyle(
fontSize: 11,
color: UnionFlowColors.textTertiary,
),
),
],
],
);
}
}

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import '../tokens/unionflow_colors.dart';
/// Bouton d'action rapide UnionFlow
/// Style fintech : fond blanc, icône + texte colorés, bordure grise légère
/// Copie exacte du style _buildActionCell du super_admin_dashboard
class UnionActionButton extends StatelessWidget {
final IconData icon;
final String label;
@@ -20,36 +22,36 @@ class UnionActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
// backgroundColor sert d'accent (icône + texte), iconColor prend la priorité
final accentColor = iconColor ?? backgroundColor ?? UnionFlowColors.unionGreen;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 8),
decoration: BoxDecoration(
color: backgroundColor ?? UnionFlowColors.unionGreenPale,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: (backgroundColor ?? UnionFlowColors.unionGreenPale)
.withOpacity(0.2),
width: 1,
),
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: UnionFlowColors.border),
),
child: Column(
mainAxisSize: MainAxisSize.min,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 28,
color: iconColor ?? UnionFlowColors.unionGreen,
),
const SizedBox(height: 8),
Text(
label,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textPrimary,
Icon(icon, size: 16, color: accentColor),
const SizedBox(width: 6),
Flexible(
child: Text(
label,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: accentColor,
),
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
textAlign: TextAlign.center,
),
],
),
@@ -73,7 +75,7 @@ class UnionActionGrid extends StatelessWidget {
children: [
for (int i = 0; i < actions.length; i++) ...[
Expanded(child: actions[i]),
if (i < actions.length - 1) const SizedBox(width: 12),
if (i < actions.length - 1) const SizedBox(width: 10),
],
],
);

View File

@@ -22,75 +22,87 @@ class UnionBalanceCard extends StatelessWidget {
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: UnionFlowColors.softShadow,
// Bordure dorée subtile en haut
border: const Border(
top: BorderSide(
color: UnionFlowColors.gold,
width: 3,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
decoration: BoxDecoration(
color: UnionFlowColors.surface,
border: Border.all(color: UnionFlowColors.border),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Label
Text(
label.toUpperCase(),
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textSecondary,
letterSpacing: 0.8,
),
),
const SizedBox(height: 8),
// Montant principal
Text(
amount,
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.w700,
color: UnionFlowColors.unionGreen,
height: 1.2,
),
),
// Trend (optionnel)
if (trend != null) ...[
const SizedBox(height: 10),
Row(
children: [
Icon(
isTrendPositive == true
? Icons.trending_up
: Icons.trending_down,
size: 16,
color: isTrendPositive == true
? UnionFlowColors.success
: UnionFlowColors.error,
),
const SizedBox(width: 4),
Text(
trend!,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: isTrendPositive == true
? UnionFlowColors.success
: UnionFlowColors.error,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(height: 2, color: UnionFlowColors.gold),
Padding(
padding: const EdgeInsets.fromLTRB(14, 10, 14, 12),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label.toUpperCase(),
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textSecondary,
letterSpacing: 0.8,
),
),
const SizedBox(height: 4),
Text(
amount,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w800,
color: UnionFlowColors.textPrimary,
height: 1,
letterSpacing: -0.5,
),
),
],
),
),
),
],
if (trend != null)
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
children: [
Icon(
isTrendPositive == true
? Icons.arrow_upward_rounded
: Icons.arrow_downward_rounded,
size: 11,
color: isTrendPositive == true
? UnionFlowColors.success
: UnionFlowColors.error,
),
const SizedBox(width: 2),
Text(
trend!,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: isTrendPositive == true
? UnionFlowColors.success
: UnionFlowColors.error,
),
),
],
),
const Text(
'ce mois',
style: TextStyle(fontSize: 9, color: UnionFlowColors.textTertiary),
),
],
),
],
),
),
],
],
),
),
),
);

View File

@@ -26,25 +26,18 @@ class UnionGlassCard extends StatelessWidget {
child: Container(
margin: margin,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius ?? 16),
borderRadius: BorderRadius.circular(borderRadius ?? 10),
border: Border.all(
color: Colors.white.withOpacity(0.2),
width: 1.5,
),
boxShadow: [
BoxShadow(
color: UnionFlowColors.unionGreen.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(borderRadius ?? 16),
borderRadius: BorderRadius.circular(borderRadius ?? 10),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
padding: padding ?? const EdgeInsets.all(20),
padding: padding ?? const EdgeInsets.all(10),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,

View File

@@ -77,11 +77,10 @@ class UnionLineChart extends StatelessWidget {
final effectiveGradientEnd = gradientEndColor ?? UnionFlowColors.unionGreen.withOpacity(0.0);
return Container(
padding: const EdgeInsets.all(20),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: UnionFlowColors.softShadow,
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -105,7 +104,7 @@ class UnionLineChart extends StatelessWidget {
),
),
],
const SizedBox(height: 20),
const SizedBox(height: 10),
// Chart
SizedBox(

View File

@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import '../tokens/unionflow_colors.dart';
/// Graphique circulaire UnionFlow - Pour afficher des répartitions
/// Graphique circulaire UnionFlow — petit donut compact avec légende latérale
/// Layout horizontal : donut 80×80 à gauche + légende à droite
/// Style uniforme avec les autres cards du design system (border + borderRadius)
class UnionPieChart extends StatelessWidget {
final List<PieChartSectionData> sections;
final String title;
@@ -19,47 +21,110 @@ class UnionPieChart extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Forcer compact : radius 14, pas de titre dans le donut
final compactSections = sections
.map((s) => s.copyWith(radius: 14, showTitle: false))
.toList();
return Container(
padding: const EdgeInsets.all(20),
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: UnionFlowColors.softShadow,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: UnionFlowColors.border),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textPrimary,
),
Row(
children: [
Text(
title,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: UnionFlowColors.textPrimary,
),
),
const Spacer(),
if (subtitle != null)
Text(
subtitle!,
style: const TextStyle(
fontSize: 10,
color: UnionFlowColors.textTertiary,
),
),
],
),
if (subtitle != null) ...[
const SizedBox(height: 4),
Text(
subtitle!,
style: const TextStyle(
fontSize: 11,
color: UnionFlowColors.textSecondary,
),
),
],
const SizedBox(height: 20),
const SizedBox(height: 10),
// Chart
SizedBox(
height: 180,
child: PieChart(
PieChartData(
sectionsSpace: 2,
centerSpaceRadius: centerSpaceRadius ?? 50,
sections: sections,
// Donut + légende côte à côte
Row(
children: [
// Petit donut
SizedBox(
width: 80,
height: 80,
child: PieChart(
PieChartData(
sectionsSpace: 2,
centerSpaceRadius: centerSpaceRadius ?? 22,
sections: compactSections,
),
),
),
),
const SizedBox(width: 14),
// Légende
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: sections.map((s) {
// Le title de la section peut contenir '\n' ex: '50%\nActifs'
final parts = s.title.split('\n');
final pct = parts.isNotEmpty ? parts[0] : '';
final label = parts.length > 1 ? parts[1] : s.title;
return Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: s.color,
shape: BoxShape.circle,
),
),
const SizedBox(width: 6),
Expanded(
child: Text(
label,
style: const TextStyle(
fontSize: 10,
color: UnionFlowColors.textSecondary,
),
overflow: TextOverflow.ellipsis,
),
),
Text(
pct,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w700,
color: s.color,
),
),
],
),
);
}).toList(),
),
),
],
),
],
),
@@ -79,8 +144,9 @@ class UnionPieChartSection {
return PieChartSectionData(
color: color,
value: value,
title: showTitle ? title : '',
title: title,
radius: radius,
showTitle: showTitle,
titleStyle: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,

View File

@@ -25,11 +25,10 @@ class UnionProgressCard extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(20),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: UnionFlowColors.softShadow,
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -43,7 +42,7 @@ class UnionProgressCard extends StatelessWidget {
color: UnionFlowColors.textPrimary,
),
),
const SizedBox(height: 12),
const SizedBox(height: 8),
// Progress bar
Stack(
@@ -69,13 +68,6 @@ class UnionProgressCard extends StatelessWidget {
],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: effectiveColor.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
),
),

View File

@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import '../tokens/unionflow_colors.dart';
/// Widget de statistique compacte avec icône et tendance
/// Widget de statistique compacte — style identique à _buildKpiCell du super admin
/// fond blanc, bordure gauche colorée, icône + valeur + label
/// [compact] réduit le padding vertical pour les grilles très plates
class UnionStatWidget extends StatelessWidget {
final String label;
final String value;
@@ -10,6 +12,9 @@ class UnionStatWidget extends StatelessWidget {
final String? trend;
final bool? isTrendUp;
/// Mode ultra-compact : padding vertical réduit, espacement minimal
final bool compact;
const UnionStatWidget({
super.key,
required this.label,
@@ -18,86 +23,65 @@ class UnionStatWidget extends StatelessWidget {
required this.color,
this.trend,
this.isTrendUp,
this.compact = false,
});
@override
Widget build(BuildContext context) {
final EdgeInsets pad = compact
? const EdgeInsets.symmetric(horizontal: 8, vertical: 5)
: const EdgeInsets.all(6);
return Container(
padding: const EdgeInsets.all(16),
padding: pad,
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: UnionFlowColors.softShadow,
border: Border(
left: BorderSide(
color: color,
width: 4,
),
),
borderRadius: BorderRadius.circular(10),
border: Border(left: BorderSide(color: color, width: 3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Icon
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, size: 20, color: color),
),
const SizedBox(height: 12),
// Value
Text(
value,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w700,
color: color,
),
),
const SizedBox(height: 4),
// Label
Text(
label,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textSecondary,
),
),
// Trend
if (trend != null) ...[
const SizedBox(height: 8),
Row(
children: [
Icon(
isTrendUp == true
? Icons.trending_up
: Icons.trending_down,
size: 14,
color: isTrendUp == true
? UnionFlowColors.success
: UnionFlowColors.error,
),
const SizedBox(width: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(icon, size: 13, color: color),
if (trend != null) ...[
const Spacer(),
Text(
trend!,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: isTrendUp == true
? UnionFlowColors.success
: UnionFlowColors.error,
fontSize: 8,
fontWeight: FontWeight.w700,
color: color,
),
),
],
],
),
SizedBox(height: compact ? 2 : 4),
Text(
value,
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w800,
color: color,
letterSpacing: -0.3,
height: 1,
),
],
),
SizedBox(height: compact ? 1 : 2),
Text(
label,
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.w500,
color: UnionFlowColors.textSecondary,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
);

View File

@@ -153,11 +153,10 @@ class UnionTransactionCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: UnionFlowColors.softShadow,
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -35,14 +35,7 @@ class UnionUnifiedAccountCard extends StatelessWidget {
UnionFlowColors.unionGreen.withOpacity(0.85),
],
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: UnionFlowColors.unionGreen.withOpacity(0.35),
offset: const Offset(0, 10),
blurRadius: 20,
),
],
borderRadius: BorderRadius.circular(12),
),
child: Stack(
children: [
@@ -58,7 +51,7 @@ class UnionUnifiedAccountCard extends StatelessWidget {
),
Padding(
padding: const EdgeInsets.all(24),
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -95,7 +88,7 @@ class UnionUnifiedAccountCard extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.white30),
),
child: Text(
@@ -111,8 +104,8 @@ class UnionUnifiedAccountCard extends StatelessWidget {
],
),
const SizedBox(height: 32),
const SizedBox(height: 16),
// Solde Total Disponible
const Text(
'Solde Total Disponible',
@@ -129,15 +122,15 @@ class UnionUnifiedAccountCard extends StatelessWidget {
soldeTotal,
style: const TextStyle(
color: Colors.white,
fontSize: 36,
fontSize: 24,
fontWeight: FontWeight.w800,
letterSpacing: -0.5,
),
),
),
const SizedBox(height: 32),
const SizedBox(height: 16),
// Grille de détails
Row(
children: [
@@ -147,8 +140,8 @@ class UnionUnifiedAccountCard extends StatelessWidget {
],
),
const SizedBox(height: 20),
const SizedBox(height: 10),
// Barre d'engagement
Column(
crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -0,0 +1,120 @@
import 'package:flutter/material.dart';
import '../tokens/unionflow_colors.dart';
/// Carte identité utilisateur — header uniforme pour tous les dashboards
/// Gradient coloré + initiales + nom + sous-titre + badge rôle
class UserIdentityCard extends StatelessWidget {
final String initials;
final String name;
final String subtitle;
final String badgeLabel;
final Gradient gradient;
final Color accentColor;
/// true = fond clair → texte sombre, badge avec fond coloré
/// false (défaut) = fond sombre → texte blanc, badge blanc + texte coloré
final bool lightBackground;
/// Afficher la bordure supérieure colorée (accentColor)
final bool showTopBorder;
const UserIdentityCard({
super.key,
required this.initials,
required this.name,
required this.subtitle,
required this.badgeLabel,
required this.gradient,
required this.accentColor,
this.lightBackground = false,
this.showTopBorder = true,
});
@override
Widget build(BuildContext context) {
final textColor =
lightBackground ? UnionFlowColors.textPrimary : Colors.white;
final subtitleColor = lightBackground
? UnionFlowColors.textSecondary
: Colors.white.withOpacity(0.85);
final avatarBg = lightBackground
? accentColor.withOpacity(0.15)
: Colors.white.withOpacity(0.25);
final avatarTextColor = lightBackground ? accentColor : Colors.white;
final badgeBg = lightBackground ? accentColor : Colors.white;
final badgeTextColor = lightBackground ? Colors.white : accentColor;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
gradient: gradient,
borderRadius: BorderRadius.circular(10),
border: showTopBorder
? Border(top: BorderSide(color: accentColor, width: 3))
: null,
boxShadow: showTopBorder
? [
BoxShadow(
color: accentColor.withOpacity(0.25),
blurRadius: 10,
offset: const Offset(0, 3),
)
]
: null,
),
child: Row(
children: [
CircleAvatar(
radius: 20,
backgroundColor: avatarBg,
child: Text(
initials,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: avatarTextColor,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: textColor,
),
),
const SizedBox(height: 3),
Text(
subtitle,
style: TextStyle(fontSize: 11, color: subtitleColor),
),
],
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: badgeBg,
borderRadius: BorderRadius.circular(4),
),
child: Text(
badgeLabel,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.w800,
color: badgeTextColor,
letterSpacing: 0.8,
),
),
),
],
),
);
}
}

View File

@@ -27,7 +27,7 @@ class AppThemeSophisticated {
// Couleurs principales
colorScheme: _lightColorScheme,
// Typographie
// Typographie (Playfair Display display + Inter body)
textTheme: _textTheme,
// Configuration de l'AppBar
@@ -85,19 +85,34 @@ class AppThemeSophisticated {
);
}
/// Thème sombre (suit le système ou sélection manuelle)
/// Thème sombre — Vert Ardoise (#1A2E1A)
static ThemeData get darkTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.dark(
primary: ColorTokens.primary,
onPrimary: Colors.white,
surface: const Color(0xFF121212),
onSurface: Colors.white,
error: ColorTokens.error,
colorScheme: const ColorScheme.dark(
primary: Color(0xFF4CAF50), // Vert clair sur fond sombre
onPrimary: Color(0xFF003908),
primaryContainer: Color(0xFF1E3A1E),
onPrimaryContainer: Color(0xFFB9F0B9),
secondary: Color(0xFFA5D6A7),
onSecondary: Color(0xFF002106),
surface: Color(0xFF1A2E1A), // Vert ardoise
onSurface: Color(0xFFE0F2E0),
surfaceContainerHighest: Color(0xFF243824),
onSurfaceVariant: Color(0xFF90B890),
error: Color(0xFFEF4444),
onError: Colors.white,
outline: Color(0xFF3A5E3A),
shadow: Color(0xFF0F1A0F),
),
scaffoldBackgroundColor: const Color(0xFF0F1A0F),
appBarTheme: const AppBarTheme(
elevation: 0,
backgroundColor: Colors.transparent,
foregroundColor: Color(0xFFE0F2E0),
surfaceTintColor: Colors.transparent,
),
scaffoldBackgroundColor: const Color(0xFF121212),
);
}
@@ -154,27 +169,27 @@ class AppThemeSophisticated {
// THÈME TYPOGRAPHIQUE
// ═══════════════════════════════════════════════════════════════════════════
static const TextTheme _textTheme = TextTheme(
// Display styles
static TextTheme get _textTheme => TextTheme(
// Display styles — Playfair Display (GoogleFonts, non-const)
displayLarge: TypographyTokens.displayLarge,
displayMedium: TypographyTokens.displayMedium,
displaySmall: TypographyTokens.displaySmall,
// Headline styles
headlineLarge: TypographyTokens.headlineLarge,
headlineMedium: TypographyTokens.headlineMedium,
headlineSmall: TypographyTokens.headlineSmall,
// Title styles
titleLarge: TypographyTokens.titleLarge,
titleMedium: TypographyTokens.titleMedium,
titleSmall: TypographyTokens.titleSmall,
// Label styles
labelLarge: TypographyTokens.labelLarge,
labelMedium: TypographyTokens.labelMedium,
labelSmall: TypographyTokens.labelSmall,
// Body styles
bodyLarge: TypographyTokens.bodyLarge,
bodyMedium: TypographyTokens.bodyMedium,

View File

@@ -1,34 +1,35 @@
import 'package:flutter/material.dart';
/// UnionFlow Mobile App - Couleurs Globales (Strict DRY)
/// Palette principale: Vert, Blanc (Jour), Noir (Nuit).
/// UnionFlow Mobile App - Couleurs Globales (DRY)
/// Palette : Vert Forêt (Jour) / Vert Ardoise (Nuit)
class AppColors {
// --- Branding ---
static const Color primaryGreen = Color(0xFF17BF63); // Vert vibrant style social (Fb/Tw) - Corrigé.
static const Color brandGreen = Color(0xFF2E7D32); // Vert professionnel et lisible
static const Color primaryGreen = Color(0xFF2E7D32); // Vert forêt professionnel
static const Color brandGreen = Color(0xFF1B5E20); // Vert foncé / gradient top
static const Color brandGreenLight = Color(0xFF4CAF50); // Vert d'accentuation
static const Color brandMint = Color(0xFFA5D6A7); // Vert menthe / secondaire
// --- Mode Jour (Light) ---
static const Color lightBackground = Color(0xFFFFFFFF); // Blanc pur
static const Color lightSurface = Color(0xFFF5F8FA); // Gris extrêmement léger pour séparer les cards
static const Color lightBorder = Color(0xFFE1E8ED); // Bordures style Twitter
// --- Mode Nuit (Dark OLED) ---
static const Color darkBackground = Color(0xFF000000); // Noir pur pour OLED
static const Color darkSurface = Color(0xFF15202B); // Gris sombre typique Twitter/Fb Dark
static const Color darkBorder = Color(0xFF38444D); // Bordure discrète sombre
static const Color lightBackground = Color(0xFFF1F8E9); // Teinte verte très légère
static const Color lightSurface = Color(0xFFFFFFFF); // Blanc pur pour les cartes
static const Color lightBorder = Color(0xFFC8E6C9); // Bordure vert pâle
// --- Mode Nuit (Dark) ---
static const Color darkBackground = Color(0xFF0F1A0F); // Vert noir profond
static const Color darkSurface = Color(0xFF1A2E1A); // Vert ardoise
static const Color darkBorder = Color(0xFF3A5E3A); // Bordure sombre
// --- Texte ---
static const Color textPrimaryLight = Color(0xFF14171A); // Presque noir
static const Color textSecondaryLight = Color(0xFF657786); // Gris texte
static const Color textPrimaryDark = Color(0xFFE1E8ED); // Presque blanc
static const Color textSecondaryDark = Color(0xFF8899A6); // Gris clair nuit
static const Color textPrimaryLight = Color(0xFF1C2B1C); // Vert très foncé / quasi noir
static const Color textSecondaryLight = Color(0xFF4E6B4E); // Vert gris moyen
static const Color textPrimaryDark = Color(0xFFE0F2E0); // Blanc verdâtre
static const Color textSecondaryDark = Color(0xFF90B890); // Vert gris clair
// --- Sémantique (Succès, Erreur, Info) ---
static const Color error = Color(0xFFE0245E); // Rouge vif
static const Color success = Color(0xFF17BF63); // Vert validation
static const Color warning = Color(0xFFFFAD1F); // Orange
static const Color info = Color(0xFF1DA1F2); // Bleu info
// --- Sémantique ---
static const Color error = Color(0xFFDC2626);
static const Color success = Color(0xFF2E7D32);
static const Color warning = Color(0xFFF59E0B);
static const Color info = Color(0xFF0288D1);
// --- Utilitaires ---
static const Color transparent = Colors.transparent;

View File

@@ -1,47 +1,162 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
/// UnionFlow Mobile App - Typographie Globale (Ultra Minimaliste)
/// RÈGLE : AUCUN gros titre. Tailles limitées entre 10px et 14px pour maximiser l'information.
/// UnionFlow Mobile App - Typographie Globale
/// Roboto (Google Fonts) — cohérence cross-platform
class AppTypography {
static const String _fontFamily = 'Roboto'; // Peut être changé pour 'Inter' si ajouté au pubspec.yaml
static const String _fontFamily = 'Roboto';
// --- Titres (Max 14px) ---
static const TextStyle headerSmall = TextStyle(
// --- Display / Titres principaux (Roboto via GoogleFonts) ---
static TextStyle get displayLarge => GoogleFonts.roboto(
fontSize: 32.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.5,
height: 1.2,
);
static TextStyle get displayMedium => GoogleFonts.roboto(
fontSize: 28.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.25,
height: 1.25,
);
static TextStyle get displaySmall => GoogleFonts.roboto(
fontSize: 24.0,
fontWeight: FontWeight.w600,
letterSpacing: 0,
height: 1.3,
);
// --- Titres de sections (Inter SemiBold) ---
static const TextStyle headerLarge = TextStyle(
fontFamily: _fontFamily,
fontSize: 14.0,
fontWeight: FontWeight.w700, // Bold
fontSize: 22.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
height: 1.27,
);
// --- Corps de texte (Max 12px) ---
/// Alias historique — conservé pour compatibilité
static const TextStyle headerSmall = TextStyle(
fontFamily: _fontFamily,
fontSize: 18.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
height: 1.3,
);
static const TextStyle titleMedium = TextStyle(
fontFamily: _fontFamily,
fontSize: 16.0,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
height: 1.5,
);
static const TextStyle titleSmall = TextStyle(
fontFamily: _fontFamily,
fontSize: 14.0,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
height: 1.43,
);
// --- Corps de texte ---
static const TextStyle bodyLarge = TextStyle(
fontFamily: _fontFamily,
fontSize: 16.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.3,
height: 1.5,
);
static const TextStyle bodyMedium = TextStyle(
fontFamily: _fontFamily,
fontSize: 14.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.25,
height: 1.43,
);
/// Alias historique — conservé pour compatibilité
static const TextStyle bodyTextSmall = TextStyle(
fontFamily: _fontFamily,
fontSize: 12.0,
fontWeight: FontWeight.w400, // Regular
fontSize: 13.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.2,
height: 1.4,
);
// --- Boutons et Actions (Max 13px) ---
// --- Actions et boutons ---
static const TextStyle actionText = TextStyle(
fontFamily: _fontFamily,
fontSize: 13.0,
fontWeight: FontWeight.w600, // SemiBold
fontSize: 15.0,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
);
// --- Sous-titres, dates, labels (Max 11px) ---
static const TextStyle subtitleSmall = TextStyle(
static const TextStyle buttonLabel = TextStyle(
fontFamily: _fontFamily,
fontSize: 11.0,
fontWeight: FontWeight.w300, // Light
letterSpacing: 0.2,
fontSize: 16.0,
fontWeight: FontWeight.w700,
letterSpacing: 0.1,
);
// --- Badges, Piles, Métriques très denses (Max 10px) ---
// --- Sous-titres et labels ---
/// Alias historique — conservé pour compatibilité
static const TextStyle subtitleSmall = TextStyle(
fontFamily: _fontFamily,
fontSize: 12.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.2,
height: 1.4,
);
static const TextStyle labelMedium = TextStyle(
fontFamily: _fontFamily,
fontSize: 12.0,
fontWeight: FontWeight.w500,
letterSpacing: 0.4,
height: 1.33,
);
// --- Badges, métriques denses ---
/// Alias historique — conservé pour compatibilité
static const TextStyle badgeText = TextStyle(
fontFamily: _fontFamily,
fontSize: 10.0,
fontWeight: FontWeight.w500, // Medium
fontSize: 11.0,
fontWeight: FontWeight.w500,
letterSpacing: 0.3,
);
static const TextStyle caption = TextStyle(
fontFamily: _fontFamily,
fontSize: 11.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.4,
height: 1.45,
);
// --- Navigation ---
static const TextStyle navLabel = TextStyle(
fontFamily: _fontFamily,
fontSize: 11.0,
fontWeight: FontWeight.w500,
letterSpacing: 0.3,
);
static const TextStyle navLabelSelected = TextStyle(
fontFamily: _fontFamily,
fontSize: 11.0,
fontWeight: FontWeight.w700,
letterSpacing: 0.3,
);
}

View File

@@ -1,11 +1,8 @@
/// Design Tokens - Couleurs UnionFlow
///
/// Palette de couleurs Bleu Roi + Bleu Pétrole
/// Inspirée des tendances UI/UX 2024-2025
/// Basée sur les principes de Material Design 3
///
/// MODE JOUR: Bleu Roi (#4169E1) - Royal Blue
/// MODE NUIT: Bleu Pétrole (#2C5F6F) - Petroleum Blue
/// Palette Vert Zen / Santé — Professionnelle et apaisante
/// MODE JOUR : Vert Forêt (#2E7D32) — Forest Green
/// MODE NUIT : Vert Ardoise (#1A2E1A) — Slate Green
library color_tokens;
import 'package:flutter/material.dart';
@@ -15,180 +12,176 @@ class ColorTokens {
ColorTokens._();
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS PRIMAIRES - MODE JOUR (Bleu Roi)
// COULEURS PRIMAIRES - MODE JOUR (Vert Forêt)
// ═══════════════════════════════════════════════════════════════════════════
/// Couleur primaire principale - Bleu Roi (Royal Blue)
static const Color primary = Color(0xFF4169E1); // Bleu roi
static const Color primaryLight = Color(0xFF6B8EF5); // Bleu roi clair
static const Color primaryDark = Color(0xFF2952C8); // Bleu roi sombre
static const Color primaryContainer = Color(0xFFE3ECFF); // Container bleu roi
static const Color onPrimary = Color(0xFFFFFFFF); // Texte sur primaire (blanc)
static const Color onPrimaryContainer = Color(0xFF001A41); // Texte sur container
/// Couleur primaire principale - Vert Forêt
static const Color primary = Color(0xFF2E7D32);
static const Color primaryLight = Color(0xFF4CAF50);
static const Color primaryDark = Color(0xFF1B5E20);
static const Color primaryContainer = Color(0xFFE8F5E9);
static const Color onPrimary = Color(0xFFFFFFFF);
static const Color onPrimaryContainer = Color(0xFF003908);
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS PRIMAIRES - MODE NUIT (Bleu Pétrole)
// COULEURS PRIMAIRES - MODE NUIT (Vert Ardoise)
// ═══════════════════════════════════════════════════════════════════════════
/// Couleur primaire mode nuit - Bleu Pétrole
static const Color primaryDarkMode = Color(0xFF2C5F6F); // Bleu pétrole
static const Color primaryLightDarkMode = Color(0xFF3D7A8C); // Bleu pétrole clair
static const Color primaryDarkDarkMode = Color(0xFF1B4D5C); // Bleu pétrole sombre
static const Color primaryContainerDarkMode = Color(0xFF1E3A44); // Container mode nuit
static const Color onPrimaryDarkMode = Color(0xFFE5E7EB); // Texte sur primaire (gris clair)
static const Color primaryDarkMode = Color(0xFF1A2E1A);
static const Color primaryLightDarkMode = Color(0xFF2E4D2E);
static const Color primaryDarkDarkMode = Color(0xFF0F1A0F);
static const Color primaryContainerDarkMode = Color(0xFF1E3A1E);
static const Color onPrimaryDarkMode = Color(0xFFE0F2E0);
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS SECONDAIRES - Indigo Moderne
// COULEURS SECONDAIRES - Vert Menthe / Sauge
// ═══════════════════════════════════════════════════════════════════════════
static const Color secondary = Color(0xFF6366F1); // Indigo moderne
static const Color secondaryLight = Color(0xFF8B8FF6); // Indigo clair
static const Color secondaryDark = Color(0xFF4F46E5); // Indigo sombre
static const Color secondaryContainer = Color(0xFFE0E7FF); // Container indigo
static const Color secondary = Color(0xFF66BB6A);
static const Color secondaryLight = Color(0xFFA5D6A7);
static const Color secondaryDark = Color(0xFF388E3C);
static const Color secondaryContainer = Color(0xFFC8E6C9);
static const Color onSecondary = Color(0xFFFFFFFF);
static const Color onSecondaryContainer = Color(0xFF1E1B3A);
static const Color onSecondaryContainer = Color(0xFF002106);
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS TERTIAIRES - Vert Émeraude
// COULEURS TERTIAIRES - Vert Lime / Accent
// ═══════════════════════════════════════════════════════════════════════════
static const Color tertiary = Color(0xFF10B981); // Vert émeraude
static const Color tertiaryLight = Color(0xFF34D399); // Vert clair
static const Color tertiaryDark = Color(0xFF059669); // Vert sombre
static const Color tertiaryContainer = Color(0xFFD1FAE5); // Container vert
static const Color tertiary = Color(0xFF8BC34A);
static const Color tertiaryLight = Color(0xFFAED581);
static const Color tertiaryDark = Color(0xFF558B2F);
static const Color tertiaryContainer = Color(0xFFDCEDC8);
static const Color onTertiary = Color(0xFFFFFFFF);
static const Color onTertiaryContainer = Color(0xFF002114);
static const Color onTertiaryContainer = Color(0xFF1B3A00);
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS NEUTRES - MODE JOUR
// ═══════════════════════════════════════════════════════════════════════════
static const Color surface = Color(0xFFFFFFFF); // Surface principale (blanc)
static const Color surfaceVariant = Color(0xFFF8F9FA); // Surface variante (gris très clair)
static const Color surfaceContainer = Color(0xFFFFFFFF); // Container surface
static const Color surfaceContainerHigh = Color(0xFFF8F9FA); // Container élevé
static const Color surfaceContainerHighest = Color(0xFFE5E7EB); // Container max
static const Color background = Color(0xFFF8F9FA); // Background général
static const Color surface = Color(0xFFFFFFFF);
static const Color surfaceVariant = Color(0xFFF1F8E9);
static const Color surfaceContainer = Color(0xFFFFFFFF);
static const Color surfaceContainerHigh = Color(0xFFF1F8E9);
static const Color surfaceContainerHighest = Color(0xFFDCEDC8);
static const Color background = Color(0xFFF1F8E9);
static const Color onSurface = Color(0xFF1F2937); // Texte principal (gris très foncé)
static const Color onSurfaceVariant = Color(0xFF6B7280); // Texte secondaire (gris moyen)
static const Color textSecondary = Color(0xFF6B7280); // Texte secondaire (alias)
static const Color outline = Color(0xFFD1D5DB); // Bordures
static const Color outlineVariant = Color(0xFFE5E7EB); // Bordures claires
static const Color onSurface = Color(0xFF1C2B1C);
static const Color onSurfaceVariant = Color(0xFF4E6B4E);
static const Color textSecondary = Color(0xFF4E6B4E);
static const Color outline = Color(0xFFC8E6C9);
static const Color outlineVariant = Color(0xFFDCEDC8);
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS NEUTRES - MODE NUIT
// ═══════════════════════════════════════════════════════════════════════════
static const Color surfaceDarkMode = Color(0xFF1E1E1E); // Surface principale (gris très sombre)
static const Color surfaceVariantDarkMode = Color(0xFF2C2C2C); // Surface variante
static const Color backgroundDarkMode = Color(0xFF121212); // Background général (noir profond)
static const Color surfaceDarkMode = Color(0xFF1A2E1A);
static const Color surfaceVariantDarkMode = Color(0xFF243824);
static const Color backgroundDarkMode = Color(0xFF0F1A0F);
static const Color onSurfaceDarkMode = Color(0xFFE5E7EB); // Texte principal (gris très clair)
static const Color onSurfaceVariantDarkMode = Color(0xFF9CA3AF); // Texte secondaire (gris moyen)
static const Color outlineDarkMode = Color(0xFF4B5563); // Bordures mode nuit
static const Color onSurfaceDarkMode = Color(0xFFE0F2E0);
static const Color onSurfaceVariantDarkMode = Color(0xFF90B890);
static const Color outlineDarkMode = Color(0xFF3A5E3A);
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS SÉMANTIQUES - États et feedback
// COULEURS SÉMANTIQUES
// ═══════════════════════════════════════════════════════════════════════════
/// Couleurs de succès
static const Color success = Color(0xFF10B981); // Vert succès
static const Color successLight = Color(0xFF34D399); // Vert clair
static const Color successDark = Color(0xFF059669); // Vert sombre
static const Color successContainer = Color(0xFFECFDF5); // Container succès
static const Color success = Color(0xFF2E7D32);
static const Color successLight = Color(0xFF4CAF50);
static const Color successDark = Color(0xFF1B5E20);
static const Color successContainer = Color(0xFFE8F5E9);
static const Color onSuccess = Color(0xFFFFFFFF);
static const Color onSuccessContainer = Color(0xFF002114);
static const Color onSuccessContainer = Color(0xFF003908);
/// Couleurs d'erreur
static const Color error = Color(0xFFDC2626); // Rouge erreur
static const Color errorLight = Color(0xFFEF4444); // Rouge clair
static const Color errorDark = Color(0xFFB91C1C); // Rouge sombre
static const Color errorContainer = Color(0xFFFEF2F2); // Container erreur
static const Color error = Color(0xFFDC2626);
static const Color errorLight = Color(0xFFEF4444);
static const Color errorDark = Color(0xFFB91C1C);
static const Color errorContainer = Color(0xFFFEF2F2);
static const Color onError = Color(0xFFFFFFFF);
static const Color onErrorContainer = Color(0xFF410002);
/// Couleurs d'avertissement
static const Color warning = Color(0xFFF59E0B); // Orange avertissement
static const Color warningLight = Color(0xFFFBBF24); // Orange clair
static const Color warningDark = Color(0xFFD97706); // Orange sombre
static const Color warningContainer = Color(0xFFFEF3C7); // Container avertissement
static const Color warning = Color(0xFFF59E0B);
static const Color warningLight = Color(0xFFFBBF24);
static const Color warningDark = Color(0xFFD97706);
static const Color warningContainer = Color(0xFFFEF3C7);
static const Color onWarning = Color(0xFFFFFFFF);
static const Color onWarningContainer = Color(0xFF2D1B00);
/// Couleurs d'information
static const Color info = Color(0xFF0EA5E9); // Bleu info
static const Color infoLight = Color(0xFF38BDF8); // Bleu clair
static const Color infoDark = Color(0xFF0284C7); // Bleu sombre
static const Color infoContainer = Color(0xFFE0F2FE); // Container info
static const Color info = Color(0xFF0288D1);
static const Color infoLight = Color(0xFF29B6F6);
static const Color infoDark = Color(0xFF01579B);
static const Color infoContainer = Color(0xFFE1F5FE);
static const Color onInfo = Color(0xFFFFFFFF);
static const Color onInfoContainer = Color(0xFF001D36);
// ═══════════════════════════════════════════════════════════════════════════
// COULEURS SPÉCIALISÉES - Interface avancée
// COULEURS DE NAVIGATION
// ═══════════════════════════════════════════════════════════════════════════
/// Couleurs de navigation - Mode Jour
static const Color navigationBackground = Color(0xFFFFFFFF);
static const Color navigationSelected = Color(0xFF4169E1); // Bleu roi
static const Color navigationUnselected = Color(0xFF6B7280);
static const Color navigationIndicator = Color(0xFF4169E1); // Bleu roi
static const Color navigationSelected = Color(0xFF2E7D32);
static const Color navigationUnselected = Color(0xFF4E6B4E);
static const Color navigationIndicator = Color(0xFFE8F5E9);
/// Couleurs de navigation - Mode Nuit
static const Color navigationBackgroundDarkMode = Color(0xFF1E1E1E);
static const Color navigationSelectedDarkMode = Color(0xFF2C5F6F); // Bleu pétrole
static const Color navigationUnselectedDarkMode = Color(0xFF9CA3AF);
static const Color navigationIndicatorDarkMode = Color(0xFF2C5F6F); // Bleu pétrole
static const Color navigationBackgroundDarkMode = Color(0xFF1A2E1A);
static const Color navigationSelectedDarkMode = Color(0xFFA5D6A7);
static const Color navigationUnselectedDarkMode = Color(0xFF90B890);
static const Color navigationIndicatorDarkMode = Color(0xFF2E4D2E);
/// Couleurs d'élévation et ombres
static const Color shadow = Color(0x1A000000); // Ombre légère
static const Color shadowMedium = Color(0x33000000); // Ombre moyenne
static const Color shadowHigh = Color(0x4D000000); // Ombre forte
// ═══════════════════════════════════════════════════════════════════════════
// OMBRES ET EFFETS
// ═══════════════════════════════════════════════════════════════════════════
/// Couleurs de glassmorphism (tendance 2024-2025)
static const Color glassBackground = Color(0x80FFFFFF); // Fond verre
static const Color glassBorder = Color(0x33FFFFFF); // Bordure verre
static const Color glassOverlay = Color(0x0DFFFFFF); // Overlay verre
static const Color shadow = Color(0x1A1C2B1C);
static const Color shadowMedium = Color(0x331C2B1C);
static const Color shadowHigh = Color(0x4D1C2B1C);
static const Color glassBackground = Color(0x80FFFFFF);
static const Color glassBorder = Color(0x33FFFFFF);
static const Color glassOverlay = Color(0x0DFFFFFF);
// ═══════════════════════════════════════════════════════════════════════════
// GRADIENTS
// ═══════════════════════════════════════════════════════════════════════════
/// Couleurs de gradient - Mode Jour (Bleu Roi)
static const List<Color> primaryGradient = [
Color(0xFF4169E1), // Bleu roi
Color(0xFF6B8EF5), // Bleu roi clair
Color(0xFF1B5E20),
Color(0xFF2E7D32),
Color(0xFF388E3C),
];
/// Couleurs de gradient - Mode Nuit (Bleu Pétrole)
static const List<Color> primaryGradientDarkMode = [
Color(0xFF2C5F6F), // Bleu pétrole
Color(0xFF3D7A8C), // Bleu pétrole clair
Color(0xFF0F1A0F),
Color(0xFF1A2E1A),
Color(0xFF243824),
];
static const List<Color> secondaryGradient = [
Color(0xFF6366F1), // Indigo
Color(0xFF8B8FF6), // Indigo clair
Color(0xFF388E3C),
Color(0xFF66BB6A),
];
static const List<Color> successGradient = [
Color(0xFF10B981), // Vert émeraude
Color(0xFF34D399), // Vert clair
Color(0xFF2E7D32),
Color(0xFF4CAF50),
];
// ═══════════════════════════════════════════════════════════════════════════
// MÉTHODES UTILITAIRES
// ═══════════════════════════════════════════════════════════════════════════
/// Obtient une couleur avec opacité
static Color withOpacity(Color color, double opacity) {
return color.withOpacity(opacity);
}
/// Obtient une couleur plus claire
static Color lighten(Color color, [double amount = 0.1]) {
final hsl = HSLColor.fromColor(color);
final lightness = (hsl.lightness + amount).clamp(0.0, 1.0);
return hsl.withLightness(lightness).toColor();
}
/// Obtient une couleur plus sombre
static Color darken(Color color, [double amount = 0.1]) {
final hsl = HSLColor.fromColor(color);
final lightness = (hsl.lightness - amount).clamp(0.0, 1.0);

View File

@@ -5,6 +5,7 @@
library typography_tokens;
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'color_tokens.dart';
/// Tokens typographiques - Système de texte moderne
@@ -15,12 +16,12 @@ class TypographyTokens {
// FAMILLES DE POLICES
// ═══════════════════════════════════════════════════════════════════════════
/// Police principale - Inter (moderne et lisible)
static const String primaryFontFamily = 'Inter';
/// Police secondaire - SF Pro Display (élégante)
static const String secondaryFontFamily = 'SF Pro Display';
/// Police principale - Roboto (Google Fonts, cross-platform)
static const String primaryFontFamily = 'Roboto';
/// Police display - Roboto (Google Fonts, titres)
static const String displayFontFamily = 'Roboto';
/// Police monospace - JetBrains Mono (code et données)
static const String monospaceFontFamily = 'JetBrains Mono';
@@ -28,33 +29,30 @@ class TypographyTokens {
// ÉCHELLE TYPOGRAPHIQUE - Basée sur Material Design 3
// ═══════════════════════════════════════════════════════════════════════════
/// Display - Titres principaux et héros
static const TextStyle displayLarge = TextStyle(
fontFamily: primaryFontFamily,
fontSize: 57.0,
fontWeight: FontWeight.w400,
letterSpacing: -0.25,
height: 1.12,
color: ColorTokens.onSurface,
);
static const TextStyle displayMedium = TextStyle(
fontFamily: primaryFontFamily,
fontSize: 45.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.0,
height: 1.16,
color: ColorTokens.onSurface,
);
static const TextStyle displaySmall = TextStyle(
fontFamily: primaryFontFamily,
fontSize: 36.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.0,
height: 1.22,
color: ColorTokens.onSurface,
);
/// Display Titres principaux (Roboto via GoogleFonts)
static TextStyle get displayLarge => GoogleFonts.roboto(
fontSize: 32.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.5,
height: 1.2,
color: ColorTokens.onSurface,
);
static TextStyle get displayMedium => GoogleFonts.roboto(
fontSize: 28.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.25,
height: 1.25,
color: ColorTokens.onSurface,
);
static TextStyle get displaySmall => GoogleFonts.roboto(
fontSize: 24.0,
fontWeight: FontWeight.w600,
letterSpacing: 0.0,
height: 1.3,
color: ColorTokens.onSurface,
);
/// Headline - Titres de sections
static const TextStyle headlineLarge = TextStyle(

View File

@@ -1,9 +1,5 @@
library unionflow_design_system;
// ═══════════════════════════════════════════════════════════════════════════
// IMPORTS de base pour le Design System
// ═══════════════════════════════════════════════════════════════════════════
import 'package:flutter/material.dart';
import 'tokens/app_colors.dart';
import 'tokens/app_typography.dart';
@@ -20,54 +16,83 @@ export 'theme/app_theme.dart';
export 'components/components.dart';
// ═══════════════════════════════════════════════════════════════════════════
// COMPATIBILITÉ - Shims pour les anciens tokens (Migration progressive)
// SHIMS DE COMPATIBILITÉ — Migration progressive vers design system unifié
// ═══════════════════════════════════════════════════════════════════════════
/// Shim de compatibilité pour ColorTokens
/// Shim ColorTokens — palette Vert Forêt/Ardoise
class ColorTokens {
static const Color primary = AppColors.primaryGreen;
static const Color primaryContainer = AppColors.lightSurface;
// Primaires
static const Color primary = AppColors.primaryGreen; // #2E7D32
static const Color primaryLight = AppColors.brandGreenLight; // #4CAF50
static const Color primaryDark = AppColors.brandGreen; // #1B5E20
static const Color primaryContainer = Color(0xFFE8F5E9);
static const Color onPrimary = Colors.white;
static const Color onPrimaryContainer = AppColors.textPrimaryLight;
static const Color secondary = AppColors.brandGreen;
static const Color secondaryContainer = AppColors.lightSurface;
// Secondaires
static const Color secondary = AppColors.brandGreenLight;
static const Color secondaryContainer = Color(0xFFC8E6C9);
static const Color onSecondary = Colors.white;
static const Color tertiary = AppColors.brandGreenLight;
static const Color tertiaryContainer = AppColors.lightSurface;
static const Color onSecondaryContainer = AppColors.textPrimaryLight;
// Tertiaires
static const Color tertiary = AppColors.brandMint;
static const Color tertiaryContainer = Color(0xFFDCEDC8);
static const Color onTertiary = Colors.white;
// Surfaces
static const Color surface = AppColors.lightSurface;
static const Color surfaceVariant = AppColors.lightSurface;
static const Color surfaceVariant = AppColors.lightBackground;
static const Color surfaceContainer = AppColors.lightSurface;
static const Color background = AppColors.lightBackground;
static const Color onSurface = AppColors.textPrimaryLight;
static const Color onSurfaceVariant = AppColors.textSecondaryLight;
static const Color outline = AppColors.lightBorder;
static const Color outlineVariant = AppColors.lightBorder;
static const Color outlineVariant = Color(0xFFDCEDC8);
// Erreur / succès
static const Color error = AppColors.error;
static const Color onError = Colors.white;
static const Color errorContainer = Color(0xFFFEF2F2);
static const Color success = AppColors.success;
static const Color onSuccess = Colors.white;
static const Color info = Color(0xFF2196F3);
static const Color warning = Color(0xFFFFC107);
static const Color shadow = Color(0x1A000000);
static const Color warning = AppColors.warning;
static const Color info = AppColors.info;
// Navigation
static const Color navigationBackground = AppColors.lightSurface;
static const Color navigationSelected = AppColors.primaryGreen;
static const Color navigationUnselected = AppColors.textSecondaryLight;
static const Color navigationIndicator = Color(0xFFE8F5E9);
// Ombres
static const Color shadow = Color(0x1A1C2B1C);
static const Color shadowMedium = Color(0x331C2B1C);
// Verre / glassmorphism
static const Color glassBackground = Color(0x80FFFFFF);
static const Color glassBorder = Color(0x33FFFFFF);
// Gradients
static const List<Color> primaryGradient = [
AppColors.brandGreen,
AppColors.primaryGreen,
AppColors.brandGreenLight,
Color(0xFF388E3C),
];
}
/// Shim de compatibilité pour ShadowTokens
/// Shim ShadowTokens
class ShadowTokens {
static const List<BoxShadow> sm = [
BoxShadow(
color: Color(0x1A000000),
color: Color(0x1A1C2B1C),
blurRadius: 4,
offset: Offset(0, 2),
),
];
static const List<BoxShadow> md = [
BoxShadow(
color: Color(0x26000000),
color: Color(0x261C2B1C),
blurRadius: 8,
offset: Offset(0, 4),
),
@@ -75,33 +100,58 @@ class ShadowTokens {
static const List<BoxShadow> primary = md;
}
/// Shim de compatibilité pour RadiusTokens
/// Shim RadiusTokens
class RadiusTokens {
static const double sm = SpacingTokens.radiusSm;
static const double md = SpacingTokens.radiusMd;
static const double lg = SpacingTokens.radiusLg;
static const double xl = SpacingTokens.radiusXl;
static const double circular = SpacingTokens.radiusCircular;
static const double round = SpacingTokens.radiusCircular; // Ajouté pour compatibilité
static const double round = SpacingTokens.radiusCircular;
}
/// Shim de compatibilité pour TypographyTokens
/// Shim TypographyTokens
class TypographyTokens {
static const TextStyle displayLarge = AppTypography.headerSmall;
static const TextStyle displayMedium = AppTypography.headerSmall;
static const TextStyle displaySmall = AppTypography.headerSmall;
static const TextStyle headlineLarge = AppTypography.headerSmall;
// Display (Playfair Display via AppTypography getters — non-const)
static TextStyle get displayLarge => AppTypography.displayLarge;
static TextStyle get displayMedium => AppTypography.displayMedium;
static TextStyle get displaySmall => AppTypography.displaySmall;
// Headlines
static const TextStyle headlineLarge = AppTypography.headerLarge;
static const TextStyle headlineMedium = AppTypography.headerSmall;
static const TextStyle headlineSmall = AppTypography.headerSmall;
static const TextStyle headlineSmall = AppTypography.titleMedium;
// Titles
static const TextStyle titleLarge = AppTypography.headerSmall;
static const TextStyle titleMedium = AppTypography.headerSmall;
static const TextStyle titleSmall = AppTypography.headerSmall;
static const TextStyle bodyLarge = AppTypography.bodyTextSmall;
static const TextStyle bodyMedium = AppTypography.bodyTextSmall;
static const TextStyle bodySmall = AppTypography.subtitleSmall;
static const TextStyle titleMedium = AppTypography.titleMedium;
static const TextStyle titleSmall = AppTypography.titleSmall;
// Body
static const TextStyle bodyLarge = AppTypography.bodyLarge;
static const TextStyle bodyMedium = AppTypography.bodyMedium;
static const TextStyle bodySmall = AppTypography.bodyTextSmall;
// Labels
static const TextStyle labelLarge = AppTypography.actionText;
static const TextStyle labelMedium = AppTypography.badgeText;
static const TextStyle labelMedium = AppTypography.labelMedium;
static const TextStyle labelSmall = AppTypography.badgeText;
static const TextStyle buttonLarge = AppTypography.actionText;
static const TextStyle cardValue = AppTypography.headerSmall;
// Buttons
static const TextStyle buttonLarge = AppTypography.buttonLabel;
static const TextStyle buttonMedium = AppTypography.actionText;
// Cards
static const TextStyle cardTitle = AppTypography.headerSmall;
static const TextStyle cardSubtitle = AppTypography.bodyTextSmall;
static const TextStyle cardValue = AppTypography.headerLarge;
// Inputs
static const TextStyle inputLabel = AppTypography.labelMedium;
static const TextStyle inputText = AppTypography.bodyLarge;
static const TextStyle inputHint = AppTypography.bodyTextSmall;
// Navigation
static const TextStyle navigationLabel = AppTypography.navLabel;
static const TextStyle navigationLabelSelected = AppTypography.navLabelSelected;
}

View File

@@ -25,4 +25,8 @@ export 'components/union_unified_account_card.dart';
export 'components/union_period_filter.dart';
export 'components/union_export_button.dart';
export 'components/union_notification_badge.dart';
export 'components/user_identity_card.dart';
export 'components/uf_section_header.dart';
export 'components/dashboard_activity_row.dart';
export 'components/dashboard_event_row.dart';

View File

@@ -14,8 +14,8 @@ class CoreCard extends StatelessWidget {
const CoreCard({
Key? key,
required this.child,
this.padding = const EdgeInsets.all(12.0),
this.margin = const EdgeInsets.only(bottom: 10.0),
this.padding = const EdgeInsets.all(8.0),
this.margin = const EdgeInsets.only(bottom: 6.0),
this.onTap,
this.backgroundColor,
}) : super(key: key);
@@ -23,22 +23,22 @@ class CoreCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Container(
width: double.infinity,
margin: margin,
decoration: BoxDecoration(
color: backgroundColor ?? (isDark ? const Color(0xFF1A1A1A) : Colors.white),
borderRadius: BorderRadius.circular(6.0),
color: backgroundColor ?? (isDark ? AppColors.darkSurface : Colors.white),
borderRadius: BorderRadius.circular(10.0),
border: Border.all(
color: isDark ? AppColors.darkBorder.withOpacity(0.5) : AppColors.lightBorder,
width: 0.4,
width: 0.8,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(isDark ? 0.3 : 0.03),
blurRadius: 10,
offset: const Offset(0, 4),
color: Colors.black.withOpacity(isDark ? 0.15 : 0.04),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
@@ -46,7 +46,7 @@ class CoreCard extends StatelessWidget {
color: Colors.transparent,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(6.0),
borderRadius: BorderRadius.circular(10.0),
child: Padding(
padding: padding,
child: child,

View File

@@ -44,7 +44,7 @@ class CoreTextField extends StatelessWidget {
: null,
filled: true,
fillColor: isDark ? AppColors.darkSurface : AppColors.lightSurface,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
@@ -63,7 +63,7 @@ class CoreTextField extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: AppColors.primaryGreen,
width: 1.5,
width: 2,
),
),
errorText: errorText,

View File

@@ -21,17 +21,17 @@ class ErrorDisplayWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Error icon
Icon(
_getErrorIcon(),
size: 64,
size: 40,
color: _getErrorColor(context),
),
const SizedBox(height: 24),
const SizedBox(height: 12),
// Error title
Text(
@@ -55,7 +55,7 @@ class ErrorDisplayWidget extends StatelessWidget {
// Retry button (if retryable and callback provided)
if (showRetryButton && failure.isRetryable && onRetry != null) ...[
const SizedBox(height: 32),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: onRetry,
icon: const Icon(Icons.refresh),
@@ -63,7 +63,7 @@ class ErrorDisplayWidget extends StatelessWidget {
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
vertical: 10,
),
),
),
@@ -151,7 +151,7 @@ class ErrorBanner extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: _getErrorColor(context).withOpacity(0.1),
border: Border.all(

View File

@@ -29,17 +29,17 @@ class AppErrorWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon ?? Icons.error_outline,
size: 64,
size: 40,
color: Theme.of(context).colorScheme.error,
),
const SizedBox(height: 16),
const SizedBox(height: 8),
Text(
title ?? 'Oups !',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
@@ -56,7 +56,7 @@ class AppErrorWidget extends StatelessWidget {
),
),
if (onRetry != null) ...[
const SizedBox(height: 24),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: onRetry,
icon: const Icon(Icons.refresh),
@@ -64,7 +64,7 @@ class AppErrorWidget extends StatelessWidget {
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
vertical: 10,
),
),
),
@@ -134,17 +134,17 @@ class EmptyDataWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon ?? Icons.inbox_outlined,
size: 64,
size: 40,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const SizedBox(height: 16),
const SizedBox(height: 8),
Text(
message,
textAlign: TextAlign.center,
@@ -153,7 +153,7 @@ class EmptyDataWidget extends StatelessWidget {
),
),
if (onAction != null && actionLabel != null) ...[
const SizedBox(height: 24),
const SizedBox(height: 12),
ElevatedButton(
onPressed: onAction,
child: Text(actionLabel!),

View File

@@ -33,7 +33,7 @@ class AppLoadingWidget extends StatelessWidget {
),
),
if (message != null) ...[
const SizedBox(height: 16),
const SizedBox(height: 8),
Text(
message!,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
@@ -62,7 +62,7 @@ class ShimmerListLoading extends StatelessWidget {
Widget build(BuildContext context) {
return ListView.builder(
itemCount: itemCount,
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(10),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
@@ -127,7 +127,7 @@ class ShimmerGridLoading extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 12,
@@ -161,7 +161,7 @@ class ShimmerDetailLoading extends StatelessWidget {
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -173,7 +173,7 @@ class ShimmerDetailLoading extends StatelessWidget {
borderRadius: BorderRadius.circular(12),
),
),
const SizedBox(height: 16),
const SizedBox(height: 8),
// Title
Container(
height: 24,
@@ -187,7 +187,7 @@ class ShimmerDetailLoading extends StatelessWidget {
width: 200,
color: Colors.white,
),
const SizedBox(height: 24),
const SizedBox(height: 12),
// Content lines
...List.generate(5, (index) {
return Padding(