feat(shared): legacy presentation/ + shared design system + widgets
- lib/presentation : pages legacy (explore/network, notifications) avec BLoC - lib/shared/design_system : UnionFlow Design System v2 (tokens, components) + MD3 tokens + module_colors par feature - lib/shared/widgets : widgets transversaux (core_card, core_shimmer, error_widget, loading_widget, powered_by_lions_dev, etc.) - lib/shared/constants + utils
This commit is contained in:
@@ -76,7 +76,7 @@ class _FinanceView extends StatelessWidget {
|
|||||||
alignment: CrossAxisAlignment.center,
|
alignment: CrossAxisAlignment.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const VerticalDivider(color: AppColors.lightBorder),
|
const VerticalDivider(color: AppColors.border),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: MiniMetricWidget(
|
child: MiniMetricWidget(
|
||||||
label: 'En attente',
|
label: 'En attente',
|
||||||
@@ -85,7 +85,7 @@ class _FinanceView extends StatelessWidget {
|
|||||||
alignment: CrossAxisAlignment.center,
|
alignment: CrossAxisAlignment.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const VerticalDivider(color: AppColors.lightBorder),
|
const VerticalDivider(color: AppColors.border),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: MiniMetricWidget(
|
child: MiniMetricWidget(
|
||||||
label: 'Épargne',
|
label: 'Épargne',
|
||||||
@@ -106,7 +106,7 @@ class _FinanceView extends StatelessWidget {
|
|||||||
style: AppTypography.badgeText.copyWith(
|
style: AppTypography.badgeText.copyWith(
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
? AppColors.textSecondaryDark
|
? AppColors.textSecondaryDark
|
||||||
: AppColors.textSecondaryLight,
|
: AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class _NetworkViewState extends State<_NetworkView> {
|
|||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
color: isDark ? AppColors.darkBorder : AppColors.lightBorder,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -75,9 +75,9 @@ class _NetworkViewState extends State<_NetworkView> {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Rechercher des membres, organisations...',
|
hintText: 'Rechercher des membres, organisations...',
|
||||||
hintStyle: AppTypography.subtitleSmall,
|
hintStyle: AppTypography.subtitleSmall,
|
||||||
prefixIcon: const Icon(Icons.search, size: 20, color: AppColors.textSecondaryLight),
|
prefixIcon: const Icon(Icons.search, size: 20, color: AppColors.textSecondary),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: isDark ? AppColors.darkSurface : AppColors.lightSurface,
|
fillColor: isDark ? AppColors.surfaceDark : AppColors.surface,
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 0), // Garder petit
|
contentPadding: const EdgeInsets.symmetric(vertical: 0), // Garder petit
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
@@ -160,8 +160,8 @@ class _NetworkViewState extends State<_NetworkView> {
|
|||||||
},
|
},
|
||||||
child: InfoBadge(
|
child: InfoBadge(
|
||||||
text: item.isConnected ? 'Connecté' : 'Suivre',
|
text: item.isConnected ? 'Connecté' : 'Suivre',
|
||||||
backgroundColor: item.isConnected ? AppColors.lightSurface : AppColors.primaryGreen,
|
backgroundColor: item.isConnected ? AppColors.surface : AppColors.primary,
|
||||||
textColor: item.isConnected ? AppColors.textPrimaryLight : Colors.white,
|
textColor: item.isConnected ? AppColors.textPrimary : Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class _UnifiedFeedViewState extends State<_UnifiedFeedView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
color: AppColors.primaryGreen,
|
color: AppColors.primary,
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
context.read<UnifiedFeedBloc>().add(const LoadFeedRequested(isRefresh: true));
|
context.read<UnifiedFeedBloc>().add(const LoadFeedRequested(isRefresh: true));
|
||||||
@@ -189,8 +189,8 @@ class _UnifiedFeedViewState extends State<_UnifiedFeedView> {
|
|||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
InfoBadge(
|
InfoBadge(
|
||||||
text: item.type.name.toUpperCase(),
|
text: item.type.name.toUpperCase(),
|
||||||
backgroundColor: AppColors.primaryGreen.withOpacity(0.1),
|
backgroundColor: AppColors.primary.withOpacity(0.1),
|
||||||
textColor: AppColors.primaryGreen,
|
textColor: AppColors.primary,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -206,7 +206,7 @@ class _UnifiedFeedViewState extends State<_UnifiedFeedView> {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
? AppColors.textSecondaryDark
|
? AppColors.textSecondaryDark
|
||||||
: AppColors.textSecondaryLight,
|
: AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
onPressed: () => _showPostOptionsMenu(context, item),
|
onPressed: () => _showPostOptionsMenu(context, item),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class _NotificationView extends StatelessWidget {
|
|||||||
itemCount: state.items.length,
|
itemCount: state.items.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = state.items[index];
|
final item = state.items[index];
|
||||||
final unreadColor = isDark ? const Color(0xFF1B2E26) : const Color(0xFFE8F5E9);
|
final unreadColor = isDark ? const Color(0xFF1A1F2E) : const Color(0xFFEFF6FF);
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -136,8 +136,8 @@ class _NotificationView extends StatelessWidget {
|
|||||||
Icon(
|
Icon(
|
||||||
item.category == 'finance' ? Icons.payment : Icons.event,
|
item.category == 'finance' ? Icons.payment : Icons.event,
|
||||||
color: item.isRead
|
color: item.isRead
|
||||||
? (isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight)
|
? (isDark ? AppColors.textSecondaryDark : AppColors.textSecondary)
|
||||||
: AppColors.primaryGreen,
|
: AppColors.primary,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class MiniHeaderBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: AppTypography.headerSmall.copyWith(
|
style: AppTypography.headerSmall.copyWith(
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class MiniMetricWidget extends StatelessWidget {
|
|||||||
style: AppTypography.badgeText.copyWith(
|
style: AppTypography.badgeText.copyWith(
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
? AppColors.textSecondaryDark
|
? AppColors.textSecondaryDark
|
||||||
: AppColors.textSecondaryLight,
|
: AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
@@ -38,7 +38,7 @@ class MiniMetricWidget extends StatelessWidget {
|
|||||||
style: AppTypography.actionText.copyWith(
|
style: AppTypography.actionText.copyWith(
|
||||||
color: valueColor ?? (Theme.of(context).brightness == Brightness.dark
|
color: valueColor ?? (Theme.of(context).brightness == Brightness.dark
|
||||||
? AppColors.textPrimaryDark
|
? AppColors.textPrimaryDark
|
||||||
: AppColors.textPrimaryLight),
|
: AppColors.textPrimary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class ProfileDrawer extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
name,
|
name,
|
||||||
style: AppTypography.headerSmall.copyWith(
|
style: AppTypography.headerSmall.copyWith(
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
@@ -68,7 +68,7 @@ class ProfileDrawer extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
Divider(color: isDark ? AppColors.darkBorder : AppColors.lightBorder, height: 1),
|
Divider(color: isDark ? AppColors.borderDark : AppColors.border, height: 1),
|
||||||
|
|
||||||
// Liens / Actions (factorisés)
|
// Liens / Actions (factorisés)
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -84,7 +84,7 @@ class ProfileDrawer extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Divider(color: isDark ? AppColors.darkBorder : AppColors.lightBorder, height: 1),
|
Divider(color: isDark ? AppColors.borderDark : AppColors.border, height: 1),
|
||||||
|
|
||||||
// Bouton Déconnexion
|
// Bouton Déconnexion
|
||||||
Padding(
|
Padding(
|
||||||
@@ -124,14 +124,14 @@ class ProfileDrawer extends StatelessWidget {
|
|||||||
Icon(
|
Icon(
|
||||||
icon,
|
icon,
|
||||||
size: 22,
|
size: 22,
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: AppTypography.headerSmall.copyWith(
|
style: AppTypography.headerSmall.copyWith(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ class UFPrimaryButton extends StatelessWidget {
|
|||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: isLoading ? null : onPressed,
|
onPressed: isLoading ? null : onPressed,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: backgroundColor ?? AppColors.primaryGreen,
|
backgroundColor: backgroundColor ?? AppColors.primary,
|
||||||
foregroundColor: textColor ?? Colors.white,
|
foregroundColor: textColor ?? Colors.white,
|
||||||
disabledBackgroundColor: (backgroundColor ?? AppColors.primaryGreen).withOpacity(0.5),
|
disabledBackgroundColor: (backgroundColor ?? AppColors.primary).withOpacity(0.5),
|
||||||
disabledForegroundColor: (textColor ?? Colors.white).withOpacity(0.7),
|
disabledForegroundColor: (textColor ?? Colors.white).withOpacity(0.7),
|
||||||
elevation: SpacingTokens.elevationSm,
|
elevation: SpacingTokens.elevationSm,
|
||||||
shadowColor: AppColors.darkBorder.withOpacity(0.1),
|
shadowColor: AppColors.borderDark.withOpacity(0.1),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
||||||
vertical: 10,
|
vertical: 10,
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ class UFSecondaryButton extends StatelessWidget {
|
|||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: isLoading ? null : onPressed,
|
onPressed: isLoading ? null : onPressed,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.brandGreen,
|
backgroundColor: AppColors.primaryDark,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
disabledBackgroundColor: AppColors.brandGreen.withOpacity(0.5),
|
disabledBackgroundColor: AppColors.primaryDark.withOpacity(0.5),
|
||||||
disabledForegroundColor: Colors.white.withOpacity(0.7),
|
disabledForegroundColor: Colors.white.withOpacity(0.7),
|
||||||
elevation: SpacingTokens.elevationSm,
|
elevation: SpacingTokens.elevationSm,
|
||||||
shadowColor: AppColors.darkBorder.withOpacity(0.1),
|
shadowColor: AppColors.borderDark.withOpacity(0.1),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
||||||
vertical: 10,
|
vertical: 10,
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class UFCard extends StatelessWidget {
|
|||||||
|
|
||||||
Widget content = Container(
|
Widget content = Container(
|
||||||
padding: effectivePadding,
|
padding: effectivePadding,
|
||||||
decoration: _getDecoration(effectiveBorderRadius),
|
decoration: _getDecoration(effectiveBorderRadius, context),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -108,27 +108,31 @@ class UFCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxDecoration _getDecoration(double radius) {
|
BoxDecoration _getDecoration(double radius, BuildContext context) {
|
||||||
|
final surfaceColor = Theme.of(context).colorScheme.surface;
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
final effectiveBorderColor = borderColor ?? (isDark ? AppColors.borderDark : AppColors.border);
|
||||||
|
|
||||||
switch (style) {
|
switch (style) {
|
||||||
case UFCardStyle.elevated:
|
case UFCardStyle.elevated:
|
||||||
return BoxDecoration(
|
return BoxDecoration(
|
||||||
color: color ?? AppColors.lightSurface,
|
color: color ?? surfaceColor,
|
||||||
borderRadius: BorderRadius.circular(radius),
|
borderRadius: BorderRadius.circular(radius),
|
||||||
);
|
);
|
||||||
|
|
||||||
case UFCardStyle.outlined:
|
case UFCardStyle.outlined:
|
||||||
return BoxDecoration(
|
return BoxDecoration(
|
||||||
color: color ?? AppColors.lightSurface,
|
color: color ?? surfaceColor,
|
||||||
borderRadius: BorderRadius.circular(radius),
|
borderRadius: BorderRadius.circular(radius),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: borderColor ?? AppColors.lightBorder,
|
color: effectiveBorderColor,
|
||||||
width: borderWidth ?? 1.0,
|
width: borderWidth ?? 1.0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
case UFCardStyle.filled:
|
case UFCardStyle.filled:
|
||||||
return BoxDecoration(
|
return BoxDecoration(
|
||||||
color: color ?? AppColors.lightSurface,
|
color: color ?? surfaceColor,
|
||||||
borderRadius: BorderRadius.circular(radius),
|
borderRadius: BorderRadius.circular(radius),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ class UFInfoCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
|
final effectiveIconColor = iconColor ?? AppColors.primary;
|
||||||
final effectivePadding = padding ?? const EdgeInsets.all(SpacingTokens.lg);
|
final effectivePadding = padding ?? const EdgeInsets.all(SpacingTokens.lg);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: effectivePadding,
|
padding: effectivePadding,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isDark ? AppColors.darkSurface : Colors.white,
|
color: isDark ? AppColors.surfaceDark : Colors.white,
|
||||||
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -71,7 +71,7 @@ class UFInfoCard extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: AppTypography.headerSmall.copyWith(
|
style: AppTypography.headerSmall.copyWith(
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ class UFStatCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
|
final effectiveIconColor = iconColor ?? AppColors.primary;
|
||||||
final effectiveIconBgColor = iconBackgroundColor ??
|
final effectiveIconBgColor = iconBackgroundColor ??
|
||||||
effectiveIconColor.withOpacity(0.1);
|
effectiveIconColor.withOpacity(0.1);
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
elevation: SpacingTokens.elevationSm,
|
elevation: SpacingTokens.elevationSm,
|
||||||
shadowColor: AppColors.darkBorder.withOpacity(0.1),
|
shadowColor: AppColors.borderDark.withOpacity(0.1),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
),
|
),
|
||||||
@@ -97,7 +97,7 @@ class UFStatCard extends StatelessWidget {
|
|||||||
Icon(
|
Icon(
|
||||||
Icons.arrow_forward_ios,
|
Icons.arrow_forward_ios,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
|
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -108,7 +108,7 @@ class UFStatCard extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: AppTypography.badgeText.copyWith(
|
style: AppTypography.badgeText.copyWith(
|
||||||
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
|
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ class UFStatCard extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: AppTypography.headerSmall.copyWith(
|
style: AppTypography.headerSmall.copyWith(
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ class UFStatCard extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
subtitle!,
|
subtitle!,
|
||||||
style: AppTypography.subtitleSmall.copyWith(
|
style: AppTypography.subtitleSmall.copyWith(
|
||||||
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
|
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class UFDropdownTile<T> extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final effectiveBgColor = backgroundColor ??
|
final effectiveBgColor = backgroundColor ??
|
||||||
(isDark ? AppColors.darkSurface : AppColors.lightSurface);
|
(isDark ? AppColors.surfaceDark : AppColors.surface);
|
||||||
final effectiveItemBuilder = itemBuilder ?? (item) => item.toString();
|
final effectiveItemBuilder = itemBuilder ?? (item) => item.toString();
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
@@ -67,16 +67,16 @@ class UFDropdownTile<T> extends StatelessWidget {
|
|||||||
title,
|
title,
|
||||||
style: AppTypography.bodyTextSmall.copyWith(
|
style: AppTypography.bodyTextSmall.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: SpacingTokens.lg),
|
padding: const EdgeInsets.symmetric(horizontal: SpacingTokens.lg),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isDark ? AppColors.darkBackground : Colors.white,
|
color: isDark ? AppColors.backgroundDark : Colors.white,
|
||||||
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
border: Border.all(color: isDark ? AppColors.darkBorder : AppColors.lightBorder),
|
border: Border.all(color: isDark ? AppColors.borderDark : AppColors.border),
|
||||||
),
|
),
|
||||||
child: DropdownButtonHideUnderline(
|
child: DropdownButtonHideUnderline(
|
||||||
child: DropdownButton<T>(
|
child: DropdownButton<T>(
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class UFSwitchTile extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final effectiveBgColor = backgroundColor ??
|
final effectiveBgColor = backgroundColor ??
|
||||||
(isDark ? AppColors.darkSurface : AppColors.lightSurface);
|
(isDark ? AppColors.surfaceDark : AppColors.surface);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: SpacingTokens.lg),
|
margin: const EdgeInsets.only(bottom: SpacingTokens.lg),
|
||||||
@@ -65,13 +65,13 @@ class UFSwitchTile extends StatelessWidget {
|
|||||||
title,
|
title,
|
||||||
style: AppTypography.bodyTextSmall.copyWith(
|
style: AppTypography.bodyTextSmall.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
subtitle,
|
subtitle,
|
||||||
style: AppTypography.subtitleSmall.copyWith(
|
style: AppTypography.subtitleSmall.copyWith(
|
||||||
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
|
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -80,7 +80,7 @@ class UFSwitchTile extends StatelessWidget {
|
|||||||
Switch(
|
Switch(
|
||||||
value: value,
|
value: value,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
activeColor: AppColors.primaryGreen,
|
activeColor: AppColors.primary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,21 +7,45 @@ import '../unionflow_design_system.dart';
|
|||||||
/// Composant AppBar unifié pour toutes les pages de détail/formulaire.
|
/// Composant AppBar unifié pour toutes les pages de détail/formulaire.
|
||||||
/// Garantit la cohérence visuelle et l'expérience utilisateur.
|
/// Garantit la cohérence visuelle et l'expérience utilisateur.
|
||||||
///
|
///
|
||||||
/// Si [mergeLeadingWithTitle] est true et que la route peut être quittée,
|
/// ### Mode module (identité chromatique)
|
||||||
/// le bouton retour et le titre sont fusionnés en une seule ligne (retour
|
///
|
||||||
/// toujours visible avec la même couleur que le titre).
|
/// Passez [moduleColor] ou [moduleGradient] pour afficher un AppBar avec
|
||||||
|
/// le gradient signature du module, rappelant visuellement à l'utilisateur
|
||||||
|
/// dans quelle section de l'application il se trouve.
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// UFAppBar(
|
||||||
|
/// title: 'Gestion des Organisations',
|
||||||
|
/// moduleGradient: ModuleColors.organisationsGradient,
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Mode standard
|
||||||
|
///
|
||||||
|
/// Sans [moduleColor] ni [moduleGradient], le comportement existant est
|
||||||
|
/// préservé intégralement (backgroundColor, foregroundColor, etc.).
|
||||||
class UFAppBar extends StatelessWidget implements PreferredSizeWidget {
|
class UFAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<Widget>? actions;
|
final List<Widget>? actions;
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
final bool automaticallyImplyLeading;
|
final bool automaticallyImplyLeading;
|
||||||
/// Fusionne le bouton retour et le titre en une seule zone (retour visible, même couleur que le titre).
|
|
||||||
|
/// Fusionne le bouton retour et le titre en une seule zone (retour visible,
|
||||||
|
/// même couleur que le titre).
|
||||||
final bool mergeLeadingWithTitle;
|
final bool mergeLeadingWithTitle;
|
||||||
final PreferredSizeWidget? bottom;
|
final PreferredSizeWidget? bottom;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
final Color? foregroundColor;
|
final Color? foregroundColor;
|
||||||
final double elevation;
|
final double elevation;
|
||||||
|
|
||||||
|
/// Couleur primaire du module — génère automatiquement un gradient subtil.
|
||||||
|
/// Prend le dessus sur [backgroundColor] si renseigné.
|
||||||
|
final Color? moduleColor;
|
||||||
|
|
||||||
|
/// Gradient du module (liste de 2 couleurs) — ex: ModuleColors.organisationsGradient.
|
||||||
|
/// Prend le dessus sur [moduleColor] et [backgroundColor] si renseigné.
|
||||||
|
final List<Color>? moduleGradient;
|
||||||
|
|
||||||
const UFAppBar({
|
const UFAppBar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
@@ -33,8 +57,21 @@ class UFAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
this.foregroundColor,
|
this.foregroundColor,
|
||||||
this.elevation = 0,
|
this.elevation = 0,
|
||||||
|
this.moduleColor,
|
||||||
|
this.moduleGradient,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bool get _isModuleMode => moduleGradient != null || moduleColor != null;
|
||||||
|
|
||||||
|
List<Color> get _resolvedGradient {
|
||||||
|
if (moduleGradient != null) return moduleGradient!;
|
||||||
|
final c = moduleColor!;
|
||||||
|
// Génère un gradient sombre → clair depuis la couleur du module
|
||||||
|
final hsl = HSLColor.fromColor(c);
|
||||||
|
final dark = hsl.withLightness((hsl.lightness - 0.06).clamp(0.0, 1.0)).toColor();
|
||||||
|
return [dark, c];
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final canPop = ModalRoute.of(context)?.canPop ?? false;
|
final canPop = ModalRoute.of(context)?.canPop ?? false;
|
||||||
@@ -44,8 +81,7 @@ class UFAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final isTransparent = backgroundColor == Colors.transparent ||
|
final isTransparent = backgroundColor == Colors.transparent ||
|
||||||
(backgroundColor != null && backgroundColor!.opacity < 0.1);
|
(backgroundColor != null && backgroundColor!.opacity < 0.1);
|
||||||
|
|
||||||
return AppBar(
|
Widget titleWidget = useMergedTitle
|
||||||
title: useMergedTitle
|
|
||||||
? Row(
|
? Row(
|
||||||
children: [
|
children: [
|
||||||
Material(
|
Material(
|
||||||
@@ -74,8 +110,51 @@ class UFAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: Text(title),
|
: Text(title);
|
||||||
backgroundColor: backgroundColor ?? AppColors.primaryGreen,
|
|
||||||
|
// Mode module : gradient via flexibleSpace
|
||||||
|
// iconTheme + actionsIconTheme explicites pour garantir la visibilité
|
||||||
|
// des icônes sur fond gradient sombre (foregroundColor seul ne suffit pas
|
||||||
|
// quand backgroundColor est transparent).
|
||||||
|
if (_isModuleMode) {
|
||||||
|
final gradient = _resolvedGradient;
|
||||||
|
return AppBar(
|
||||||
|
title: titleWidget,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: elevation,
|
||||||
|
leading: useMergedTitle ? null : leading,
|
||||||
|
automaticallyImplyLeading: useMergedTitle ? false : automaticallyImplyLeading,
|
||||||
|
actions: actions,
|
||||||
|
bottom: bottom,
|
||||||
|
iconTheme: const IconThemeData(color: Colors.white, size: 22),
|
||||||
|
actionsIconTheme: const IconThemeData(color: Colors.white, size: 22),
|
||||||
|
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
statusBarBrightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
centerTitle: false,
|
||||||
|
titleTextStyle: AppTypography.headerSmall.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
flexibleSpace: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: gradient,
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode standard (comportement existant préservé)
|
||||||
|
return AppBar(
|
||||||
|
title: titleWidget,
|
||||||
|
backgroundColor: backgroundColor ?? AppColors.primary,
|
||||||
foregroundColor: fg,
|
foregroundColor: fg,
|
||||||
elevation: elevation,
|
elevation: elevation,
|
||||||
leading: useMergedTitle ? null : leading,
|
leading: useMergedTitle ? null : leading,
|
||||||
@@ -100,4 +179,3 @@ class UFAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
kToolbarHeight + (bottom?.preferredSize.height ?? 0.0),
|
kToolbarHeight + (bottom?.preferredSize.height ?? 0.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class UFContainer extends StatelessWidget {
|
|||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: gradient == null ? (color ?? AppColors.lightSurface) : null,
|
color: gradient == null ? (color ?? Theme.of(context).colorScheme.surface) : null,
|
||||||
gradient: gradient,
|
gradient: gradient,
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
border: border,
|
border: border,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class UFHeader extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(SpacingTokens.lg),
|
padding: const EdgeInsets.all(SpacingTokens.lg),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: const LinearGradient(
|
||||||
colors: [AppColors.primaryGreen, AppColors.brandGreenLight],
|
colors: [AppColors.primary, AppColors.primaryLight],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class UFPageHeader extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
|
final effectiveIconColor = iconColor ?? AppColors.primary;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -66,7 +66,7 @@ class UFPageHeader extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: AppTypography.headerSmall.copyWith(
|
style: AppTypography.headerSmall.copyWith(
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -83,7 +83,7 @@ class UFPageHeader extends StatelessWidget {
|
|||||||
Divider(
|
Divider(
|
||||||
height: 1,
|
height: 1,
|
||||||
thickness: 1,
|
thickness: 1,
|
||||||
color: isDark ? AppColors.darkBorder : AppColors.lightBorder,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -112,7 +112,7 @@ class UFPageHeaderWithStats extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final effectiveIconColor = iconColor ?? AppColors.primaryGreen;
|
final effectiveIconColor = iconColor ?? AppColors.primary;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -146,7 +146,7 @@ class UFPageHeaderWithStats extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: AppTypography.headerSmall.copyWith(
|
style: AppTypography.headerSmall.copyWith(
|
||||||
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight,
|
color: isDark ? AppColors.textPrimaryDark : AppColors.textPrimary,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -185,14 +185,14 @@ class UFPageHeaderWithStats extends StatelessWidget {
|
|||||||
Divider(
|
Divider(
|
||||||
height: 1,
|
height: 1,
|
||||||
thickness: 1,
|
thickness: 1,
|
||||||
color: isDark ? AppColors.darkBorder : AppColors.lightBorder,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStatItem(UFHeaderStat stat, bool isDark) {
|
Widget _buildStatItem(UFHeaderStat stat, bool isDark) {
|
||||||
final effectiveColor = stat.color ?? AppColors.primaryGreen;
|
final effectiveColor = stat.color ?? AppColors.primary;
|
||||||
return UFContainer.rounded(
|
return UFContainer.rounded(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: SpacingTokens.md,
|
horizontal: SpacingTokens.md,
|
||||||
@@ -213,7 +213,7 @@ class UFPageHeaderWithStats extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
stat.label,
|
stat.label,
|
||||||
style: AppTypography.badgeText.copyWith(
|
style: AppTypography.badgeText.copyWith(
|
||||||
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
|
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'tokens/color_tokens.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
/// Gestionnaire de thèmes personnalisables pour le Dashboard
|
/// Gestionnaire de thèmes personnalisables pour le Dashboard
|
||||||
class DashboardThemeManager {
|
class DashboardThemeManager {
|
||||||
static const String _themeKey = 'dashboard_theme';
|
static const String _themeKey = 'dashboard_theme';
|
||||||
static DashboardThemeData _currentTheme = DashboardThemeData.royalTeal();
|
static DashboardThemeData _currentTheme = DashboardThemeData.unionFlow();
|
||||||
static SharedPreferences? _prefs;
|
static SharedPreferences? _prefs;
|
||||||
|
|
||||||
/// Initialise le gestionnaire de thèmes
|
/// Initialise le gestionnaire de thèmes
|
||||||
@@ -15,7 +16,7 @@ class DashboardThemeManager {
|
|||||||
|
|
||||||
/// Charge le thème sauvegardé
|
/// Charge le thème sauvegardé
|
||||||
static Future<void> _loadSavedTheme() async {
|
static Future<void> _loadSavedTheme() async {
|
||||||
final themeName = _prefs?.getString(_themeKey) ?? 'royalTeal';
|
final themeName = _prefs?.getString(_themeKey) ?? 'unionFlow';
|
||||||
_currentTheme = _getThemeByName(themeName);
|
_currentTheme = _getThemeByName(themeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +32,8 @@ class DashboardThemeManager {
|
|||||||
/// Obtient un thème par son nom
|
/// Obtient un thème par son nom
|
||||||
static DashboardThemeData _getThemeByName(String name) {
|
static DashboardThemeData _getThemeByName(String name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
case 'unionFlow':
|
||||||
|
return DashboardThemeData.unionFlow();
|
||||||
case 'royalTeal':
|
case 'royalTeal':
|
||||||
return DashboardThemeData.royalTeal();
|
return DashboardThemeData.royalTeal();
|
||||||
case 'oceanBlue':
|
case 'oceanBlue':
|
||||||
@@ -44,12 +47,13 @@ class DashboardThemeManager {
|
|||||||
case 'darkMode':
|
case 'darkMode':
|
||||||
return DashboardThemeData.darkMode();
|
return DashboardThemeData.darkMode();
|
||||||
default:
|
default:
|
||||||
return DashboardThemeData.royalTeal();
|
return DashboardThemeData.unionFlow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtient la liste des thèmes disponibles
|
/// Obtient la liste des thèmes disponibles
|
||||||
static List<ThemeOption> get availableThemes => [
|
static List<ThemeOption> get availableThemes => [
|
||||||
|
ThemeOption('unionFlow', 'UnionFlow (défaut)', DashboardThemeData.unionFlow()),
|
||||||
ThemeOption('royalTeal', 'Bleu Roi & Pétrole', DashboardThemeData.royalTeal()),
|
ThemeOption('royalTeal', 'Bleu Roi & Pétrole', DashboardThemeData.royalTeal()),
|
||||||
ThemeOption('oceanBlue', 'Bleu Océan', DashboardThemeData.oceanBlue()),
|
ThemeOption('oceanBlue', 'Bleu Océan', DashboardThemeData.oceanBlue()),
|
||||||
ThemeOption('forestGreen', 'Vert Forêt', DashboardThemeData.forestGreen()),
|
ThemeOption('forestGreen', 'Vert Forêt', DashboardThemeData.forestGreen()),
|
||||||
@@ -108,7 +112,29 @@ class DashboardThemeData {
|
|||||||
this.isDark = false,
|
this.isDark = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Thème Bleu Roi & Pétrole (par défaut)
|
/// Thème UnionFlow officiel — Bleu #297FFF → Violet #7616E8 (logo signature)
|
||||||
|
factory DashboardThemeData.unionFlow() {
|
||||||
|
return const DashboardThemeData(
|
||||||
|
name: 'UnionFlow',
|
||||||
|
primaryColor: Color(0xFF297FFF),
|
||||||
|
secondaryColor: Color(0xFF7616E8),
|
||||||
|
primaryLight: Color(0xFF69B7FF),
|
||||||
|
primaryDark: Color(0xFF2170E7),
|
||||||
|
secondaryLight: Color(0xFF9B59F0),
|
||||||
|
secondaryDark: Color(0xFF5B0FBA),
|
||||||
|
backgroundColor: Color(0xFFF5F8FF),
|
||||||
|
surfaceColor: Color(0xFFFFFFFF),
|
||||||
|
cardColor: Color(0xFFFFFFFF),
|
||||||
|
textPrimary: Color(0xFF111827),
|
||||||
|
textSecondary: Color(0xFF6B7280),
|
||||||
|
success: Color(0xFF22C55E),
|
||||||
|
warning: Color(0xFFF59E0B),
|
||||||
|
error: Color(0xFFEF4444),
|
||||||
|
info: Color(0xFF297FFF),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Thème Bleu Roi & Pétrole
|
||||||
factory DashboardThemeData.royalTeal() {
|
factory DashboardThemeData.royalTeal() {
|
||||||
return const DashboardThemeData(
|
return const DashboardThemeData(
|
||||||
name: 'Bleu Roi & Pétrole',
|
name: 'Bleu Roi & Pétrole',
|
||||||
@@ -123,7 +149,7 @@ class DashboardThemeData {
|
|||||||
cardColor: Color(0xFFFFFFFF),
|
cardColor: Color(0xFFFFFFFF),
|
||||||
textPrimary: Color(0xFF111827),
|
textPrimary: Color(0xFF111827),
|
||||||
textSecondary: Color(0xFF6B7280),
|
textSecondary: Color(0xFF6B7280),
|
||||||
success: Color(0xFF10B981),
|
success: ColorTokens.successLight,
|
||||||
warning: Color(0xFFF59E0B),
|
warning: Color(0xFFF59E0B),
|
||||||
error: Color(0xFFEF4444),
|
error: Color(0xFFEF4444),
|
||||||
info: Color(0xFF3B82F6),
|
info: Color(0xFF3B82F6),
|
||||||
@@ -158,7 +184,7 @@ class DashboardThemeData {
|
|||||||
name: 'Vert Forêt',
|
name: 'Vert Forêt',
|
||||||
primaryColor: Color(0xFF059669),
|
primaryColor: Color(0xFF059669),
|
||||||
secondaryColor: Color(0xFF047857),
|
secondaryColor: Color(0xFF047857),
|
||||||
primaryLight: Color(0xFF10B981),
|
primaryLight: ColorTokens.successLight,
|
||||||
primaryDark: Color(0xFF065F46),
|
primaryDark: Color(0xFF065F46),
|
||||||
secondaryLight: Color(0xFF059669),
|
secondaryLight: Color(0xFF059669),
|
||||||
secondaryDark: Color(0xFF064E3B),
|
secondaryDark: Color(0xFF064E3B),
|
||||||
@@ -167,7 +193,7 @@ class DashboardThemeData {
|
|||||||
cardColor: Color(0xFFFFFFFF),
|
cardColor: Color(0xFFFFFFFF),
|
||||||
textPrimary: Color(0xFF064E3B),
|
textPrimary: Color(0xFF064E3B),
|
||||||
textSecondary: Color(0xFF6B7280),
|
textSecondary: Color(0xFF6B7280),
|
||||||
success: Color(0xFF10B981),
|
success: ColorTokens.successLight,
|
||||||
warning: Color(0xFFF59E0B),
|
warning: Color(0xFFF59E0B),
|
||||||
error: Color(0xFFEF4444),
|
error: Color(0xFFEF4444),
|
||||||
info: Color(0xFF3B82F6),
|
info: Color(0xFF3B82F6),
|
||||||
@@ -227,7 +253,7 @@ class DashboardThemeData {
|
|||||||
primaryLight: Color(0xFF93C5FD),
|
primaryLight: Color(0xFF93C5FD),
|
||||||
primaryDark: Color(0xFF3B82F6),
|
primaryDark: Color(0xFF3B82F6),
|
||||||
secondaryLight: Color(0xFF6EE7B7),
|
secondaryLight: Color(0xFF6EE7B7),
|
||||||
secondaryDark: Color(0xFF10B981),
|
secondaryDark: ColorTokens.successLight,
|
||||||
backgroundColor: Color(0xFF111827),
|
backgroundColor: Color(0xFF111827),
|
||||||
surfaceColor: Color(0xFF1F2937),
|
surfaceColor: Color(0xFF1F2937),
|
||||||
cardColor: Color(0xFF374151),
|
cardColor: Color(0xFF374151),
|
||||||
|
|||||||
@@ -1,93 +1,197 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import '../tokens/app_colors.dart';
|
import '../tokens/app_colors.dart';
|
||||||
import '../tokens/app_typography.dart';
|
import '../tokens/app_typography.dart';
|
||||||
|
|
||||||
/// UnionFlow Mobile App - Thème Global
|
/// UnionFlow Mobile App — Thème Global
|
||||||
/// Utilise la charte stricte (Vert/Blanc/Noir OLED) et force la petite typographie.
|
///
|
||||||
|
/// Palette WCAG 2.1 validée :
|
||||||
|
/// Primary : #2563EB [5.7:1 ✅ WCAG AA]
|
||||||
|
/// Dark mode: #0A0D1A [Navy profond]
|
||||||
class AppTheme {
|
class AppTheme {
|
||||||
|
|
||||||
// --- THÈME CLAIR (Mode Jour) ---
|
// ─── THÈME CLAIR ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
static final ThemeData lightTheme = ThemeData(
|
static final ThemeData lightTheme = ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primaryColor: AppColors.primaryGreen,
|
primaryColor: AppColors.primary,
|
||||||
scaffoldBackgroundColor: AppColors.lightSurface,
|
scaffoldBackgroundColor: AppColors.background,
|
||||||
colorScheme: const ColorScheme.light(
|
colorScheme: const ColorScheme.light(
|
||||||
primary: AppColors.primaryGreen,
|
primary: AppColors.primary,
|
||||||
secondary: AppColors.brandGreenLight,
|
|
||||||
surface: AppColors.lightBackground,
|
|
||||||
error: AppColors.error,
|
|
||||||
onPrimary: Colors.white,
|
onPrimary: Colors.white,
|
||||||
|
primaryContainer: AppColors.primaryContainer,
|
||||||
|
onPrimaryContainer: Color(0xFF1E3A8A),
|
||||||
|
secondary: AppColors.accent,
|
||||||
onSecondary: Colors.white,
|
onSecondary: Colors.white,
|
||||||
onSurface: AppColors.textPrimaryLight,
|
tertiary: Color(0xFF5297FF),
|
||||||
|
surface: AppColors.surface,
|
||||||
|
onSurface: AppColors.textPrimary,
|
||||||
|
error: AppColors.error,
|
||||||
onError: Colors.white,
|
onError: Colors.white,
|
||||||
|
outline: AppColors.border,
|
||||||
),
|
),
|
||||||
|
|
||||||
// Forcer la typographie standardisée
|
|
||||||
textTheme: const TextTheme(
|
textTheme: const TextTheme(
|
||||||
titleMedium: AppTypography.headerSmall,
|
titleMedium: AppTypography.headerSmall,
|
||||||
bodyMedium: AppTypography.bodyTextSmall,
|
bodyMedium: AppTypography.bodyTextSmall,
|
||||||
bodySmall: AppTypography.subtitleSmall,
|
bodySmall: AppTypography.subtitleSmall,
|
||||||
labelLarge: AppTypography.actionText,
|
labelLarge: AppTypography.actionText,
|
||||||
labelSmall: AppTypography.badgeText,
|
labelSmall: AppTypography.badgeText,
|
||||||
|
).apply(
|
||||||
|
bodyColor: AppColors.textPrimary,
|
||||||
|
displayColor: AppColors.textPrimary,
|
||||||
|
).copyWith(
|
||||||
|
// .apply() ne propage pas bodyColor aux styles label* (comportement Flutter documenté)
|
||||||
|
// → on les force explicitement ici pour le light mode
|
||||||
|
titleMedium: AppTypography.headerSmall.copyWith(color: AppColors.textTitle),
|
||||||
|
labelLarge: AppTypography.actionText.copyWith(color: AppColors.textTitle),
|
||||||
|
labelSmall: AppTypography.badgeText.copyWith(color: AppColors.textPrimary),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Personnalisation des AppBar (Garder la minimaliste)
|
appBarTheme: AppBarTheme(
|
||||||
appBarTheme: const AppBarTheme(
|
backgroundColor: AppColors.surface,
|
||||||
backgroundColor: AppColors.lightBackground,
|
foregroundColor: AppColors.textPrimary,
|
||||||
foregroundColor: AppColors.textPrimaryLight,
|
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
iconTheme: IconThemeData(color: AppColors.textPrimaryLight, size: 20),
|
surfaceTintColor: Colors.transparent,
|
||||||
titleTextStyle: AppTypography.headerSmall,
|
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
statusBarIconBrightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
iconTheme: const IconThemeData(
|
||||||
|
color: AppColors.textPrimary,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
titleTextStyle: AppTypography.headerSmall.copyWith(
|
||||||
|
color: AppColors.textPrimary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Boutons par défaut
|
|
||||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.primaryGreen,
|
backgroundColor: AppColors.primary,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
textStyle: AppTypography.actionText,
|
textStyle: AppTypography.actionText,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
shape: RoundedRectangleBorder(
|
||||||
minimumSize: const Size(64, 32),
|
borderRadius: BorderRadius.circular(20),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
),
|
||||||
|
minimumSize: const Size(64, 40),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// BottomNavigationBar ultra-compacte
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: AppColors.primary,
|
||||||
|
side: const BorderSide(color: AppColors.primary),
|
||||||
|
textStyle: AppTypography.actionText,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
minimumSize: const Size(64, 40),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: AppColors.primary,
|
||||||
|
textStyle: AppTypography.actionText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
filled: true,
|
||||||
|
fillColor: AppColors.surface,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.border),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.border),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.primary, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.error),
|
||||||
|
),
|
||||||
|
labelStyle: const TextStyle(color: AppColors.textSecondary),
|
||||||
|
hintStyle: const TextStyle(color: AppColors.textTertiary),
|
||||||
|
),
|
||||||
|
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||||
backgroundColor: AppColors.lightBackground,
|
backgroundColor: AppColors.surface,
|
||||||
selectedItemColor: AppColors.primaryGreen,
|
selectedItemColor: AppColors.primary,
|
||||||
unselectedItemColor: AppColors.textSecondaryLight,
|
unselectedItemColor: AppColors.textSecondary,
|
||||||
showSelectedLabels: false,
|
showSelectedLabels: false,
|
||||||
showUnselectedLabels: false,
|
showUnselectedLabels: false,
|
||||||
elevation: 8,
|
elevation: 8,
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
chipTheme: ChipThemeData(
|
||||||
|
backgroundColor: AppColors.primaryContainer,
|
||||||
|
selectedColor: AppColors.primary,
|
||||||
|
labelStyle: AppTypography.labelMedium,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
dividerTheme: const DividerThemeData(
|
dividerTheme: const DividerThemeData(
|
||||||
color: AppColors.lightBorder,
|
color: AppColors.border,
|
||||||
thickness: 1,
|
thickness: 1,
|
||||||
space: 1,
|
space: 1,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
cardTheme: CardThemeData(
|
||||||
|
color: AppColors.surface,
|
||||||
|
elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
side: const BorderSide(color: AppColors.border),
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
|
||||||
|
snackBarTheme: SnackBarThemeData(
|
||||||
|
backgroundColor: AppColors.textPrimary,
|
||||||
|
contentTextStyle: AppTypography.bodyTextSmall.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
behavior: SnackBarBehavior.fixed,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- THÈME SOMBRE (Mode Nuit OLED) ---
|
// ─── THÈME SOMBRE (Navy Profond) ──────────────────────────────────────────
|
||||||
|
|
||||||
static final ThemeData darkTheme = ThemeData(
|
static final ThemeData darkTheme = ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primaryColor: AppColors.primaryGreen,
|
primaryColor: AppColors.primaryLight,
|
||||||
scaffoldBackgroundColor: AppColors.darkBackground, // Noir OLED
|
scaffoldBackgroundColor: AppColors.backgroundDark,
|
||||||
colorScheme: const ColorScheme.dark(
|
colorScheme: const ColorScheme.dark(
|
||||||
primary: AppColors.primaryGreen,
|
primary: AppColors.primaryLight,
|
||||||
secondary: AppColors.brandGreenLight,
|
onPrimary: Color(0xFF1E3A8A),
|
||||||
surface: AppColors.darkSurface, // Gris très sombre
|
primaryContainer: Color(0xFF1A2350),
|
||||||
error: AppColors.error,
|
onPrimaryContainer: Color(0xFFD6E8FF),
|
||||||
onPrimary: Colors.white,
|
secondary: AppColors.accentLight,
|
||||||
onSecondary: Colors.white,
|
onSecondary: Color(0xFF2A006F),
|
||||||
|
surface: AppColors.surfaceDark,
|
||||||
onSurface: AppColors.textPrimaryDark,
|
onSurface: AppColors.textPrimaryDark,
|
||||||
|
error: AppColors.error,
|
||||||
onError: Colors.white,
|
onError: Colors.white,
|
||||||
|
outline: AppColors.borderDark,
|
||||||
),
|
),
|
||||||
|
|
||||||
textTheme: const TextTheme(
|
textTheme: const TextTheme(
|
||||||
@@ -99,32 +203,94 @@ class AppTheme {
|
|||||||
).apply(
|
).apply(
|
||||||
bodyColor: AppColors.textPrimaryDark,
|
bodyColor: AppColors.textPrimaryDark,
|
||||||
displayColor: AppColors.textPrimaryDark,
|
displayColor: AppColors.textPrimaryDark,
|
||||||
|
).copyWith(
|
||||||
|
// .apply() ne propage pas bodyColor aux styles label* (comportement Flutter documenté)
|
||||||
|
// → on les force explicitement ici pour le dark mode
|
||||||
|
titleMedium: AppTypography.headerSmall.copyWith(color: AppColors.textTitleDark),
|
||||||
|
labelLarge: AppTypography.actionText.copyWith(color: AppColors.textTitleDark),
|
||||||
|
labelSmall: AppTypography.badgeText.copyWith(color: AppColors.textPrimaryDark),
|
||||||
),
|
),
|
||||||
|
|
||||||
appBarTheme: const AppBarTheme(
|
appBarTheme: AppBarTheme(
|
||||||
backgroundColor: AppColors.darkBackground,
|
backgroundColor: AppColors.backgroundDark,
|
||||||
foregroundColor: AppColors.textPrimaryDark,
|
foregroundColor: AppColors.textPrimaryDark,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
iconTheme: IconThemeData(color: AppColors.textPrimaryDark, size: 20),
|
surfaceTintColor: Colors.transparent,
|
||||||
titleTextStyle: AppTypography.headerSmall, // Remplace titleTextStyle
|
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
),
|
||||||
|
iconTheme: const IconThemeData(
|
||||||
|
color: AppColors.textPrimaryDark,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
titleTextStyle: AppTypography.headerSmall.copyWith(
|
||||||
|
color: AppColors.textPrimaryDark,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.primaryGreen,
|
backgroundColor: AppColors.primary,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
textStyle: AppTypography.actionText,
|
textStyle: AppTypography.actionText,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
shape: RoundedRectangleBorder(
|
||||||
minimumSize: const Size(64, 32),
|
borderRadius: BorderRadius.circular(20),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
),
|
||||||
|
minimumSize: const Size(64, 40),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: AppColors.primaryLight,
|
||||||
|
side: const BorderSide(color: AppColors.primaryLight),
|
||||||
|
textStyle: AppTypography.actionText,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
minimumSize: const Size(64, 40),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: AppColors.primaryLight,
|
||||||
|
textStyle: AppTypography.actionText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
filled: true,
|
||||||
|
fillColor: AppColors.surfaceDark,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.borderDark),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.borderDark),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.primaryLight, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: AppColors.error),
|
||||||
|
),
|
||||||
|
labelStyle: const TextStyle(color: AppColors.textSecondaryDark),
|
||||||
|
hintStyle: TextStyle(color: AppColors.textSecondaryDark.withOpacity(0.6)),
|
||||||
|
),
|
||||||
|
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||||
backgroundColor: AppColors.darkBackground,
|
backgroundColor: AppColors.backgroundDark,
|
||||||
selectedItemColor: AppColors.primaryGreen,
|
selectedItemColor: AppColors.primaryLight,
|
||||||
unselectedItemColor: AppColors.textSecondaryDark,
|
unselectedItemColor: AppColors.textSecondaryDark,
|
||||||
showSelectedLabels: false,
|
showSelectedLabels: false,
|
||||||
showUnselectedLabels: false,
|
showUnselectedLabels: false,
|
||||||
@@ -133,9 +299,30 @@ class AppTheme {
|
|||||||
),
|
),
|
||||||
|
|
||||||
dividerTheme: const DividerThemeData(
|
dividerTheme: const DividerThemeData(
|
||||||
color: AppColors.darkBorder,
|
color: AppColors.borderDark,
|
||||||
thickness: 1,
|
thickness: 1,
|
||||||
space: 1,
|
space: 1,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
cardTheme: CardThemeData(
|
||||||
|
color: AppColors.surfaceDark,
|
||||||
|
elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
side: const BorderSide(color: AppColors.borderDark),
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
|
||||||
|
snackBarTheme: SnackBarThemeData(
|
||||||
|
backgroundColor: AppColors.surfaceDark,
|
||||||
|
contentTextStyle: AppTypography.bodyTextSmall.copyWith(
|
||||||
|
color: AppColors.textPrimaryDark,
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
behavior: SnackBarBehavior.fixed,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/// Thème Sophistiqué UnionFlow
|
/// Thème Sophistiqué UnionFlow — Material Design 3
|
||||||
///
|
///
|
||||||
/// Implémentation complète du design system avec les dernières tendances UI/UX 2024-2025
|
/// Palette WCAG 2.1 validée : #2563EB (primary) + #7616E8 (accent)
|
||||||
/// Architecture modulaire et tokens de design cohérents
|
|
||||||
library app_theme_sophisticated;
|
library app_theme_sophisticated;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -10,197 +9,389 @@ import '../tokens/color_tokens.dart';
|
|||||||
import '../tokens/typography_tokens.dart';
|
import '../tokens/typography_tokens.dart';
|
||||||
import '../tokens/spacing_tokens.dart';
|
import '../tokens/spacing_tokens.dart';
|
||||||
|
|
||||||
/// Thème principal de l'application UnionFlow
|
|
||||||
class AppThemeSophisticated {
|
class AppThemeSophisticated {
|
||||||
AppThemeSophisticated._();
|
AppThemeSophisticated._();
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// THÈME PRINCIPAL - Configuration complète
|
// THÈME CLAIR
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
/// Thème clair principal
|
|
||||||
static ThemeData get lightTheme {
|
static ThemeData get lightTheme {
|
||||||
return ThemeData(
|
return ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
|
|
||||||
// Couleurs principales
|
|
||||||
colorScheme: _lightColorScheme,
|
colorScheme: _lightColorScheme,
|
||||||
|
|
||||||
// Typographie (Playfair Display display + Inter body)
|
|
||||||
textTheme: _textTheme,
|
textTheme: _textTheme,
|
||||||
|
|
||||||
// Configuration de l'AppBar
|
|
||||||
appBarTheme: _appBarTheme,
|
appBarTheme: _appBarTheme,
|
||||||
|
|
||||||
// Configuration des cartes
|
|
||||||
cardTheme: _cardTheme,
|
cardTheme: _cardTheme,
|
||||||
|
|
||||||
// Configuration des boutons
|
|
||||||
elevatedButtonTheme: _elevatedButtonTheme,
|
elevatedButtonTheme: _elevatedButtonTheme,
|
||||||
filledButtonTheme: _filledButtonTheme,
|
filledButtonTheme: _filledButtonTheme,
|
||||||
outlinedButtonTheme: _outlinedButtonTheme,
|
outlinedButtonTheme: _outlinedButtonTheme,
|
||||||
textButtonTheme: _textButtonTheme,
|
textButtonTheme: _textButtonTheme,
|
||||||
|
|
||||||
// Configuration des champs de saisie
|
|
||||||
inputDecorationTheme: _inputDecorationTheme,
|
inputDecorationTheme: _inputDecorationTheme,
|
||||||
|
|
||||||
// Configuration de la navigation
|
|
||||||
navigationBarTheme: _navigationBarTheme,
|
navigationBarTheme: _navigationBarTheme,
|
||||||
navigationDrawerTheme: _navigationDrawerTheme,
|
navigationDrawerTheme: _navigationDrawerTheme,
|
||||||
|
|
||||||
// Configuration des dialogues
|
|
||||||
dialogTheme: _dialogTheme,
|
dialogTheme: _dialogTheme,
|
||||||
|
|
||||||
// Configuration des snackbars
|
|
||||||
snackBarTheme: _snackBarTheme,
|
snackBarTheme: _snackBarTheme,
|
||||||
|
|
||||||
// Configuration des puces
|
|
||||||
chipTheme: _chipTheme,
|
chipTheme: _chipTheme,
|
||||||
|
|
||||||
// Configuration des listes
|
|
||||||
listTileTheme: _listTileTheme,
|
listTileTheme: _listTileTheme,
|
||||||
|
|
||||||
// Configuration des onglets
|
|
||||||
tabBarTheme: _tabBarTheme,
|
tabBarTheme: _tabBarTheme,
|
||||||
|
|
||||||
// Configuration des dividers
|
|
||||||
dividerTheme: _dividerTheme,
|
dividerTheme: _dividerTheme,
|
||||||
|
|
||||||
// Configuration des icônes
|
|
||||||
iconTheme: _iconTheme,
|
iconTheme: _iconTheme,
|
||||||
|
scaffoldBackgroundColor: ColorTokens.background,
|
||||||
// Configuration des surfaces
|
|
||||||
scaffoldBackgroundColor: ColorTokens.surface,
|
|
||||||
canvasColor: ColorTokens.surface,
|
canvasColor: ColorTokens.surface,
|
||||||
|
|
||||||
// Configuration des animations
|
|
||||||
pageTransitionsTheme: _pageTransitionsTheme,
|
pageTransitionsTheme: _pageTransitionsTheme,
|
||||||
|
extensions: const [_customColors, _customSpacing],
|
||||||
// Configuration des extensions
|
|
||||||
extensions: const [
|
|
||||||
_customColors,
|
|
||||||
_customSpacing,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Thème sombre — Vert Ardoise (#1A2E1A)
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// THÈME SOMBRE — Navy Profond (#0A0D1A)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
static ThemeData get darkTheme {
|
static ThemeData get darkTheme {
|
||||||
return ThemeData(
|
return ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
colorScheme: const ColorScheme.dark(
|
colorScheme: _darkColorScheme,
|
||||||
primary: Color(0xFF4CAF50), // Vert clair sur fond sombre
|
textTheme: _textThemeDark,
|
||||||
onPrimary: Color(0xFF003908),
|
scaffoldBackgroundColor: ColorTokens.backgroundDark,
|
||||||
primaryContainer: Color(0xFF1E3A1E),
|
canvasColor: ColorTokens.surfaceDark,
|
||||||
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(
|
appBarTheme: const AppBarTheme(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: Color(0xFFE0F2E0),
|
foregroundColor: ColorTokens.onSurfaceDark,
|
||||||
surfaceTintColor: Colors.transparent,
|
surfaceTintColor: Colors.transparent,
|
||||||
|
systemOverlayStyle: SystemUiOverlayStyle(
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
statusBarIconBrightness: Brightness.light, // icônes blanches en dark
|
||||||
|
statusBarBrightness: Brightness.dark, // pour iOS
|
||||||
|
systemNavigationBarColor: Colors.transparent,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
dividerTheme: const DividerThemeData(
|
||||||
|
color: ColorTokens.outlineDark,
|
||||||
|
thickness: 1.0,
|
||||||
|
space: SpacingTokens.md,
|
||||||
|
),
|
||||||
|
cardTheme: CardThemeData(
|
||||||
|
color: ColorTokens.surfaceDark,
|
||||||
|
elevation: 0,
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
surfaceTintColor: Colors.transparent,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
|
||||||
|
side: const BorderSide(color: ColorTokens.outlineDark),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(SpacingTokens.cardMargin),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
elevation: SpacingTokens.elevationSm,
|
||||||
|
backgroundColor: ColorTokens.primaryLight,
|
||||||
|
foregroundColor: ColorTokens.onPrimaryContainer,
|
||||||
|
textStyle: TypographyTokens.buttonMedium,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
||||||
|
vertical: SpacingTokens.buttonPaddingVertical,
|
||||||
|
),
|
||||||
|
minimumSize: const Size(SpacingTokens.minButtonWidth, SpacingTokens.buttonHeightMedium),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
filledButtonTheme: FilledButtonThemeData(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: ColorTokens.primaryLight,
|
||||||
|
foregroundColor: ColorTokens.onPrimaryContainer,
|
||||||
|
textStyle: TypographyTokens.buttonMedium,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
||||||
|
vertical: SpacingTokens.buttonPaddingVertical,
|
||||||
|
),
|
||||||
|
minimumSize: const Size(SpacingTokens.minButtonWidth, SpacingTokens.buttonHeightMedium),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: ColorTokens.primaryLight,
|
||||||
|
textStyle: TypographyTokens.buttonMedium,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
||||||
|
vertical: SpacingTokens.buttonPaddingVertical,
|
||||||
|
),
|
||||||
|
minimumSize: const Size(SpacingTokens.minButtonWidth, SpacingTokens.buttonHeightMedium),
|
||||||
|
side: const BorderSide(color: ColorTokens.outlineDark),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: ColorTokens.primaryLight,
|
||||||
|
textStyle: TypographyTokens.buttonMedium,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: SpacingTokens.buttonPaddingHorizontal,
|
||||||
|
vertical: SpacingTokens.buttonPaddingVertical,
|
||||||
|
),
|
||||||
|
minimumSize: const Size(SpacingTokens.minButtonWidth, SpacingTokens.buttonHeightMedium),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
filled: true,
|
||||||
|
fillColor: ColorTokens.surfaceContainerDark,
|
||||||
|
labelStyle: TypographyTokens.inputLabel.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceVariantDark,
|
||||||
|
),
|
||||||
|
hintStyle: TypographyTokens.inputHint.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceVariantDark,
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
borderSide: const BorderSide(color: ColorTokens.outlineDark),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
borderSide: const BorderSide(color: ColorTokens.outlineDark),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
borderSide: const BorderSide(color: ColorTokens.primaryLight, width: 2.0),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
borderSide: const BorderSide(color: ColorTokens.error),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.all(SpacingTokens.formPadding),
|
||||||
|
),
|
||||||
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
|
backgroundColor: ColorTokens.navigationBackgroundDark,
|
||||||
|
indicatorColor: ColorTokens.navigationIndicatorDark,
|
||||||
|
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.selected)) {
|
||||||
|
return TypographyTokens.navigationLabelSelected.copyWith(
|
||||||
|
color: ColorTokens.navigationSelectedDark,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return TypographyTokens.navigationLabel.copyWith(
|
||||||
|
color: ColorTokens.navigationUnselectedDark,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
iconTheme: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.selected)) {
|
||||||
|
return const IconThemeData(color: ColorTokens.navigationSelectedDark);
|
||||||
|
}
|
||||||
|
return const IconThemeData(color: ColorTokens.navigationUnselectedDark);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
navigationDrawerTheme: NavigationDrawerThemeData(
|
||||||
|
backgroundColor: ColorTokens.surfaceContainerDark,
|
||||||
|
elevation: SpacingTokens.elevationMd,
|
||||||
|
shadowColor: Colors.black,
|
||||||
|
surfaceTintColor: Colors.transparent,
|
||||||
|
indicatorColor: ColorTokens.navigationIndicatorDark,
|
||||||
|
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.selected)) {
|
||||||
|
return TypographyTokens.navigationLabelSelected.copyWith(
|
||||||
|
color: ColorTokens.navigationSelectedDark,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return TypographyTokens.navigationLabel.copyWith(
|
||||||
|
color: ColorTokens.navigationUnselectedDark,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
dialogTheme: DialogThemeData(
|
||||||
|
backgroundColor: ColorTokens.surfaceDark,
|
||||||
|
elevation: SpacingTokens.elevationLg,
|
||||||
|
surfaceTintColor: Colors.transparent,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusXl),
|
||||||
|
),
|
||||||
|
titleTextStyle: TypographyTokens.headlineSmall.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceDark,
|
||||||
|
),
|
||||||
|
contentTextStyle: TypographyTokens.bodyMedium.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceVariantDark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
snackBarTheme: SnackBarThemeData(
|
||||||
|
backgroundColor: ColorTokens.surfaceVariantDark,
|
||||||
|
contentTextStyle: TypographyTokens.bodyMedium.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceDark,
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
),
|
||||||
|
behavior: SnackBarBehavior.fixed,
|
||||||
|
),
|
||||||
|
chipTheme: ChipThemeData(
|
||||||
|
backgroundColor: ColorTokens.surfaceContainerDark,
|
||||||
|
selectedColor: ColorTokens.navigationIndicatorDark,
|
||||||
|
labelStyle: TypographyTokens.labelMedium.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceDark,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: SpacingTokens.md,
|
||||||
|
vertical: SpacingTokens.sm,
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
listTileTheme: ListTileThemeData(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: SpacingTokens.xl,
|
||||||
|
vertical: SpacingTokens.md,
|
||||||
|
),
|
||||||
|
tileColor: ColorTokens.surfaceDark,
|
||||||
|
titleTextStyle: TypographyTokens.titleMedium.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceDark,
|
||||||
|
),
|
||||||
|
subtitleTextStyle: TypographyTokens.bodyMedium.copyWith(
|
||||||
|
color: ColorTokens.onSurfaceVariantDark,
|
||||||
|
),
|
||||||
|
minVerticalPadding: SpacingTokens.md,
|
||||||
|
),
|
||||||
|
tabBarTheme: TabBarThemeData(
|
||||||
|
labelColor: ColorTokens.primaryLight,
|
||||||
|
unselectedLabelColor: ColorTokens.onSurfaceVariantDark,
|
||||||
|
labelStyle: TypographyTokens.titleSmall,
|
||||||
|
unselectedLabelStyle: TypographyTokens.titleSmall,
|
||||||
|
indicator: UnderlineTabIndicator(
|
||||||
|
borderSide: const BorderSide(color: ColorTokens.primaryLight, width: 2.0),
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusXs),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
iconTheme: const IconThemeData(
|
||||||
|
color: ColorTokens.onSurfaceVariantDark,
|
||||||
|
size: 24.0,
|
||||||
|
),
|
||||||
|
pageTransitionsTheme: _pageTransitionsTheme,
|
||||||
|
extensions: const [_customColors, _customSpacing],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// SCHÉMA DE COULEURS
|
// COLOR SCHEME SOMBRE — Navy profond OLED
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const ColorScheme _darkColorScheme = ColorScheme.dark(
|
||||||
|
primary: ColorTokens.primaryLight, // #60A5FA sur fond sombre
|
||||||
|
onPrimary: ColorTokens.onPrimaryContainer, // #1E3A8A
|
||||||
|
primaryContainer: ColorTokens.navigationIndicatorDark, // #1A2350
|
||||||
|
onPrimaryContainer: Color(0xFFD6E8FF),
|
||||||
|
secondary: ColorTokens.secondaryLight, // #9B59F0
|
||||||
|
onSecondary: ColorTokens.onSecondaryContainer, // #2A006F
|
||||||
|
secondaryContainer: Color(0xFF3B1F6A),
|
||||||
|
onSecondaryContainer: Color(0xFFEAD5FF),
|
||||||
|
surface: ColorTokens.surfaceDark, // #161B26
|
||||||
|
onSurface: ColorTokens.onSurfaceDark, // #F1F5FF
|
||||||
|
surfaceContainerHighest: ColorTokens.surfaceVariantDark, // #1A1F2E
|
||||||
|
onSurfaceVariant: ColorTokens.onSurfaceVariantDark, // #94A3B8
|
||||||
|
error: ColorTokens.error,
|
||||||
|
onError: Colors.white,
|
||||||
|
errorContainer: Color(0xFF5C1111),
|
||||||
|
onErrorContainer: ColorTokens.errorLight,
|
||||||
|
outline: ColorTokens.outlineDark, // #2D3554
|
||||||
|
outlineVariant: Color(0xFF1F2B3E),
|
||||||
|
shadow: ColorTokens.backgroundDark,
|
||||||
|
scrim: Colors.black,
|
||||||
|
inverseSurface: ColorTokens.onSurfaceDark,
|
||||||
|
onInverseSurface: ColorTokens.surfaceDark,
|
||||||
|
inversePrimary: ColorTokens.primary,
|
||||||
|
);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// COLOR SCHEME CLAIR
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
static const ColorScheme _lightColorScheme = ColorScheme.light(
|
static const ColorScheme _lightColorScheme = ColorScheme.light(
|
||||||
// Couleurs primaires
|
|
||||||
primary: ColorTokens.primary,
|
primary: ColorTokens.primary,
|
||||||
onPrimary: ColorTokens.onPrimary,
|
onPrimary: ColorTokens.onPrimary,
|
||||||
primaryContainer: ColorTokens.primaryContainer,
|
primaryContainer: ColorTokens.primaryContainer,
|
||||||
onPrimaryContainer: ColorTokens.onPrimaryContainer,
|
onPrimaryContainer: ColorTokens.onPrimaryContainer,
|
||||||
|
|
||||||
// Couleurs secondaires
|
|
||||||
secondary: ColorTokens.secondary,
|
secondary: ColorTokens.secondary,
|
||||||
onSecondary: ColorTokens.onSecondary,
|
onSecondary: ColorTokens.onSecondary,
|
||||||
secondaryContainer: ColorTokens.secondaryContainer,
|
secondaryContainer: ColorTokens.secondaryContainer,
|
||||||
onSecondaryContainer: ColorTokens.onSecondaryContainer,
|
onSecondaryContainer: ColorTokens.onSecondaryContainer,
|
||||||
|
|
||||||
// Couleurs tertiaires
|
|
||||||
tertiary: ColorTokens.tertiary,
|
tertiary: ColorTokens.tertiary,
|
||||||
onTertiary: ColorTokens.onTertiary,
|
onTertiary: ColorTokens.onTertiary,
|
||||||
tertiaryContainer: ColorTokens.tertiaryContainer,
|
tertiaryContainer: ColorTokens.tertiaryContainer,
|
||||||
onTertiaryContainer: ColorTokens.onTertiaryContainer,
|
onTertiaryContainer: ColorTokens.onTertiaryContainer,
|
||||||
|
|
||||||
// Couleurs d'erreur
|
|
||||||
error: ColorTokens.error,
|
error: ColorTokens.error,
|
||||||
onError: ColorTokens.onError,
|
onError: ColorTokens.onError,
|
||||||
errorContainer: ColorTokens.errorContainer,
|
errorContainer: ColorTokens.errorContainer,
|
||||||
onErrorContainer: ColorTokens.onErrorContainer,
|
onErrorContainer: ColorTokens.onErrorContainer,
|
||||||
|
|
||||||
// Couleurs de surface
|
|
||||||
surface: ColorTokens.surface,
|
surface: ColorTokens.surface,
|
||||||
onSurface: ColorTokens.onSurface,
|
onSurface: ColorTokens.onSurface,
|
||||||
surfaceContainerHighest: ColorTokens.surfaceVariant,
|
surfaceContainerHighest: ColorTokens.surfaceVariant,
|
||||||
onSurfaceVariant: ColorTokens.onSurfaceVariant,
|
onSurfaceVariant: ColorTokens.onSurfaceVariant,
|
||||||
|
|
||||||
// Couleurs de contour
|
|
||||||
outline: ColorTokens.outline,
|
outline: ColorTokens.outline,
|
||||||
outlineVariant: ColorTokens.outlineVariant,
|
outlineVariant: ColorTokens.outlineVariant,
|
||||||
|
|
||||||
// Couleurs d'ombre
|
|
||||||
shadow: ColorTokens.shadow,
|
shadow: ColorTokens.shadow,
|
||||||
scrim: ColorTokens.shadow,
|
scrim: ColorTokens.shadow,
|
||||||
|
|
||||||
// Couleurs d'inversion
|
|
||||||
inverseSurface: ColorTokens.onSurface,
|
inverseSurface: ColorTokens.onSurface,
|
||||||
onInverseSurface: ColorTokens.surface,
|
onInverseSurface: ColorTokens.surface,
|
||||||
inversePrimary: ColorTokens.primaryLight,
|
inversePrimary: ColorTokens.primaryLight,
|
||||||
);
|
);
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// THÈME TYPOGRAPHIQUE
|
// TYPOGRAPHIE
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
static TextTheme get _textTheme => TextTheme(
|
static TextTheme get _textTheme => TextTheme(
|
||||||
// Display styles — Playfair Display (GoogleFonts, non-const)
|
|
||||||
displayLarge: TypographyTokens.displayLarge,
|
displayLarge: TypographyTokens.displayLarge,
|
||||||
displayMedium: TypographyTokens.displayMedium,
|
displayMedium: TypographyTokens.displayMedium,
|
||||||
displaySmall: TypographyTokens.displaySmall,
|
displaySmall: TypographyTokens.displaySmall,
|
||||||
|
|
||||||
// Headline styles
|
|
||||||
headlineLarge: TypographyTokens.headlineLarge,
|
headlineLarge: TypographyTokens.headlineLarge,
|
||||||
headlineMedium: TypographyTokens.headlineMedium,
|
headlineMedium: TypographyTokens.headlineMedium,
|
||||||
headlineSmall: TypographyTokens.headlineSmall,
|
headlineSmall: TypographyTokens.headlineSmall,
|
||||||
|
|
||||||
// Title styles
|
|
||||||
titleLarge: TypographyTokens.titleLarge,
|
titleLarge: TypographyTokens.titleLarge,
|
||||||
titleMedium: TypographyTokens.titleMedium,
|
titleMedium: TypographyTokens.titleMedium,
|
||||||
titleSmall: TypographyTokens.titleSmall,
|
titleSmall: TypographyTokens.titleSmall,
|
||||||
|
|
||||||
// Label styles
|
|
||||||
labelLarge: TypographyTokens.labelLarge,
|
labelLarge: TypographyTokens.labelLarge,
|
||||||
labelMedium: TypographyTokens.labelMedium,
|
labelMedium: TypographyTokens.labelMedium,
|
||||||
labelSmall: TypographyTokens.labelSmall,
|
labelSmall: TypographyTokens.labelSmall,
|
||||||
|
|
||||||
// Body styles
|
|
||||||
bodyLarge: TypographyTokens.bodyLarge,
|
bodyLarge: TypographyTokens.bodyLarge,
|
||||||
bodyMedium: TypographyTokens.bodyMedium,
|
bodyMedium: TypographyTokens.bodyMedium,
|
||||||
bodySmall: TypographyTokens.bodySmall,
|
bodySmall: TypographyTokens.bodySmall,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Thème typographique dark — même structure que _textTheme mais avec
|
||||||
|
/// ColorTokens.onSurfaceDark (#F1F5FF) pour que le texte soit lisible
|
||||||
|
/// sur les fonds sombres (#0A0D1A / #161B26).
|
||||||
|
static TextTheme get _textThemeDark => TextTheme(
|
||||||
|
displayLarge: TypographyTokens.displayLarge.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
displayMedium: TypographyTokens.displayMedium.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
displaySmall: TypographyTokens.displaySmall.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
headlineLarge: TypographyTokens.headlineLarge.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
headlineMedium: TypographyTokens.headlineMedium.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
headlineSmall: TypographyTokens.headlineSmall.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
titleLarge: TypographyTokens.titleLarge.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
titleMedium: TypographyTokens.titleMedium.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
titleSmall: TypographyTokens.titleSmall.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
labelLarge: TypographyTokens.labelLarge.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
labelMedium: TypographyTokens.labelMedium.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
labelSmall: TypographyTokens.labelSmall.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
bodyLarge: TypographyTokens.bodyLarge.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
bodyMedium: TypographyTokens.bodyMedium.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
bodySmall: TypographyTokens.bodySmall.copyWith(color: ColorTokens.onSurfaceDark),
|
||||||
|
);
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// THÈMES DE COMPOSANTS
|
// COMPOSANTS
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
/// Configuration AppBar moderne (sans AppBar traditionnelle)
|
|
||||||
static const AppBarTheme _appBarTheme = AppBarTheme(
|
static const AppBarTheme _appBarTheme = AppBarTheme(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
@@ -214,19 +405,20 @@ class AppThemeSophisticated {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des cartes sophistiquées
|
|
||||||
static final CardThemeData _cardTheme = CardThemeData(
|
static final CardThemeData _cardTheme = CardThemeData(
|
||||||
elevation: SpacingTokens.elevationSm,
|
elevation: SpacingTokens.elevationSm,
|
||||||
shadowColor: ColorTokens.shadow,
|
shadowColor: ColorTokens.shadow,
|
||||||
surfaceTintColor: ColorTokens.surfaceContainer,
|
surfaceTintColor: Colors.transparent,
|
||||||
|
color: ColorTokens.surface,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusLg),
|
||||||
|
side: const BorderSide(color: ColorTokens.outline),
|
||||||
),
|
),
|
||||||
margin: const EdgeInsets.all(SpacingTokens.cardMargin),
|
margin: const EdgeInsets.all(SpacingTokens.cardMargin),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des boutons élevés
|
static final ElevatedButtonThemeData _elevatedButtonTheme =
|
||||||
static final ElevatedButtonThemeData _elevatedButtonTheme = ElevatedButtonThemeData(
|
ElevatedButtonThemeData(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
elevation: SpacingTokens.elevationSm,
|
elevation: SpacingTokens.elevationSm,
|
||||||
shadowColor: ColorTokens.shadow,
|
shadowColor: ColorTokens.shadow,
|
||||||
@@ -247,8 +439,8 @@ class AppThemeSophisticated {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des boutons remplis
|
static final FilledButtonThemeData _filledButtonTheme =
|
||||||
static final FilledButtonThemeData _filledButtonTheme = FilledButtonThemeData(
|
FilledButtonThemeData(
|
||||||
style: FilledButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
backgroundColor: ColorTokens.primary,
|
backgroundColor: ColorTokens.primary,
|
||||||
foregroundColor: ColorTokens.onPrimary,
|
foregroundColor: ColorTokens.onPrimary,
|
||||||
@@ -267,8 +459,8 @@ class AppThemeSophisticated {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des boutons avec contour
|
static final OutlinedButtonThemeData _outlinedButtonTheme =
|
||||||
static final OutlinedButtonThemeData _outlinedButtonTheme = OutlinedButtonThemeData(
|
OutlinedButtonThemeData(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: ColorTokens.primary,
|
foregroundColor: ColorTokens.primary,
|
||||||
textStyle: TypographyTokens.buttonMedium,
|
textStyle: TypographyTokens.buttonMedium,
|
||||||
@@ -280,17 +472,13 @@ class AppThemeSophisticated {
|
|||||||
SpacingTokens.minButtonWidth,
|
SpacingTokens.minButtonWidth,
|
||||||
SpacingTokens.buttonHeightMedium,
|
SpacingTokens.buttonHeightMedium,
|
||||||
),
|
),
|
||||||
side: const BorderSide(
|
side: const BorderSide(color: ColorTokens.outline),
|
||||||
color: ColorTokens.outline,
|
|
||||||
width: 1.0,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des boutons texte
|
|
||||||
static final TextButtonThemeData _textButtonTheme = TextButtonThemeData(
|
static final TextButtonThemeData _textButtonTheme = TextButtonThemeData(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: ColorTokens.primary,
|
foregroundColor: ColorTokens.primary,
|
||||||
@@ -309,8 +497,8 @@ class AppThemeSophisticated {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des champs de saisie
|
static final InputDecorationTheme _inputDecorationTheme =
|
||||||
static final InputDecorationTheme _inputDecorationTheme = InputDecorationTheme(
|
InputDecorationTheme(
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: ColorTokens.surfaceContainer,
|
fillColor: ColorTokens.surfaceContainer,
|
||||||
labelStyle: TypographyTokens.inputLabel,
|
labelStyle: TypographyTokens.inputLabel,
|
||||||
@@ -334,8 +522,8 @@ class AppThemeSophisticated {
|
|||||||
contentPadding: const EdgeInsets.all(SpacingTokens.formPadding),
|
contentPadding: const EdgeInsets.all(SpacingTokens.formPadding),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration de la barre de navigation
|
static final NavigationBarThemeData _navigationBarTheme =
|
||||||
static final NavigationBarThemeData _navigationBarTheme = NavigationBarThemeData(
|
NavigationBarThemeData(
|
||||||
backgroundColor: ColorTokens.navigationBackground,
|
backgroundColor: ColorTokens.navigationBackground,
|
||||||
indicatorColor: ColorTokens.navigationIndicator,
|
indicatorColor: ColorTokens.navigationIndicator,
|
||||||
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
||||||
@@ -352,12 +540,12 @@ class AppThemeSophisticated {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration du drawer de navigation
|
static final NavigationDrawerThemeData _navigationDrawerTheme =
|
||||||
static final NavigationDrawerThemeData _navigationDrawerTheme = NavigationDrawerThemeData(
|
NavigationDrawerThemeData(
|
||||||
backgroundColor: ColorTokens.surfaceContainer,
|
backgroundColor: ColorTokens.surfaceContainer,
|
||||||
elevation: SpacingTokens.elevationMd,
|
elevation: SpacingTokens.elevationMd,
|
||||||
shadowColor: ColorTokens.shadow,
|
shadowColor: ColorTokens.shadow,
|
||||||
surfaceTintColor: ColorTokens.surfaceContainer,
|
surfaceTintColor: Colors.transparent,
|
||||||
indicatorColor: ColorTokens.primaryContainer,
|
indicatorColor: ColorTokens.primaryContainer,
|
||||||
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
if (states.contains(WidgetState.selected)) {
|
||||||
@@ -367,12 +555,11 @@ class AppThemeSophisticated {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des dialogues
|
|
||||||
static final DialogThemeData _dialogTheme = DialogThemeData(
|
static final DialogThemeData _dialogTheme = DialogThemeData(
|
||||||
backgroundColor: ColorTokens.surfaceContainer,
|
backgroundColor: ColorTokens.surface,
|
||||||
elevation: SpacingTokens.elevationLg,
|
elevation: SpacingTokens.elevationLg,
|
||||||
shadowColor: ColorTokens.shadow,
|
shadowColor: ColorTokens.shadow,
|
||||||
surfaceTintColor: ColorTokens.surfaceContainer,
|
surfaceTintColor: Colors.transparent,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(SpacingTokens.radiusXl),
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusXl),
|
||||||
),
|
),
|
||||||
@@ -380,7 +567,6 @@ class AppThemeSophisticated {
|
|||||||
contentTextStyle: TypographyTokens.bodyMedium,
|
contentTextStyle: TypographyTokens.bodyMedium,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des snackbars (fixed pour éviter "Floating SnackBar off screen" avec bottomNavigationBar)
|
|
||||||
static final SnackBarThemeData _snackBarTheme = SnackBarThemeData(
|
static final SnackBarThemeData _snackBarTheme = SnackBarThemeData(
|
||||||
backgroundColor: ColorTokens.onSurface,
|
backgroundColor: ColorTokens.onSurface,
|
||||||
contentTextStyle: TypographyTokens.bodyMedium.copyWith(
|
contentTextStyle: TypographyTokens.bodyMedium.copyWith(
|
||||||
@@ -392,7 +578,6 @@ class AppThemeSophisticated {
|
|||||||
behavior: SnackBarBehavior.fixed,
|
behavior: SnackBarBehavior.fixed,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des puces
|
|
||||||
static final ChipThemeData _chipTheme = ChipThemeData(
|
static final ChipThemeData _chipTheme = ChipThemeData(
|
||||||
backgroundColor: ColorTokens.surfaceVariant,
|
backgroundColor: ColorTokens.surfaceVariant,
|
||||||
selectedColor: ColorTokens.primaryContainer,
|
selectedColor: ColorTokens.primaryContainer,
|
||||||
@@ -406,7 +591,6 @@ class AppThemeSophisticated {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des éléments de liste
|
|
||||||
static const ListTileThemeData _listTileTheme = ListTileThemeData(
|
static const ListTileThemeData _listTileTheme = ListTileThemeData(
|
||||||
contentPadding: EdgeInsets.symmetric(
|
contentPadding: EdgeInsets.symmetric(
|
||||||
horizontal: SpacingTokens.xl,
|
horizontal: SpacingTokens.xl,
|
||||||
@@ -418,7 +602,6 @@ class AppThemeSophisticated {
|
|||||||
minVerticalPadding: SpacingTokens.md,
|
minVerticalPadding: SpacingTokens.md,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des onglets
|
|
||||||
static final TabBarThemeData _tabBarTheme = TabBarThemeData(
|
static final TabBarThemeData _tabBarTheme = TabBarThemeData(
|
||||||
labelColor: ColorTokens.primary,
|
labelColor: ColorTokens.primary,
|
||||||
unselectedLabelColor: ColorTokens.onSurfaceVariant,
|
unselectedLabelColor: ColorTokens.onSurfaceVariant,
|
||||||
@@ -433,56 +616,43 @@ class AppThemeSophisticated {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des dividers
|
|
||||||
static const DividerThemeData _dividerTheme = DividerThemeData(
|
static const DividerThemeData _dividerTheme = DividerThemeData(
|
||||||
color: ColorTokens.outline,
|
color: ColorTokens.outline,
|
||||||
thickness: 1.0,
|
thickness: 1.0,
|
||||||
space: SpacingTokens.md,
|
space: SpacingTokens.md,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des icônes
|
|
||||||
static const IconThemeData _iconTheme = IconThemeData(
|
static const IconThemeData _iconTheme = IconThemeData(
|
||||||
color: ColorTokens.onSurfaceVariant,
|
color: ColorTokens.onSurfaceVariant,
|
||||||
size: 24.0,
|
size: 24.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Configuration des transitions de page
|
static const PageTransitionsTheme _pageTransitionsTheme =
|
||||||
static const PageTransitionsTheme _pageTransitionsTheme = PageTransitionsTheme(
|
PageTransitionsTheme(
|
||||||
builders: {
|
builders: {
|
||||||
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
||||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Extensions personnalisées - Couleurs
|
|
||||||
static const CustomColors _customColors = CustomColors();
|
static const CustomColors _customColors = CustomColors();
|
||||||
|
|
||||||
/// Extensions personnalisées - Espacements
|
|
||||||
static const CustomSpacing _customSpacing = CustomSpacing();
|
static const CustomSpacing _customSpacing = CustomSpacing();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extension de couleurs personnalisées
|
|
||||||
class CustomColors extends ThemeExtension<CustomColors> {
|
class CustomColors extends ThemeExtension<CustomColors> {
|
||||||
const CustomColors();
|
const CustomColors();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CustomColors copyWith() => const CustomColors();
|
CustomColors copyWith() => const CustomColors();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CustomColors lerp(ThemeExtension<CustomColors>? other, double t) {
|
CustomColors lerp(ThemeExtension<CustomColors>? other, double t) =>
|
||||||
return const CustomColors();
|
const CustomColors();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extension d'espacements personnalisés
|
|
||||||
class CustomSpacing extends ThemeExtension<CustomSpacing> {
|
class CustomSpacing extends ThemeExtension<CustomSpacing> {
|
||||||
const CustomSpacing();
|
const CustomSpacing();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CustomSpacing copyWith() => const CustomSpacing();
|
CustomSpacing copyWith() => const CustomSpacing();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CustomSpacing lerp(ThemeExtension<CustomSpacing>? other, double t) {
|
CustomSpacing lerp(ThemeExtension<CustomSpacing>? other, double t) =>
|
||||||
return const CustomSpacing();
|
const CustomSpacing();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,524 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// UnionFlow Mobile App - Couleurs Globales (DRY)
|
/// UnionFlow Mobile App — Source unique de vérité pour toutes les couleurs
|
||||||
/// Palette : Vert Forêt (Jour) / Vert Ardoise (Nuit)
|
///
|
||||||
|
/// Palette WCAG 2.1 validée, alignée sur le logo SVG (gradient #297FFF→#7616E8)
|
||||||
|
/// et le frontend web Freya.
|
||||||
|
///
|
||||||
|
/// ╔══════════════════════════════════════════════════════════════════════════╗
|
||||||
|
/// ║ RÈGLES D'USAGE ║
|
||||||
|
/// ║ • Texte sur fond clair → textPrimary / textTitle (WCAG AAA/AA) ║
|
||||||
|
/// ║ • Texte sur fond sombre → textPrimaryDark / textTitleDark ║
|
||||||
|
/// ║ • Boutons / icônes UI → primary (WCAG AA) ║
|
||||||
|
/// ║ • Texte sur bouton coloré → onPrimary / onError / onGradient ║
|
||||||
|
/// ║ • Gradient/branding → logoGradient (décoratif) ║
|
||||||
|
/// ║ • Accent violet UI → accent (≥3:1) ║
|
||||||
|
/// ║ • Fond carte/surface → surface / surfaceDark ║
|
||||||
|
/// ║ • Fond page → background / backgroundDark ║
|
||||||
|
/// ║ • Fond légèrement teinté → backgroundSubtle / backgroundSubtleDark ║
|
||||||
|
/// ║ • Bordures → border / borderDark ║
|
||||||
|
/// ║ • Ombres → shadow / shadowMedium / softShadow ║
|
||||||
|
/// ╚══════════════════════════════════════════════════════════════════════════╝
|
||||||
class AppColors {
|
class AppColors {
|
||||||
// --- Branding ---
|
AppColors._();
|
||||||
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(0xFFF1F8E9); // Teinte verte très légère
|
// LOGO
|
||||||
static const Color lightSurface = Color(0xFFFFFFFF); // Blanc pur pour les cartes
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
static const Color lightBorder = Color(0xFFC8E6C9); // Bordure vert pâle
|
|
||||||
|
|
||||||
// --- Mode Nuit (Dark) ---
|
/// #297FFF — Bleu logo (début du gradient décoratif — décoratif uniquement)
|
||||||
static const Color darkBackground = Color(0xFF0F1A0F); // Vert noir profond
|
static const Color logoBlue = Color(0xFF297FFF);
|
||||||
static const Color darkSurface = Color(0xFF1A2E1A); // Vert ardoise
|
|
||||||
static const Color darkBorder = Color(0xFF3A5E3A); // Bordure sombre
|
|
||||||
|
|
||||||
// --- Texte ---
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
static const Color textPrimaryLight = Color(0xFF1C2B1C); // Vert très foncé / quasi noir
|
// PRIMAIRE — Bleu UnionFlow
|
||||||
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 ---
|
/// #2563EB — Bleu primaire UI [5.7:1 sur blanc ✅ WCAG AA]
|
||||||
|
static const Color primary = Color(0xFF2563EB);
|
||||||
|
|
||||||
|
/// #60A5FA — Bleu clair (dark mode, fonds colorés)
|
||||||
|
static const Color primaryLight = Color(0xFF60A5FA);
|
||||||
|
|
||||||
|
/// #1D4ED8 — Bleu foncé [7.2:1 sur blanc ✅ WCAG AAA]
|
||||||
|
static const Color primaryDark = Color(0xFF1D4ED8);
|
||||||
|
|
||||||
|
/// #EFF6FF — Container / fond clair teinté bleu
|
||||||
|
static const Color primaryContainer = Color(0xFFEFF6FF);
|
||||||
|
|
||||||
|
/// #1E3A8A — Contenu sur container primaire
|
||||||
|
static const Color onPrimaryContainer = Color(0xFF1E3A8A);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ACCENT / SECONDAIRE — Violet logo
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #7616E8 — Violet accent (gradient fin logo, UI ≥3:1, NON pour texte)
|
||||||
|
static const Color accent = Color(0xFF7616E8);
|
||||||
|
|
||||||
|
/// #9B59F0 — Violet clair
|
||||||
|
static const Color accentLight = Color(0xFF9B59F0);
|
||||||
|
|
||||||
|
/// #5B0FBA — Violet foncé [4.8:1 sur blanc ✅ pour texte]
|
||||||
|
static const Color accentDark = Color(0xFF5B0FBA);
|
||||||
|
|
||||||
|
/// #F3ECFF — Container accent clair
|
||||||
|
static const Color accentContainer = Color(0xFFF3ECFF);
|
||||||
|
|
||||||
|
/// #2A006F — Contenu sur container accent
|
||||||
|
static const Color onAccentContainer = Color(0xFF2A006F);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// TERTIAIRE — Bleu intermédiaire (MD3)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #5297FF — Bleu intermédiaire (mi-chemin logo)
|
||||||
|
static const Color tertiary = Color(0xFF5297FF);
|
||||||
|
|
||||||
|
/// #85BAFF — Tertiaire clair
|
||||||
|
static const Color tertiaryLight = Color(0xFF85BAFF);
|
||||||
|
|
||||||
|
/// #DBEAFF — Container tertiaire
|
||||||
|
static const Color tertiaryContainer = Color(0xFFDBEAFF);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// SURFACES — Mode Jour
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #EFF6FF — Fond principal (légèrement teinté bleu)
|
||||||
|
static const Color background = Color(0xFFEFF6FF);
|
||||||
|
|
||||||
|
/// #F8FAFC — Fond subtil (zones secondaires, headers de liste)
|
||||||
|
static const Color backgroundSubtle = Color(0xFFF8FAFC);
|
||||||
|
|
||||||
|
/// #FFFFFF — Surface pure (cards, dialogs)
|
||||||
|
static const Color surface = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// #F1F5F9 — Surface variante (sections internes)
|
||||||
|
static const Color surfaceVariant = Color(0xFFF1F5F9);
|
||||||
|
|
||||||
|
/// #F5F8FF — Surface container (légèrement teintée)
|
||||||
|
static const Color surfaceContainer = Color(0xFFF5F8FF);
|
||||||
|
|
||||||
|
/// #EAF0FF — Surface container élevée
|
||||||
|
static const Color surfaceContainerHigh = Color(0xFFEAF0FF);
|
||||||
|
|
||||||
|
/// #E2E8F0 — Bordures subtiles
|
||||||
|
static const Color border = Color(0xFFE2E8F0);
|
||||||
|
|
||||||
|
/// #CBD5E1 — Bordures plus marquées (séparateurs, inputs)
|
||||||
|
static const Color borderStrong = Color(0xFFCBD5E1);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// SURFACES — Mode Nuit
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #0A0D1A — Navy profond OLED [20:1 ✅]
|
||||||
|
static const Color backgroundDark = Color(0xFF0A0D1A);
|
||||||
|
|
||||||
|
/// #121824 — Fond subtil dark
|
||||||
|
static const Color backgroundSubtleDark = Color(0xFF121824);
|
||||||
|
|
||||||
|
/// #161B26 — Surface principale dark
|
||||||
|
static const Color surfaceDark = Color(0xFF161B26);
|
||||||
|
|
||||||
|
/// #1E2535 — Surface variante dark
|
||||||
|
static const Color surfaceVariantDark = Color(0xFF1E2535);
|
||||||
|
|
||||||
|
/// #1E2438 — Surface container dark (cards)
|
||||||
|
static const Color surfaceContainerDark = Color(0xFF1E2438);
|
||||||
|
|
||||||
|
/// #2D3554 — Bordures dark
|
||||||
|
static const Color borderDark = Color(0xFF2D3554);
|
||||||
|
|
||||||
|
/// #3D4B6E — Bordures plus marquées dark
|
||||||
|
static const Color borderStrongDark = Color(0xFF3D4B6E);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// TEXTE — Mode Jour
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #0F172A — Texte principal [19.1:1 ✅ WCAG AAA]
|
||||||
|
static const Color textPrimary = Color(0xFF0F172A);
|
||||||
|
|
||||||
|
/// #1E293B — Titres (hiérarchie entre primary et body)
|
||||||
|
static const Color textTitle = Color(0xFF1E293B);
|
||||||
|
|
||||||
|
/// #475569 — Texte secondaire [7.2:1 ✅ WCAG AA]
|
||||||
|
static const Color textSecondary = Color(0xFF475569);
|
||||||
|
|
||||||
|
/// #64748B — Texte tertiaire / icônes secondaires [4.8:1 ✅]
|
||||||
|
static const Color textTertiary = Color(0xFF64748B);
|
||||||
|
|
||||||
|
/// #94A3B8 — Texte désactivé / captions [3.3:1, hints uniquement]
|
||||||
|
static const Color textDisabled = Color(0xFF94A3B8);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// TEXTE — Mode Nuit
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #F1F5FF — Texte principal dark
|
||||||
|
static const Color textPrimaryDark = Color(0xFFF1F5FF);
|
||||||
|
|
||||||
|
/// #FFFFFF — Titres dark (lumineux pour la hiérarchie)
|
||||||
|
static const Color textTitleDark = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// #94A3B8 — Texte secondaire dark
|
||||||
|
static const Color textSecondaryDark = Color(0xFF94A3B8);
|
||||||
|
|
||||||
|
/// #64748B — Texte désactivé dark
|
||||||
|
static const Color textDisabledDark = Color(0xFF64748B);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ON-COLORS — Contenu SUR une couleur
|
||||||
|
// NE PAS hardcoder Colors.white — utiliser ces tokens.
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Blanc pur — texte/icônes sur [primary], gradient, fond coloré
|
||||||
|
static const Color onPrimary = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Blanc pur — texte/icônes sur [accent]
|
||||||
|
static const Color onAccent = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Blanc pur — texte/icônes sur [error]
|
||||||
|
static const Color onError = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Blanc pur — texte/icônes sur [success]
|
||||||
|
static const Color onSuccess = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Blanc pur — texte/icônes sur [warning] foncé
|
||||||
|
static const Color onWarning = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Blanc pur — texte/icônes sur gradient logo / module headers
|
||||||
|
static const Color onGradient = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// SÉMANTIQUE — Succès, Erreur, Avertissement, Info
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #15803D — Succès texte [6.6:1 ✅ WCAG AA]
|
||||||
|
static const Color success = Color(0xFF15803D);
|
||||||
|
|
||||||
|
/// #166534 — Succès foncé
|
||||||
|
static const Color successDark = Color(0xFF166534);
|
||||||
|
|
||||||
|
/// #22C55E — Succès UI/icônes (≥3:1)
|
||||||
|
static const Color successUI = Color(0xFF22C55E);
|
||||||
|
|
||||||
|
/// #DCFCE7 — Fond succès light (badges, chips)
|
||||||
|
static const Color successContainer = Color(0xFFDCFCE7);
|
||||||
|
|
||||||
|
/// #052E16 — Contenu sur container succès
|
||||||
|
static const Color onSuccessContainer = Color(0xFF052E16);
|
||||||
|
|
||||||
|
/// #DC2626 — Erreur [4.5:1 ✅ WCAG AA]
|
||||||
static const Color error = Color(0xFFDC2626);
|
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 ---
|
/// #B91C1C — Erreur foncée
|
||||||
|
static const Color errorDark = Color(0xFFB91C1C);
|
||||||
|
|
||||||
|
/// #FCA5A5 — Erreur claire (icônes sur fond blanc)
|
||||||
|
static const Color errorLight = Color(0xFFFCA5A5);
|
||||||
|
|
||||||
|
/// #FEE2E2 — Fond erreur light
|
||||||
|
static const Color errorContainer = Color(0xFFFEE2E2);
|
||||||
|
|
||||||
|
/// #410002 — Contenu sur container erreur
|
||||||
|
static const Color onErrorContainer = Color(0xFF410002);
|
||||||
|
|
||||||
|
/// #B45309 — Warning texte [5.8:1 ✅ WCAG AA]
|
||||||
|
static const Color warning = Color(0xFFB45309);
|
||||||
|
|
||||||
|
/// #92400E — Warning foncé
|
||||||
|
static const Color warningDark = Color(0xFF92400E);
|
||||||
|
|
||||||
|
/// #F59E0B — Warning UI/icônes (décoratif)
|
||||||
|
static const Color warningUI = Color(0xFFF59E0B);
|
||||||
|
|
||||||
|
/// #FEF3C7 — Fond warning light
|
||||||
|
static const Color warningContainer = Color(0xFFFEF3C7);
|
||||||
|
|
||||||
|
/// #2D1B00 — Contenu sur container warning
|
||||||
|
static const Color onWarningContainer = Color(0xFF2D1B00);
|
||||||
|
|
||||||
|
/// #2563EB — Info (identique au primaire)
|
||||||
|
static const Color info = Color(0xFF2563EB);
|
||||||
|
|
||||||
|
/// #1D4ED8 — Info foncée
|
||||||
|
static const Color infoDark = Color(0xFF1D4ED8);
|
||||||
|
|
||||||
|
/// #60A5FA — Info claire
|
||||||
|
static const Color infoLight = Color(0xFF60A5FA);
|
||||||
|
|
||||||
|
/// #DBEAFE — Fond info light
|
||||||
|
static const Color infoContainer = Color(0xFFDBEAFE);
|
||||||
|
|
||||||
|
/// #1E3A8A — Contenu sur container info
|
||||||
|
static const Color onInfoContainer = Color(0xFF1E3A8A);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// NAVIGATION
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color navigationBackground = Color(0xFFFFFFFF);
|
||||||
|
static const Color navigationSelected = Color(0xFF2563EB);
|
||||||
|
static const Color navigationUnselected = Color(0xFF64748B);
|
||||||
|
static const Color navigationIndicator = Color(0xFFEFF6FF);
|
||||||
|
|
||||||
|
static const Color navigationBackgroundDark = Color(0xFF161B26);
|
||||||
|
static const Color navigationSelectedDark = Color(0xFF60A5FA);
|
||||||
|
static const Color navigationUnselectedDark = Color(0xFF64748B);
|
||||||
|
static const Color navigationIndicatorDark = Color(0xFF1A2350);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// GLASS / EFFETS
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Fond verre translucide (glassmorphism)
|
||||||
|
static const Color glassBackground = Color(0x80FFFFFF);
|
||||||
|
|
||||||
|
/// Bordure verre translucide
|
||||||
|
static const Color glassBorder = Color(0x33FFFFFF);
|
||||||
|
|
||||||
|
/// Overlay verre très subtil
|
||||||
|
static const Color glassOverlay = Color(0x0DFFFFFF);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// OMBRES — Color tokens
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Ombre très subtile (élévation 1-2)
|
||||||
|
static const Color shadow = Color(0x0A000000);
|
||||||
|
|
||||||
|
/// Ombre légère (élévation 3-4)
|
||||||
|
static const Color shadowMedium = Color(0x1A000000);
|
||||||
|
|
||||||
|
/// Ombre marquée (élévation 6+)
|
||||||
|
static const Color shadowStrong = Color(0x33000000);
|
||||||
|
|
||||||
|
/// Ombre intense (modales, drawers)
|
||||||
|
static const Color shadowHigh = Color(0x4D000000);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// OMBRES — BoxShadow getters (prêts à l'emploi)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Ombre douce — cards standards
|
||||||
|
static List<BoxShadow> get softShadow => [
|
||||||
|
BoxShadow(
|
||||||
|
color: const Color(0xFF0F172A).withOpacity(0.08),
|
||||||
|
blurRadius: 24,
|
||||||
|
offset: const Offset(0, 8),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Ombre moyenne — éléments surélevés, sheets
|
||||||
|
static List<BoxShadow> get mediumShadow => [
|
||||||
|
BoxShadow(
|
||||||
|
color: const Color(0xFF0F172A).withOpacity(0.12),
|
||||||
|
blurRadius: 32,
|
||||||
|
offset: const Offset(0, 12),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Halo bleu primaire — boutons CTA, éléments actifs
|
||||||
|
static List<BoxShadow> get blueGlowShadow => [
|
||||||
|
BoxShadow(
|
||||||
|
color: primary.withOpacity(0.25),
|
||||||
|
blurRadius: 20,
|
||||||
|
offset: const Offset(0, 8),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Halo violet accent — éléments premium, gradients
|
||||||
|
static List<BoxShadow> get purpleGlowShadow => [
|
||||||
|
BoxShadow(
|
||||||
|
color: accent.withOpacity(0.2),
|
||||||
|
blurRadius: 20,
|
||||||
|
offset: const Offset(0, 8),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// PALETTE BRAND — Couleurs expressives (dashboards, UI premium)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// #F4A261 — Ambre chaleureux (dashboards consultant, UI premium)
|
||||||
|
static const Color amber = Color(0xFFF4A261);
|
||||||
|
|
||||||
|
/// #F8C590 — Ambre clair
|
||||||
|
static const Color amberLight = Color(0xFFF8C590);
|
||||||
|
|
||||||
|
/// #E9DCC9 — Sable doux (fonds neutres chauds)
|
||||||
|
static const Color sand = Color(0xFFE9DCC9);
|
||||||
|
|
||||||
|
/// #F5F0E8 — Sable clair
|
||||||
|
static const Color sandLight = Color(0xFFF5F0E8);
|
||||||
|
|
||||||
|
/// #E07A5F — Terracotta (accents événements, UI chaleureux)
|
||||||
|
static const Color terracotta = Color(0xFFE07A5F);
|
||||||
|
|
||||||
|
/// #F2AC99 — Terracotta clair
|
||||||
|
static const Color terracottaLight = Color(0xFFF2AC99);
|
||||||
|
|
||||||
|
/// #FFF3F0 — Terracotta pale (containers, badges)
|
||||||
|
static const Color terracottaPale = Color(0xFFFFF3F0);
|
||||||
|
|
||||||
|
/// #1E1A44 — Indigo profond (fonds sombres premium)
|
||||||
|
static const Color indigo = Color(0xFF1E1A44);
|
||||||
|
|
||||||
|
/// #3A3A6B — Indigo moyen
|
||||||
|
static const Color indigoLight = Color(0xFF3A3A6B);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRADIENTS — List<Color> (pour ShaderMask, charts, etc.)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Gradient logo signature : #297FFF → #7616E8 (décoratif — non pour texte)
|
||||||
|
static const List<Color> logoGradientColors = [
|
||||||
|
Color(0xFF297FFF),
|
||||||
|
Color(0xFF7616E8),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Gradient primaire UI (accessible)
|
||||||
|
static const List<Color> primaryGradientColors = [
|
||||||
|
Color(0xFF1D4ED8),
|
||||||
|
Color(0xFF2563EB),
|
||||||
|
Color(0xFF5297FF),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Gradient login (fond immersif)
|
||||||
|
static const List<Color> loginGradientColors = [
|
||||||
|
Color(0xFF1D4ED8),
|
||||||
|
Color(0xFF2563EB),
|
||||||
|
Color(0xFF7616E8),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Gradient dark mode
|
||||||
|
static const List<Color> darkGradientColors = [
|
||||||
|
Color(0xFF0A0D1A),
|
||||||
|
Color(0xFF161B26),
|
||||||
|
Color(0xFF1E2438),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Gradient succès
|
||||||
|
static const List<Color> successGradientColors = [
|
||||||
|
Color(0xFF166534),
|
||||||
|
Color(0xFF15803D),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Gradient warm (terracotta → ambre)
|
||||||
|
static const List<Color> warmGradientColors = [
|
||||||
|
Color(0xFFE07A5F),
|
||||||
|
Color(0xFFF4A261),
|
||||||
|
];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRADIENTS — LinearGradient (prêts à l'emploi)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Gradient logo signature (décoratif — NE PAS utiliser pour du texte)
|
||||||
|
static const LinearGradient logoGradient = LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [Color(0xFF297FFF), Color(0xFF7616E8)],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Gradient primaire UI (version accessible)
|
||||||
|
static const LinearGradient primaryGradient = LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [Color(0xFF1D4ED8), Color(0xFF2563EB)],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Gradient login (fond immersif plein écran)
|
||||||
|
static const LinearGradient loginGradient = LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [Color(0xFF1D4ED8), Color(0xFF2563EB), Color(0xFF7616E8)],
|
||||||
|
stops: [0.0, 0.55, 1.0],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Gradient subtil (backgrounds de sections, headers légers)
|
||||||
|
static const LinearGradient subtleGradient = LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [Color(0xFFF5F8FF), Color(0xFFEFF6FF)],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Gradient warm terracotta → ambre (UI premium, dashboards)
|
||||||
|
static const LinearGradient warmGradient = LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [Color(0xFFE07A5F), Color(0xFFF4A261)],
|
||||||
|
);
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// UTILITAIRES
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Applique une opacité à une couleur
|
||||||
|
static Color withOpacity(Color color, double opacity) =>
|
||||||
|
color.withOpacity(opacity);
|
||||||
|
|
||||||
|
/// Éclaircit une couleur d'un facteur donné (HSL)
|
||||||
|
static Color lighten(Color color, [double amount = 0.1]) {
|
||||||
|
final hsl = HSLColor.fromColor(color);
|
||||||
|
return hsl
|
||||||
|
.withLightness((hsl.lightness + amount).clamp(0.0, 1.0))
|
||||||
|
.toColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assombrit une couleur d'un facteur donné (HSL)
|
||||||
|
static Color darken(Color color, [double amount = 0.1]) {
|
||||||
|
final hsl = HSLColor.fromColor(color);
|
||||||
|
return hsl
|
||||||
|
.withLightness((hsl.lightness - amount).clamp(0.0, 1.0))
|
||||||
|
.toColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ALIAS DE COMPATIBILITÉ (@deprecated — à migrer progressivement)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// @deprecated → [primary]
|
||||||
|
static const Color primaryGreen = primary;
|
||||||
|
|
||||||
|
/// @deprecated → [primaryDark]
|
||||||
|
static const Color brandGreen = primaryDark;
|
||||||
|
|
||||||
|
/// @deprecated → [primaryLight]
|
||||||
|
static const Color brandGreenLight = primaryLight;
|
||||||
|
|
||||||
|
/// @deprecated → [primaryContainer]
|
||||||
|
static const Color brandMint = primaryContainer;
|
||||||
|
|
||||||
|
/// @deprecated → [background]
|
||||||
|
static const Color lightBackground = background;
|
||||||
|
|
||||||
|
/// @deprecated → [surface]
|
||||||
|
static const Color lightSurface = surface;
|
||||||
|
|
||||||
|
/// @deprecated → [border]
|
||||||
|
static const Color lightBorder = border;
|
||||||
|
|
||||||
|
/// @deprecated → [backgroundDark]
|
||||||
|
static const Color darkBackground = backgroundDark;
|
||||||
|
|
||||||
|
/// @deprecated → [surfaceDark]
|
||||||
|
static const Color darkSurface = surfaceDark;
|
||||||
|
|
||||||
|
/// @deprecated → [borderDark]
|
||||||
|
static const Color darkBorder = borderDark;
|
||||||
|
|
||||||
|
/// @deprecated → [textPrimary]
|
||||||
|
static const Color textPrimaryLight = textPrimary;
|
||||||
|
|
||||||
|
/// @deprecated → [textSecondary]
|
||||||
|
static const Color textSecondaryLight = textSecondary;
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// DIVERS
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
static const Color transparent = Colors.transparent;
|
static const Color transparent = Colors.transparent;
|
||||||
static const Color surface = lightSurface;
|
|
||||||
static const Color background = lightBackground;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,190 +1,126 @@
|
|||||||
/// Design Tokens - Couleurs UnionFlow
|
/// Facade de compatibilité — redirige vers [AppColors]
|
||||||
///
|
///
|
||||||
/// Palette Vert Zen / Santé — Professionnelle et apaisante
|
/// Cette classe existe uniquement pour ne pas casser le code existant.
|
||||||
/// MODE JOUR : Vert Forêt (#2E7D32) — Forest Green
|
/// Pour tout nouveau code, utiliser [AppColors] directement.
|
||||||
/// MODE NUIT : Vert Ardoise (#1A2E1A) — Slate Green
|
///
|
||||||
|
/// @deprecated Utiliser [AppColors]
|
||||||
library color_tokens;
|
library color_tokens;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'app_colors.dart';
|
||||||
|
|
||||||
/// Tokens de couleurs UnionFlow - Design System Unifié
|
|
||||||
class ColorTokens {
|
class ColorTokens {
|
||||||
ColorTokens._();
|
ColorTokens._();
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ── Primaire ────────────────────────────────────────────────────────────
|
||||||
// COULEURS PRIMAIRES - MODE JOUR (Vert Forêt)
|
static const Color primary = AppColors.primary;
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color primaryLight = AppColors.primaryLight;
|
||||||
|
static const Color primaryDark = AppColors.primaryDark;
|
||||||
|
static const Color primaryContainer = AppColors.primaryContainer;
|
||||||
|
static const Color onPrimary = AppColors.onPrimary;
|
||||||
|
static const Color onPrimaryContainer = AppColors.onPrimaryContainer;
|
||||||
|
|
||||||
/// Couleur primaire principale - Vert Forêt
|
// ── Secondaire (= Accent) ────────────────────────────────────────────────
|
||||||
static const Color primary = Color(0xFF2E7D32);
|
static const Color secondary = AppColors.accent;
|
||||||
static const Color primaryLight = Color(0xFF4CAF50);
|
static const Color secondaryLight = AppColors.accentLight;
|
||||||
static const Color primaryDark = Color(0xFF1B5E20);
|
static const Color secondaryDark = AppColors.accentDark;
|
||||||
static const Color primaryContainer = Color(0xFFE8F5E9);
|
static const Color secondaryContainer = AppColors.accentContainer;
|
||||||
static const Color onPrimary = Color(0xFFFFFFFF);
|
static const Color onSecondary = AppColors.onAccent;
|
||||||
static const Color onPrimaryContainer = Color(0xFF003908);
|
static const Color onSecondaryContainer = AppColors.onAccentContainer;
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ── Tertiaire ────────────────────────────────────────────────────────────
|
||||||
// COULEURS PRIMAIRES - MODE NUIT (Vert Ardoise)
|
static const Color tertiary = AppColors.tertiary;
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color tertiaryLight = AppColors.tertiaryLight;
|
||||||
|
static const Color tertiaryDark = AppColors.primaryDark;
|
||||||
|
static const Color tertiaryContainer = AppColors.tertiaryContainer;
|
||||||
|
static const Color onTertiary = AppColors.onPrimary;
|
||||||
|
static const Color onTertiaryContainer = AppColors.onPrimaryContainer;
|
||||||
|
|
||||||
static const Color primaryDarkMode = Color(0xFF1A2E1A);
|
// ── Surfaces Mode Jour ───────────────────────────────────────────────────
|
||||||
static const Color primaryLightDarkMode = Color(0xFF2E4D2E);
|
static const Color background = AppColors.background;
|
||||||
static const Color primaryDarkDarkMode = Color(0xFF0F1A0F);
|
static const Color surface = AppColors.surface;
|
||||||
static const Color primaryContainerDarkMode = Color(0xFF1E3A1E);
|
static const Color surfaceVariant = AppColors.surfaceContainer;
|
||||||
static const Color onPrimaryDarkMode = Color(0xFFE0F2E0);
|
static const Color surfaceContainer = AppColors.surface;
|
||||||
|
static const Color surfaceContainerHigh = AppColors.surfaceContainer;
|
||||||
|
static const Color surfaceContainerHighest = AppColors.surfaceContainerHigh;
|
||||||
|
static const Color onSurface = AppColors.textPrimary;
|
||||||
|
static const Color onSurfaceVariant = AppColors.textSecondary;
|
||||||
|
static const Color textSecondary = AppColors.textTertiary;
|
||||||
|
static const Color outline = AppColors.border;
|
||||||
|
static const Color outlineVariant = AppColors.borderStrong;
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ── Surfaces Mode Nuit ───────────────────────────────────────────────────
|
||||||
// COULEURS SECONDAIRES - Vert Menthe / Sauge
|
static const Color backgroundDark = AppColors.backgroundDark;
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color surfaceDark = AppColors.surfaceDark;
|
||||||
|
static const Color surfaceVariantDark = AppColors.surfaceVariantDark;
|
||||||
|
static const Color surfaceContainerDark = AppColors.surfaceContainerDark;
|
||||||
|
static const Color onSurfaceDark = AppColors.textPrimaryDark;
|
||||||
|
static const Color onSurfaceVariantDark = AppColors.textSecondaryDark;
|
||||||
|
static const Color outlineDark = AppColors.borderDark;
|
||||||
|
|
||||||
static const Color secondary = Color(0xFF66BB6A);
|
// ── Sémantique ───────────────────────────────────────────────────────────
|
||||||
static const Color secondaryLight = Color(0xFFA5D6A7);
|
static const Color success = AppColors.success;
|
||||||
static const Color secondaryDark = Color(0xFF388E3C);
|
static const Color successLight = AppColors.successUI;
|
||||||
static const Color secondaryContainer = Color(0xFFC8E6C9);
|
static const Color successDark = AppColors.successDark;
|
||||||
static const Color onSecondary = Color(0xFFFFFFFF);
|
static const Color successContainer = AppColors.successContainer;
|
||||||
static const Color onSecondaryContainer = Color(0xFF002106);
|
static const Color onSuccess = AppColors.onSuccess;
|
||||||
|
static const Color onSuccessContainer = AppColors.onSuccessContainer;
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color error = AppColors.error;
|
||||||
// COULEURS TERTIAIRES - Vert Lime / Accent
|
static const Color errorLight = AppColors.errorLight;
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color errorDark = AppColors.errorDark;
|
||||||
|
static const Color errorContainer = AppColors.errorContainer;
|
||||||
|
static const Color onError = AppColors.onError;
|
||||||
|
static const Color onErrorContainer = AppColors.onErrorContainer;
|
||||||
|
|
||||||
static const Color tertiary = Color(0xFF8BC34A);
|
static const Color warning = AppColors.warning;
|
||||||
static const Color tertiaryLight = Color(0xFFAED581);
|
static const Color warningLight = AppColors.warningUI;
|
||||||
static const Color tertiaryDark = Color(0xFF558B2F);
|
static const Color warningDark = AppColors.warningDark;
|
||||||
static const Color tertiaryContainer = Color(0xFFDCEDC8);
|
static const Color warningContainer = AppColors.warningContainer;
|
||||||
static const Color onTertiary = Color(0xFFFFFFFF);
|
static const Color onWarning = AppColors.onWarning;
|
||||||
static const Color onTertiaryContainer = Color(0xFF1B3A00);
|
static const Color onWarningContainer = AppColors.onWarningContainer;
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color info = AppColors.info;
|
||||||
// COULEURS NEUTRES - MODE JOUR
|
static const Color infoLight = AppColors.infoLight;
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color infoDark = AppColors.infoDark;
|
||||||
|
static const Color infoContainer = AppColors.infoContainer;
|
||||||
|
static const Color onInfo = AppColors.onPrimary;
|
||||||
|
static const Color onInfoContainer = AppColors.onInfoContainer;
|
||||||
|
|
||||||
static const Color surface = Color(0xFFFFFFFF);
|
// ── Navigation ───────────────────────────────────────────────────────────
|
||||||
static const Color surfaceVariant = Color(0xFFF1F8E9);
|
static const Color navigationBackground = AppColors.navigationBackground;
|
||||||
static const Color surfaceContainer = Color(0xFFFFFFFF);
|
static const Color navigationSelected = AppColors.navigationSelected;
|
||||||
static const Color surfaceContainerHigh = Color(0xFFF1F8E9);
|
static const Color navigationUnselected = AppColors.navigationUnselected;
|
||||||
static const Color surfaceContainerHighest = Color(0xFFDCEDC8);
|
static const Color navigationIndicator = AppColors.navigationIndicator;
|
||||||
static const Color background = Color(0xFFF1F8E9);
|
static const Color navigationBackgroundDark = AppColors.navigationBackgroundDark;
|
||||||
|
static const Color navigationSelectedDark = AppColors.navigationSelectedDark;
|
||||||
|
static const Color navigationUnselectedDark = AppColors.navigationUnselectedDark;
|
||||||
|
static const Color navigationIndicatorDark = AppColors.navigationIndicatorDark;
|
||||||
|
|
||||||
static const Color onSurface = Color(0xFF1C2B1C);
|
// ── Ombres & Effets ──────────────────────────────────────────────────────
|
||||||
static const Color onSurfaceVariant = Color(0xFF4E6B4E);
|
static const Color shadow = AppColors.shadow;
|
||||||
static const Color textSecondary = Color(0xFF4E6B4E);
|
static const Color shadowMedium = AppColors.shadowMedium;
|
||||||
static const Color outline = Color(0xFFC8E6C9);
|
static const Color shadowHigh = AppColors.shadowHigh;
|
||||||
static const Color outlineVariant = Color(0xFFDCEDC8);
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color glassBackground = AppColors.glassBackground;
|
||||||
// COULEURS NEUTRES - MODE NUIT
|
static const Color glassBorder = AppColors.glassBorder;
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static const Color glassOverlay = AppColors.glassOverlay;
|
||||||
|
|
||||||
static const Color surfaceDarkMode = Color(0xFF1A2E1A);
|
// ── Gradients ────────────────────────────────────────────────────────────
|
||||||
static const Color surfaceVariantDarkMode = Color(0xFF243824);
|
static const List<Color> logoGradient = AppColors.logoGradientColors;
|
||||||
static const Color backgroundDarkMode = Color(0xFF0F1A0F);
|
static const List<Color> primaryGradient = AppColors.primaryGradientColors;
|
||||||
|
static const List<Color> loginGradient = AppColors.loginGradientColors;
|
||||||
|
static const List<Color> primaryGradientDark = AppColors.darkGradientColors;
|
||||||
|
static const List<Color> secondaryGradient = [AppColors.tertiary, AppColors.accent];
|
||||||
|
static const List<Color> successGradient = AppColors.successGradientColors;
|
||||||
|
|
||||||
static const Color onSurfaceDarkMode = Color(0xFFE0F2E0);
|
// ── Utilitaires ──────────────────────────────────────────────────────────
|
||||||
static const Color onSurfaceVariantDarkMode = Color(0xFF90B890);
|
static Color withOpacity(Color color, double opacity) =>
|
||||||
static const Color outlineDarkMode = Color(0xFF3A5E3A);
|
AppColors.withOpacity(color, opacity);
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
static Color lighten(Color color, [double amount = 0.1]) =>
|
||||||
// COULEURS SÉMANTIQUES
|
AppColors.lighten(color, amount);
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
static const Color success = Color(0xFF2E7D32);
|
static Color darken(Color color, [double amount = 0.1]) =>
|
||||||
static const Color successLight = Color(0xFF4CAF50);
|
AppColors.darken(color, amount);
|
||||||
static const Color successDark = Color(0xFF1B5E20);
|
|
||||||
static const Color successContainer = Color(0xFFE8F5E9);
|
|
||||||
static const Color onSuccess = Color(0xFFFFFFFF);
|
|
||||||
static const Color onSuccessContainer = Color(0xFF003908);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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 DE NAVIGATION
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
static const Color navigationBackground = Color(0xFFFFFFFF);
|
|
||||||
static const Color navigationSelected = Color(0xFF2E7D32);
|
|
||||||
static const Color navigationUnselected = Color(0xFF4E6B4E);
|
|
||||||
static const Color navigationIndicator = Color(0xFFE8F5E9);
|
|
||||||
|
|
||||||
static const Color navigationBackgroundDarkMode = Color(0xFF1A2E1A);
|
|
||||||
static const Color navigationSelectedDarkMode = Color(0xFFA5D6A7);
|
|
||||||
static const Color navigationUnselectedDarkMode = Color(0xFF90B890);
|
|
||||||
static const Color navigationIndicatorDarkMode = Color(0xFF2E4D2E);
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
// OMBRES ET EFFETS
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
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
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
static const List<Color> primaryGradient = [
|
|
||||||
Color(0xFF1B5E20),
|
|
||||||
Color(0xFF2E7D32),
|
|
||||||
Color(0xFF388E3C),
|
|
||||||
];
|
|
||||||
|
|
||||||
static const List<Color> primaryGradientDarkMode = [
|
|
||||||
Color(0xFF0F1A0F),
|
|
||||||
Color(0xFF1A2E1A),
|
|
||||||
Color(0xFF243824),
|
|
||||||
];
|
|
||||||
|
|
||||||
static const List<Color> secondaryGradient = [
|
|
||||||
Color(0xFF388E3C),
|
|
||||||
Color(0xFF66BB6A),
|
|
||||||
];
|
|
||||||
|
|
||||||
static const List<Color> successGradient = [
|
|
||||||
Color(0xFF2E7D32),
|
|
||||||
Color(0xFF4CAF50),
|
|
||||||
];
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
// MÉTHODES UTILITAIRES
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
static Color withOpacity(Color color, double opacity) {
|
|
||||||
return color.withOpacity(opacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Color darken(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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
218
lib/shared/design_system/tokens/module_colors.dart
Normal file
218
lib/shared/design_system/tokens/module_colors.dart
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
/// Système d'identité chromatique par module — UnionFlow
|
||||||
|
///
|
||||||
|
/// Chaque module/section de l'application possède :
|
||||||
|
/// • Une couleur primaire distinctive (WCAG AA validée)
|
||||||
|
/// • Un gradient directionnel (foncé → clair)
|
||||||
|
/// • Une couleur de container claire (fonds, badges)
|
||||||
|
/// • La couleur sur laquelle écrire en blanc
|
||||||
|
///
|
||||||
|
/// Principe : quand l'utilisateur entre dans un écran d'un module,
|
||||||
|
/// le top bar reflète intelligemment la couleur de ce module.
|
||||||
|
library module_colors;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'app_colors.dart';
|
||||||
|
|
||||||
|
class ModuleColors {
|
||||||
|
ModuleColors._();
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ORGANISATIONS — Bleu primaire UnionFlow [5.7:1 ✅]
|
||||||
|
// Bouton dashboard : Icons.business_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color organisations = Color(0xFF2563EB);
|
||||||
|
static const Color organisationsDark = Color(0xFF1D4ED8);
|
||||||
|
static const Color organisationsLight = Color(0xFFEFF6FF);
|
||||||
|
static const Color organisationsOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> organisationsGradient = [Color(0xFF1D4ED8), Color(0xFF2563EB)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// MEMBRES — Violet accent [4.8:1 ✅ pour texte]
|
||||||
|
// Bouton dashboard : Icons.people_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color membres = Color(0xFF7616E8);
|
||||||
|
static const Color membresDark = Color(0xFF5B0FBA);
|
||||||
|
static const Color membresLight = Color(0xFFF3ECFF);
|
||||||
|
static const Color membresOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> membresGradient = [Color(0xFF5B0FBA), Color(0xFF7616E8)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// COTISATIONS / FINANCE — Ambre doré [5.8:1 ✅]
|
||||||
|
// Bouton dashboard : Icons.payments_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color cotisations = Color(0xFFB45309);
|
||||||
|
static const Color cotisationsDark = Color(0xFF92400E);
|
||||||
|
static const Color cotisationsLight = Color(0xFFFEF3C7);
|
||||||
|
static const Color cotisationsOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> cotisationsGradient = [Color(0xFF92400E), Color(0xFFB45309)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ÉVÉNEMENTS — Terracotta vibrant
|
||||||
|
// Bouton dashboard : Icons.event_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color evenements = Color(0xFFD4551C);
|
||||||
|
static const Color evenementsDark = Color(0xFFB84315);
|
||||||
|
static const Color evenementsLight = Color(0xFFFFF3F0);
|
||||||
|
static const Color evenementsOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> evenementsGradient = [Color(0xFFB84315), Color(0xFFD4551C)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// SOLIDARITÉ / AIDE — Rouge compassion [4.5:1 ✅]
|
||||||
|
// Bouton dashboard : Icons.favorite_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color solidarite = Color(0xFFDC2626);
|
||||||
|
static const Color solidariteDark = Color(0xFFB91C1C);
|
||||||
|
static const Color solidariteLight = Color(0xFFFEF2F2);
|
||||||
|
static const Color solidariteOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> solidariteGradient = [Color(0xFFB91C1C), Color(0xFFDC2626)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ÉPARGNE — Vert croissance [6.6:1 ✅]
|
||||||
|
// Bouton dashboard : Icons.savings_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color epargne = Color(0xFF15803D);
|
||||||
|
static const Color epargneDark = Color(0xFF166534);
|
||||||
|
static const Color epargneLight = Color(0xFFF0FDF4);
|
||||||
|
static const Color epargneOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> epargneGradient = [Color(0xFF166534), Color(0xFF15803D)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ADHÉSIONS — Émeraude [4.8:1 ✅]
|
||||||
|
// Bouton dashboard : Icons.how_to_reg_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color adhesions = Color(0xFF047857);
|
||||||
|
static const Color adhesionsDark = Color(0xFF065F46);
|
||||||
|
static const Color adhesionsLight = Color(0xFFECFDF5);
|
||||||
|
static const Color adhesionsOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> adhesionsGradient = [Color(0xFF065F46), Color(0xFF047857)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// COMMUNICATION — Cyan dynamique
|
||||||
|
// Bouton dashboard : Icons.chat_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color communication = Color(0xFF0891B2);
|
||||||
|
static const Color communicationDark = Color(0xFF0E7490);
|
||||||
|
static const Color communicationLight = Color(0xFFECFEFF);
|
||||||
|
static const Color communicationOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> communicationGradient = [Color(0xFF0E7490), Color(0xFF0891B2)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// RAPPORTS / ANALYTICS — Indigo analytique
|
||||||
|
// Bouton dashboard : Icons.analytics_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color rapports = Color(0xFF4338CA);
|
||||||
|
static const Color rapportsDark = Color(0xFF3730A3);
|
||||||
|
static const Color rapportsLight = Color(0xFFEEF2FF);
|
||||||
|
static const Color rapportsOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> rapportsGradient = [Color(0xFF3730A3), Color(0xFF4338CA)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// PROFIL — Gradient signature logo (identité personnelle)
|
||||||
|
// Bouton dashboard : Icons.person_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color profil = Color(0xFF7616E8);
|
||||||
|
static const Color profilDark = Color(0xFF297FFF);
|
||||||
|
static const Color profilLight = Color(0xFFF3ECFF);
|
||||||
|
static const Color profilOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> profilGradient = [Color(0xFF297FFF), Color(0xFF7616E8)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// SYSTÈME / ADMIN — Navy autorité
|
||||||
|
// Bouton dashboard : Icons.settings_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color systeme = Color(0xFF1E293B);
|
||||||
|
static const Color systemeDark = Color(0xFF0F172A);
|
||||||
|
static const Color systemeLight = Color(0xFFF1F5F9);
|
||||||
|
static const Color systemeOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> systemeGradient = [Color(0xFF0F172A), Color(0xFF1E293B)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// BACKUP — Ambre attention
|
||||||
|
// Bouton dashboard : Icons.backup_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color backup = Color(0xFFD97706);
|
||||||
|
static const Color backupDark = Color(0xFFB45309);
|
||||||
|
static const Color backupLight = Color(0xFFFEF3C7);
|
||||||
|
static const Color backupOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> backupGradient = [Color(0xFFB45309), Color(0xFFD97706)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// LOGS / MONITORING — Slate technique
|
||||||
|
// Bouton dashboard : Icons.terminal_rounded
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color logs = Color(0xFF475569);
|
||||||
|
static const Color logsDark = Color(0xFF334155);
|
||||||
|
static const Color logsLight = Color(0xFFF8FAFC);
|
||||||
|
static const Color logsOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> logsGradient = [Color(0xFF334155), Color(0xFF475569)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// NOTIFICATIONS — Violet vif
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color notifications = Color(0xFF6366F1);
|
||||||
|
static const Color notificationsDark = Color(0xFF4F46E5);
|
||||||
|
static const Color notificationsLight = Color(0xFFEEF2FF);
|
||||||
|
static const Color notificationsOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> notificationsGradient = [Color(0xFF4F46E5), Color(0xFF6366F1)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// PARAMÈTRES — Slate neutre
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color parametres = Color(0xFF334155);
|
||||||
|
static const Color parametresDark = Color(0xFF1E293B);
|
||||||
|
static const Color parametresLight = Color(0xFFF1F5F9);
|
||||||
|
static const Color parametresOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> parametresGradient = [Color(0xFF1E293B), Color(0xFF334155)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// FINANCE WORKFLOW (Approbations, Budgets)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color financeWorkflow = Color(0xFF0F766E);
|
||||||
|
static const Color financeWorkflowDark = Color(0xFF134E4A);
|
||||||
|
static const Color financeWorkflowLight = Color(0xFFF0FDFA);
|
||||||
|
static const Color financeWorkflowOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> financeWorkflowGradient = [Color(0xFF134E4A), Color(0xFF0F766E)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// AIDE / SUPPORT
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
static const Color support = Color(0xFF2563EB);
|
||||||
|
static const Color supportDark = Color(0xFF1D4ED8);
|
||||||
|
static const Color supportLight = Color(0xFFEFF6FF);
|
||||||
|
static const Color supportOnColor = AppColors.onGradient;
|
||||||
|
static const List<Color> supportGradient = [Color(0xFF1D4ED8), Color(0xFF2563EB)];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// UTILITAIRES
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// Retourne le gradient LinearGradient depuis la liste de couleurs d'un module
|
||||||
|
static LinearGradient gradient(
|
||||||
|
List<Color> colors, {
|
||||||
|
AlignmentGeometry begin = Alignment.centerLeft,
|
||||||
|
AlignmentGeometry end = Alignment.centerRight,
|
||||||
|
}) {
|
||||||
|
return LinearGradient(colors: colors, begin: begin, end: end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retourne le gradient vertical (pour AppBar)
|
||||||
|
static LinearGradient verticalGradient(List<Color> colors) =>
|
||||||
|
gradient(colors, begin: Alignment.topLeft, end: Alignment.bottomRight);
|
||||||
|
}
|
||||||
@@ -1,185 +1,93 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'app_colors.dart';
|
||||||
|
|
||||||
/// UnionFlow Color System - Palette Signature
|
/// Facade de compatibilité — redirige vers [AppColors]
|
||||||
/// Inspirée des valeurs de solidarité, prospérité et modernité africaine
|
///
|
||||||
|
/// Cette classe existe uniquement pour ne pas casser le code existant.
|
||||||
|
/// Pour tout nouveau code, utiliser [AppColors] directement.
|
||||||
|
///
|
||||||
|
/// @deprecated Utiliser [AppColors]
|
||||||
class UnionFlowColors {
|
class UnionFlowColors {
|
||||||
UnionFlowColors._();
|
UnionFlowColors._();
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ── Primaire ────────────────────────────────────────────────────────────
|
||||||
// COULEURS PRIMAIRES (Identité UnionFlow)
|
static const Color unionBlue = AppColors.primary;
|
||||||
// ═══════════════════════════════════════════════════════════════
|
static const Color unionBlueLight = AppColors.primaryLight;
|
||||||
|
static const Color unionBlueDark = AppColors.primaryDark;
|
||||||
|
static const Color unionBluePale = AppColors.primaryContainer;
|
||||||
|
static const Color unionBlueLogo = AppColors.logoBlue;
|
||||||
|
|
||||||
/// Vert UnionFlow - Symbole de croissance et prospérité
|
// ── Accent ──────────────────────────────────────────────────────────────
|
||||||
static const Color unionGreen = Color(0xFF0F6B4F);
|
static const Color unionPurple = AppColors.accent;
|
||||||
static const Color unionGreenLight = Color(0xFF1F8A67);
|
static const Color unionPurpleLight = AppColors.accentLight;
|
||||||
static const Color unionGreenPale = Color(0xFFEEF5F2);
|
static const Color unionPurpleDark = AppColors.accentDark;
|
||||||
|
static const Color unionPurplePale = AppColors.accentContainer;
|
||||||
|
|
||||||
/// Or - Symbole de richesse et communauté
|
// ── Sémantique ──────────────────────────────────────────────────────────
|
||||||
static const Color gold = Color(0xFFD4A017);
|
static const Color success = AppColors.success;
|
||||||
static const Color goldLight = Color(0xFFE8C568);
|
static const Color successLight = AppColors.successUI;
|
||||||
static const Color goldPale = Color(0xFFFFF9E6);
|
static const Color successPale = AppColors.successContainer;
|
||||||
|
|
||||||
/// Indigo - Modernité et confiance
|
static const Color warning = AppColors.warning;
|
||||||
static const Color indigo = Color(0xFF1E2A44);
|
static const Color warningLight = AppColors.warningUI;
|
||||||
static const Color indigoLight = Color(0xFF3A4A6B);
|
static const Color warningPale = AppColors.warningContainer;
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
static const Color error = AppColors.error;
|
||||||
// COULEURS SECONDAIRES (Accents culturels)
|
static const Color errorLight = AppColors.errorLight;
|
||||||
// ═══════════════════════════════════════════════════════════════
|
static const Color errorPale = AppColors.errorContainer;
|
||||||
|
|
||||||
/// Terracotta - Chaleur et tradition
|
static const Color info = AppColors.info;
|
||||||
static const Color terracotta = Color(0xFFE07A5F);
|
static const Color infoLight = AppColors.infoLight;
|
||||||
static const Color terracottaLight = Color(0xFFF2AC99);
|
static const Color infoPale = AppColors.infoContainer;
|
||||||
static const Color terracottaPale = Color(0xFFFFF3F0);
|
|
||||||
|
|
||||||
/// Ambre - Énergie et optimisme
|
// ── Surfaces ────────────────────────────────────────────────────────────
|
||||||
static const Color amber = Color(0xFFF4A261);
|
static const Color background = AppColors.background;
|
||||||
static const Color amberLight = Color(0xFFF8C590);
|
static const Color surface = AppColors.surface;
|
||||||
|
static const Color surfaceVariant = AppColors.surfaceContainer;
|
||||||
|
static const Color surfaceContainer = AppColors.surfaceContainerHigh;
|
||||||
|
static const Color surfaceGlass = AppColors.backgroundSubtle;
|
||||||
|
|
||||||
/// Sable - Neutralité élégante
|
// ── Texte & Bordures ─────────────────────────────────────────────────────
|
||||||
static const Color sand = Color(0xFFE9DCC9);
|
static const Color textPrimary = AppColors.textPrimary;
|
||||||
static const Color sandLight = Color(0xFFF5F0E8);
|
static const Color textSecondary = AppColors.textSecondary;
|
||||||
|
static const Color textTertiary = AppColors.textDisabled;
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
static const Color border = AppColors.border;
|
||||||
// COULEURS SÉMANTIQUES
|
static const Color borderMedium = AppColors.borderStrong;
|
||||||
// ═══════════════════════════════════════════════════════════════
|
static const Color borderStrong = AppColors.textDisabled;
|
||||||
|
|
||||||
/// Succès - Validation, confirmation
|
// ── Palette brand ────────────────────────────────────────────────────────
|
||||||
static const Color success = Color(0xFF22C55E);
|
static const Color amber = AppColors.amber;
|
||||||
static const Color successLight = Color(0xFF86EFAC);
|
static const Color amberLight = AppColors.amberLight;
|
||||||
static const Color successPale = Color(0xFFF0FDF4);
|
static const Color gold = AppColors.accent;
|
||||||
|
static const Color goldLight = AppColors.accentLight;
|
||||||
|
static const Color goldPale = AppColors.accentContainer;
|
||||||
|
static const Color terracotta = AppColors.terracotta;
|
||||||
|
static const Color terracottaLight = AppColors.terracottaLight;
|
||||||
|
static const Color terracottaPale = AppColors.terracottaPale;
|
||||||
|
static const Color indigo = AppColors.indigo;
|
||||||
|
static const Color indigoLight = AppColors.indigoLight;
|
||||||
|
static const Color sand = AppColors.sand;
|
||||||
|
static const Color sandLight = AppColors.sandLight;
|
||||||
|
|
||||||
/// Attention - Avertissements
|
// ── Alias historiques ────────────────────────────────────────────────────
|
||||||
static const Color warning = Color(0xFFF59E0B);
|
static const Color unionGreen = AppColors.primary;
|
||||||
static const Color warningLight = Color(0xFFFBBF24);
|
static const Color unionGreenLight = AppColors.primaryLight;
|
||||||
static const Color warningPale = Color(0xFFFEFCE8);
|
static const Color unionGreenPale = AppColors.primaryContainer;
|
||||||
|
|
||||||
/// Erreur - Actions négatives
|
// ── Gradients ────────────────────────────────────────────────────────────
|
||||||
static const Color error = Color(0xFFEF4444);
|
static const LinearGradient logoGradient = AppColors.logoGradient;
|
||||||
static const Color errorLight = Color(0xFFFCA5A5);
|
static const LinearGradient primaryGradient = AppColors.primaryGradient;
|
||||||
static const Color errorPale = Color(0xFFFEF2F2);
|
static const LinearGradient loginGradient = AppColors.loginGradient;
|
||||||
|
static const LinearGradient subtleGradient = AppColors.subtleGradient;
|
||||||
|
static const LinearGradient warmGradient = AppColors.warmGradient;
|
||||||
|
static const LinearGradient goldGradient = AppColors.logoGradient;
|
||||||
|
|
||||||
/// Info - Informations neutres
|
// ── Ombres ───────────────────────────────────────────────────────────────
|
||||||
static const Color info = Color(0xFF3B82F6);
|
static List<BoxShadow> get softShadow => AppColors.softShadow;
|
||||||
static const Color infoLight = Color(0xFF93C5FD);
|
static List<BoxShadow> get mediumShadow => AppColors.mediumShadow;
|
||||||
static const Color infoPale = Color(0xFFEFF6FF);
|
static List<BoxShadow> get blueGlowShadow => AppColors.blueGlowShadow;
|
||||||
|
static List<BoxShadow> get purpleGlowShadow => AppColors.purpleGlowShadow;
|
||||||
// ═══════════════════════════════════════════════════════════════
|
static List<BoxShadow> get greenGlowShadow => AppColors.blueGlowShadow;
|
||||||
// COULEURS DE SURFACE
|
static List<BoxShadow> get goldGlowShadow => AppColors.purpleGlowShadow;
|
||||||
// ═══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Background principal
|
|
||||||
static const Color background = Color(0xFFF7F9FA);
|
|
||||||
|
|
||||||
/// Surface des cards
|
|
||||||
static const Color surface = Color(0xFFFFFFFF);
|
|
||||||
|
|
||||||
/// Surface variant (légèrement teintée)
|
|
||||||
static const Color surfaceVariant = Color(0xFFF5F7F8);
|
|
||||||
|
|
||||||
/// Surface avec effet glassmorphism
|
|
||||||
static const Color surfaceGlass = Color(0xFFFAFBFC);
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
|
||||||
// TEXTE & BORDURES
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Texte principal
|
|
||||||
static const Color textPrimary = Color(0xFF111827);
|
|
||||||
|
|
||||||
/// Texte secondaire
|
|
||||||
static const Color textSecondary = Color(0xFF6B7280);
|
|
||||||
|
|
||||||
/// Texte tertiaire
|
|
||||||
static const Color textTertiary = Color(0xFF9CA3AF);
|
|
||||||
|
|
||||||
/// Bordures subtiles
|
|
||||||
static const Color border = Color(0xFFE5E7EB);
|
|
||||||
|
|
||||||
/// Bordures moyennes
|
|
||||||
static const Color borderMedium = Color(0xFFD1D5DB);
|
|
||||||
|
|
||||||
/// Bordures fortes
|
|
||||||
static const Color borderStrong = Color(0xFF9CA3AF);
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
|
||||||
// GRADIENTS SIGNATURE
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Gradient principal (Vert → Or)
|
|
||||||
static const LinearGradient primaryGradient = LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [unionGreen, unionGreenLight],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Gradient chaleureux (Terracotta → Ambre)
|
|
||||||
static const LinearGradient warmGradient = LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [terracotta, amber],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Gradient or (Gold → Gold Light)
|
|
||||||
static const LinearGradient goldGradient = LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [gold, goldLight],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Gradient subtil pour backgrounds
|
|
||||||
static const LinearGradient subtleGradient = LinearGradient(
|
|
||||||
begin: Alignment.topCenter,
|
|
||||||
end: Alignment.bottomCenter,
|
|
||||||
colors: [Color(0xFFFAFBFC), Color(0xFFF7F9FA)],
|
|
||||||
);
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
|
||||||
// OMBRES SIGNATURE
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Ombre douce (cards, buttons)
|
|
||||||
static List<BoxShadow> get softShadow => [
|
|
||||||
BoxShadow(
|
|
||||||
color: const Color(0xFF000000).withOpacity(0.08),
|
|
||||||
blurRadius: 24,
|
|
||||||
offset: const Offset(0, 8),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Ombre moyenne (modals, floating elements)
|
|
||||||
static List<BoxShadow> get mediumShadow => [
|
|
||||||
BoxShadow(
|
|
||||||
color: const Color(0xFF000000).withOpacity(0.12),
|
|
||||||
blurRadius: 32,
|
|
||||||
offset: const Offset(0, 12),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Ombre forte (dialogs, overlays)
|
|
||||||
static List<BoxShadow> get strongShadow => [
|
|
||||||
BoxShadow(
|
|
||||||
color: const Color(0xFF000000).withOpacity(0.16),
|
|
||||||
blurRadius: 48,
|
|
||||||
offset: const Offset(0, 16),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Ombre colorée verte (pour CTAs)
|
|
||||||
static List<BoxShadow> get greenGlowShadow => [
|
|
||||||
BoxShadow(
|
|
||||||
color: unionGreen.withOpacity(0.2),
|
|
||||||
blurRadius: 20,
|
|
||||||
offset: const Offset(0, 8),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Ombre colorée dorée (pour éléments premium)
|
|
||||||
static List<BoxShadow> get goldGlowShadow => [
|
|
||||||
BoxShadow(
|
|
||||||
color: gold.withOpacity(0.25),
|
|
||||||
blurRadius: 20,
|
|
||||||
offset: const Offset(0, 8),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,93 +6,33 @@ import 'tokens/app_typography.dart';
|
|||||||
import 'tokens/spacing_tokens.dart';
|
import 'tokens/spacing_tokens.dart';
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// EXPORTS - Point d'entrée unique (DRY)
|
// EXPORTS — Point d'entrée unique du design system
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
export 'tokens/app_colors.dart';
|
export 'tokens/app_colors.dart';
|
||||||
export 'tokens/app_typography.dart';
|
export 'tokens/app_typography.dart';
|
||||||
export 'tokens/spacing_tokens.dart';
|
export 'tokens/spacing_tokens.dart';
|
||||||
|
export 'tokens/color_tokens.dart'; // ← Facade de compat → AppColors
|
||||||
|
export 'tokens/unionflow_colors.dart'; // ← Facade de compat → AppColors
|
||||||
|
export 'tokens/module_colors.dart'; // ← Identité chromatique par module
|
||||||
export 'theme/app_theme.dart';
|
export 'theme/app_theme.dart';
|
||||||
export 'components/components.dart';
|
export 'components/components.dart';
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// SHIMS DE COMPATIBILITÉ — Migration progressive vers design system unifié
|
// SHADOW TOKENS
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
/// Shim ColorTokens — palette Vert Forêt/Ardoise
|
|
||||||
class ColorTokens {
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Secondaires
|
|
||||||
static const Color secondary = AppColors.brandGreenLight;
|
|
||||||
static const Color secondaryContainer = Color(0xFFC8E6C9);
|
|
||||||
static const Color onSecondary = Colors.white;
|
|
||||||
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.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 = 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 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,
|
|
||||||
Color(0xFF388E3C),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shim ShadowTokens
|
|
||||||
class ShadowTokens {
|
class ShadowTokens {
|
||||||
static const List<BoxShadow> sm = [
|
static const List<BoxShadow> sm = [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x1A1C2B1C),
|
color: AppColors.shadowMedium,
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: Offset(0, 2),
|
offset: Offset(0, 2),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
static const List<BoxShadow> md = [
|
static const List<BoxShadow> md = [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x261C2B1C),
|
color: AppColors.shadowStrong,
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
offset: Offset(0, 4),
|
offset: Offset(0, 4),
|
||||||
),
|
),
|
||||||
@@ -100,7 +40,10 @@ class ShadowTokens {
|
|||||||
static const List<BoxShadow> primary = md;
|
static const List<BoxShadow> primary = md;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shim RadiusTokens
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// RADIUS TOKENS
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
class RadiusTokens {
|
class RadiusTokens {
|
||||||
static const double sm = SpacingTokens.radiusSm;
|
static const double sm = SpacingTokens.radiusSm;
|
||||||
static const double md = SpacingTokens.radiusMd;
|
static const double md = SpacingTokens.radiusMd;
|
||||||
@@ -110,48 +53,42 @@ class RadiusTokens {
|
|||||||
static const double round = SpacingTokens.radiusCircular;
|
static const double round = SpacingTokens.radiusCircular;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shim TypographyTokens
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// TYPOGRAPHY TOKENS (shim — renvoie vers AppTypography)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
class TypographyTokens {
|
class TypographyTokens {
|
||||||
// Display (Playfair Display via AppTypography getters — non-const)
|
|
||||||
static TextStyle get displayLarge => AppTypography.displayLarge;
|
static TextStyle get displayLarge => AppTypography.displayLarge;
|
||||||
static TextStyle get displayMedium => AppTypography.displayMedium;
|
static TextStyle get displayMedium => AppTypography.displayMedium;
|
||||||
static TextStyle get displaySmall => AppTypography.displaySmall;
|
static TextStyle get displaySmall => AppTypography.displaySmall;
|
||||||
|
|
||||||
// Headlines
|
|
||||||
static const TextStyle headlineLarge = AppTypography.headerLarge;
|
static const TextStyle headlineLarge = AppTypography.headerLarge;
|
||||||
static const TextStyle headlineMedium = AppTypography.headerSmall;
|
static const TextStyle headlineMedium = AppTypography.headerSmall;
|
||||||
static const TextStyle headlineSmall = AppTypography.titleMedium;
|
static const TextStyle headlineSmall = AppTypography.titleMedium;
|
||||||
|
|
||||||
// Titles
|
|
||||||
static const TextStyle titleLarge = AppTypography.headerSmall;
|
static const TextStyle titleLarge = AppTypography.headerSmall;
|
||||||
static const TextStyle titleMedium = AppTypography.titleMedium;
|
static const TextStyle titleMedium = AppTypography.titleMedium;
|
||||||
static const TextStyle titleSmall = AppTypography.titleSmall;
|
static const TextStyle titleSmall = AppTypography.titleSmall;
|
||||||
|
|
||||||
// Body
|
|
||||||
static const TextStyle bodyLarge = AppTypography.bodyLarge;
|
static const TextStyle bodyLarge = AppTypography.bodyLarge;
|
||||||
static const TextStyle bodyMedium = AppTypography.bodyMedium;
|
static const TextStyle bodyMedium = AppTypography.bodyMedium;
|
||||||
static const TextStyle bodySmall = AppTypography.bodyTextSmall;
|
static const TextStyle bodySmall = AppTypography.bodyTextSmall;
|
||||||
|
|
||||||
// Labels
|
|
||||||
static const TextStyle labelLarge = AppTypography.actionText;
|
static const TextStyle labelLarge = AppTypography.actionText;
|
||||||
static const TextStyle labelMedium = AppTypography.labelMedium;
|
static const TextStyle labelMedium = AppTypography.labelMedium;
|
||||||
static const TextStyle labelSmall = AppTypography.badgeText;
|
static const TextStyle labelSmall = AppTypography.badgeText;
|
||||||
|
|
||||||
// Buttons
|
|
||||||
static const TextStyle buttonLarge = AppTypography.buttonLabel;
|
static const TextStyle buttonLarge = AppTypography.buttonLabel;
|
||||||
static const TextStyle buttonMedium = AppTypography.actionText;
|
static const TextStyle buttonMedium = AppTypography.actionText;
|
||||||
|
|
||||||
// Cards
|
|
||||||
static const TextStyle cardTitle = AppTypography.headerSmall;
|
static const TextStyle cardTitle = AppTypography.headerSmall;
|
||||||
static const TextStyle cardSubtitle = AppTypography.bodyTextSmall;
|
static const TextStyle cardSubtitle = AppTypography.bodyTextSmall;
|
||||||
static const TextStyle cardValue = AppTypography.headerLarge;
|
static const TextStyle cardValue = AppTypography.headerLarge;
|
||||||
|
|
||||||
// Inputs
|
|
||||||
static const TextStyle inputLabel = AppTypography.labelMedium;
|
static const TextStyle inputLabel = AppTypography.labelMedium;
|
||||||
static const TextStyle inputText = AppTypography.bodyLarge;
|
static const TextStyle inputText = AppTypography.bodyLarge;
|
||||||
static const TextStyle inputHint = AppTypography.bodyTextSmall;
|
static const TextStyle inputHint = AppTypography.bodyTextSmall;
|
||||||
|
|
||||||
// Navigation
|
|
||||||
static const TextStyle navigationLabel = AppTypography.navLabel;
|
static const TextStyle navigationLabel = AppTypography.navLabel;
|
||||||
static const TextStyle navigationLabelSelected = AppTypography.navLabelSelected;
|
static const TextStyle navigationLabelSelected = AppTypography.navLabelSelected;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ library snackbar_helper;
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../core/error/failures.dart';
|
import '../../core/error/failures.dart';
|
||||||
|
import '../design_system/tokens/app_colors.dart';
|
||||||
|
|
||||||
/// Helper class for showing consistent Snackbar messages throughout the app
|
/// Helper class for showing consistent Snackbar messages throughout the app
|
||||||
class SnackbarHelper {
|
class SnackbarHelper {
|
||||||
@@ -16,12 +17,12 @@ class SnackbarHelper {
|
|||||||
final snackBar = SnackBar(
|
final snackBar = SnackBar(
|
||||||
content: Row(
|
content: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.check_circle, color: Colors.white),
|
const Icon(Icons.check_circle, color: AppColors.onPrimary),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: Text(message)),
|
Expanded(child: Text(message)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.green[700],
|
backgroundColor: AppColors.success,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
action: action,
|
action: action,
|
||||||
@@ -39,18 +40,18 @@ class SnackbarHelper {
|
|||||||
final snackBar = SnackBar(
|
final snackBar = SnackBar(
|
||||||
content: Row(
|
content: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.error_outline, color: Colors.white),
|
const Icon(Icons.error_outline, color: AppColors.onPrimary),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: Text(message)),
|
Expanded(child: Text(message)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.red[700],
|
backgroundColor: AppColors.error,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
action: onRetry != null
|
action: onRetry != null
|
||||||
? SnackBarAction(
|
? SnackBarAction(
|
||||||
label: 'Réessayer',
|
label: 'Réessayer',
|
||||||
textColor: Colors.white,
|
textColor: AppColors.onPrimary,
|
||||||
onPressed: onRetry,
|
onPressed: onRetry,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
@@ -67,12 +68,12 @@ class SnackbarHelper {
|
|||||||
final snackBar = SnackBar(
|
final snackBar = SnackBar(
|
||||||
content: Row(
|
content: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.warning_amber, color: Colors.white),
|
const Icon(Icons.warning_amber, color: AppColors.onPrimary),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: Text(message)),
|
Expanded(child: Text(message)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.orange[700],
|
backgroundColor: AppColors.warning,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
);
|
);
|
||||||
@@ -88,12 +89,12 @@ class SnackbarHelper {
|
|||||||
final snackBar = SnackBar(
|
final snackBar = SnackBar(
|
||||||
content: Row(
|
content: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.info_outline, color: Colors.white),
|
const Icon(Icons.info_outline, color: AppColors.onPrimary),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: Text(message)),
|
Expanded(child: Text(message)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.blue[700],
|
backgroundColor: AppColors.info,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
);
|
);
|
||||||
@@ -112,12 +113,12 @@ class SnackbarHelper {
|
|||||||
final snackBar = SnackBar(
|
final snackBar = SnackBar(
|
||||||
content: Row(
|
content: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.construction, color: Colors.white),
|
const Icon(Icons.construction, color: AppColors.onPrimary),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: Text(message)),
|
Expanded(child: Text(message)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.blue[700],
|
backgroundColor: AppColors.info,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
duration: const Duration(seconds: 4),
|
duration: const Duration(seconds: 4),
|
||||||
);
|
);
|
||||||
@@ -140,26 +141,26 @@ class SnackbarHelper {
|
|||||||
IconData icon;
|
IconData icon;
|
||||||
|
|
||||||
if (failure is NetworkFailure) {
|
if (failure is NetworkFailure) {
|
||||||
backgroundColor = Colors.orange[700]!;
|
backgroundColor = AppColors.warning;
|
||||||
icon = Icons.wifi_off;
|
icon = Icons.wifi_off;
|
||||||
} else if (failure is UnauthorizedFailure) {
|
} else if (failure is UnauthorizedFailure) {
|
||||||
backgroundColor = Colors.red[700]!;
|
backgroundColor = AppColors.error;
|
||||||
icon = Icons.lock_outline;
|
icon = Icons.lock_outline;
|
||||||
} else if (failure is ForbiddenFailure) {
|
} else if (failure is ForbiddenFailure) {
|
||||||
backgroundColor = Colors.deepOrange[700]!;
|
backgroundColor = AppColors.warning;
|
||||||
icon = Icons.block;
|
icon = Icons.block;
|
||||||
} else if (failure is ValidationFailure) {
|
} else if (failure is ValidationFailure) {
|
||||||
backgroundColor = Colors.amber[700]!;
|
backgroundColor = AppColors.warningUI;
|
||||||
icon = Icons.error_outline;
|
icon = Icons.error_outline;
|
||||||
} else {
|
} else {
|
||||||
backgroundColor = Colors.red[700]!;
|
backgroundColor = AppColors.error;
|
||||||
icon = Icons.error_outline;
|
icon = Icons.error_outline;
|
||||||
}
|
}
|
||||||
|
|
||||||
final snackBar = SnackBar(
|
final snackBar = SnackBar(
|
||||||
content: Row(
|
content: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, color: Colors.white),
|
Icon(icon, color: AppColors.onPrimary),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: Text(failure.getUserMessage())),
|
Expanded(child: Text(failure.getUserMessage())),
|
||||||
],
|
],
|
||||||
@@ -170,7 +171,7 @@ class SnackbarHelper {
|
|||||||
action: failure.isRetryable && onRetry != null
|
action: failure.isRetryable && onRetry != null
|
||||||
? SnackBarAction(
|
? SnackBarAction(
|
||||||
label: 'Réessayer',
|
label: 'Réessayer',
|
||||||
textColor: Colors.white,
|
textColor: AppColors.onPrimary,
|
||||||
onPressed: onRetry,
|
onPressed: onRetry,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
@@ -192,14 +193,14 @@ class SnackbarHelper {
|
|||||||
height: 16,
|
height: 16,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
valueColor: AlwaysStoppedAnimation<Color>(AppColors.onPrimary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(child: Text(message)),
|
Expanded(child: Text(message)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.grey[800],
|
backgroundColor: AppColors.surfaceDark,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class ActionRow extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final iconColor = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight;
|
final iconColor = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
@@ -75,18 +75,18 @@ class ActionRow extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.primaryGreen.withOpacity(0.1),
|
color: AppColors.primary.withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (customActionIcon != null) ...[
|
if (customActionIcon != null) ...[
|
||||||
Icon(customActionIcon, size: 14, color: AppColors.primaryGreen),
|
Icon(customActionIcon, size: 14, color: AppColors.primary),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
],
|
],
|
||||||
Text(
|
Text(
|
||||||
customActionLabel ?? '',
|
customActionLabel ?? '',
|
||||||
style: AppTypography.badgeText.copyWith(color: AppColors.primaryGreen),
|
style: AppTypography.badgeText.copyWith(color: AppColors.primary),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../design_system/tokens/app_colors.dart';
|
|
||||||
|
|
||||||
/// UnionFlow Mobile - Composant DRY Centralisé : CoreCard
|
/// UnionFlow Mobile - Composant DRY Centralisé : CoreCard
|
||||||
/// Le seul et unique conteneur d'affichage (Posts, Événements, Profils).
|
/// Le seul et unique conteneur d'affichage (Posts, Événements, Profils).
|
||||||
@@ -22,21 +21,21 @@ class CoreCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final scheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
margin: margin,
|
margin: margin,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor ?? (isDark ? AppColors.darkSurface : Colors.white),
|
color: backgroundColor ?? scheme.surface,
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isDark ? AppColors.darkBorder.withOpacity(0.5) : AppColors.lightBorder,
|
color: scheme.outlineVariant.withOpacity(0.6),
|
||||||
width: 0.8,
|
width: 0.8,
|
||||||
),
|
),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(isDark ? 0.15 : 0.04),
|
color: scheme.shadow.withOpacity(0.06),
|
||||||
blurRadius: 6,
|
blurRadius: 6,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -37,32 +37,32 @@ class CoreTextField extends StatelessWidget {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: hintText,
|
hintText: hintText,
|
||||||
hintStyle: AppTypography.subtitleSmall.copyWith(
|
hintStyle: AppTypography.subtitleSmall.copyWith(
|
||||||
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
|
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
|
||||||
),
|
),
|
||||||
prefixIcon: prefixIcon != null
|
prefixIcon: prefixIcon != null
|
||||||
? Icon(prefixIcon, size: 20, color: AppColors.primaryGreen)
|
? Icon(prefixIcon, size: 20, color: AppColors.primary)
|
||||||
: null,
|
: null,
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: isDark ? AppColors.darkSurface : AppColors.lightSurface,
|
fillColor: isDark ? AppColors.surfaceDark : AppColors.surface,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isDark ? AppColors.darkBorder : AppColors.lightBorder,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isDark ? AppColors.darkBorder : AppColors.lightBorder,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderSide: const BorderSide(
|
borderSide: const BorderSide(
|
||||||
color: AppColors.primaryGreen,
|
color: AppColors.primary,
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class DynamicFAB extends StatelessWidget {
|
|||||||
if (label != null) {
|
if (label != null) {
|
||||||
return FloatingActionButton.extended(
|
return FloatingActionButton.extended(
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
backgroundColor: AppColors.primaryGreen,
|
backgroundColor: AppColors.primary,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
icon: Icon(icon, size: 20),
|
icon: Icon(icon, size: 20),
|
||||||
@@ -34,7 +34,7 @@ class DynamicFAB extends StatelessWidget {
|
|||||||
|
|
||||||
return FloatingActionButton(
|
return FloatingActionButton(
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
backgroundColor: AppColors.primaryGreen,
|
backgroundColor: AppColors.primary,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
child: Icon(icon, size: 24),
|
child: Icon(icon, size: 24),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ library error_display_widget;
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../core/error/failures.dart';
|
import '../../core/error/failures.dart';
|
||||||
|
import '../design_system/tokens/app_colors.dart';
|
||||||
|
|
||||||
/// Error display widget that shows failures in a user-friendly way
|
/// Error display widget that shows failures in a user-friendly way
|
||||||
class ErrorDisplayWidget extends StatelessWidget {
|
class ErrorDisplayWidget extends StatelessWidget {
|
||||||
@@ -48,7 +49,7 @@ class ErrorDisplayWidget extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
failure.getUserMessage(),
|
failure.getUserMessage(),
|
||||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||||
color: Colors.grey[600],
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
@@ -98,15 +99,15 @@ class ErrorDisplayWidget extends StatelessWidget {
|
|||||||
/// Get appropriate color for error type
|
/// Get appropriate color for error type
|
||||||
Color _getErrorColor(BuildContext context) {
|
Color _getErrorColor(BuildContext context) {
|
||||||
if (failure is NetworkFailure) {
|
if (failure is NetworkFailure) {
|
||||||
return Colors.orange;
|
return AppColors.warning;
|
||||||
} else if (failure is UnauthorizedFailure) {
|
} else if (failure is UnauthorizedFailure) {
|
||||||
return Colors.red;
|
return AppColors.error;
|
||||||
} else if (failure is ForbiddenFailure) {
|
} else if (failure is ForbiddenFailure) {
|
||||||
return Colors.deepOrange;
|
return AppColors.warning;
|
||||||
} else if (failure is ValidationFailure) {
|
} else if (failure is ValidationFailure) {
|
||||||
return Colors.amber;
|
return AppColors.warningUI;
|
||||||
} else if (failure is NotImplementedFailure) {
|
} else if (failure is NotImplementedFailure) {
|
||||||
return Colors.blue[700]!;
|
return AppColors.info;
|
||||||
} else {
|
} else {
|
||||||
return Theme.of(context).colorScheme.error;
|
return Theme.of(context).colorScheme.error;
|
||||||
}
|
}
|
||||||
@@ -183,7 +184,7 @@ class ErrorBanner extends StatelessWidget {
|
|||||||
failure.getUserMessage(),
|
failure.getUserMessage(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: Colors.grey[700],
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -228,15 +229,15 @@ class ErrorBanner extends StatelessWidget {
|
|||||||
|
|
||||||
Color _getErrorColor(BuildContext context) {
|
Color _getErrorColor(BuildContext context) {
|
||||||
if (failure is NetworkFailure) {
|
if (failure is NetworkFailure) {
|
||||||
return Colors.orange;
|
return AppColors.warning;
|
||||||
} else if (failure is UnauthorizedFailure) {
|
} else if (failure is UnauthorizedFailure) {
|
||||||
return Colors.red;
|
return AppColors.error;
|
||||||
} else if (failure is ForbiddenFailure) {
|
} else if (failure is ForbiddenFailure) {
|
||||||
return Colors.deepOrange;
|
return AppColors.warning;
|
||||||
} else if (failure is ValidationFailure) {
|
} else if (failure is ValidationFailure) {
|
||||||
return Colors.amber;
|
return AppColors.warningUI;
|
||||||
} else if (failure is NotImplementedFailure) {
|
} else if (failure is NotImplementedFailure) {
|
||||||
return Colors.blue[700]!;
|
return AppColors.info;
|
||||||
} else {
|
} else {
|
||||||
return Theme.of(context).colorScheme.error;
|
return Theme.of(context).colorScheme.error;
|
||||||
}
|
}
|
||||||
@@ -282,7 +283,7 @@ void showErrorSnackBar(
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: failure is NetworkFailure ? Colors.orange : Colors.red,
|
backgroundColor: failure is NetworkFailure ? AppColors.warning : AppColors.error,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
action: failure.isRetryable && onRetry != null
|
action: failure.isRetryable && onRetry != null
|
||||||
? SnackBarAction(
|
? SnackBarAction(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class InfoBadge extends StatelessWidget {
|
|||||||
const InfoBadge({
|
const InfoBadge({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.text,
|
required this.text,
|
||||||
this.backgroundColor = AppColors.brandGreenLight,
|
this.backgroundColor = AppColors.primaryLight,
|
||||||
this.textColor = Colors.white,
|
this.textColor = Colors.white,
|
||||||
this.icon,
|
this.icon,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|||||||
@@ -67,12 +67,12 @@ class ShimmerListLoading extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
child: Shimmer.fromColors(
|
child: Shimmer.fromColors(
|
||||||
baseColor: Colors.grey[300]!,
|
baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
highlightColor: Colors.grey[100]!,
|
highlightColor: Theme.of(context).colorScheme.surface,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: itemHeight,
|
height: itemHeight,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -97,13 +97,13 @@ class ShimmerCardLoading extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor: Colors.grey[300]!,
|
baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
highlightColor: Colors.grey[100]!,
|
highlightColor: Theme.of(context).colorScheme.surface,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: height,
|
height: height,
|
||||||
width: width,
|
width: width,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -137,11 +137,11 @@ class ShimmerGridLoading extends StatelessWidget {
|
|||||||
itemCount: itemCount,
|
itemCount: itemCount,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor: Colors.grey[300]!,
|
baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
highlightColor: Colors.grey[100]!,
|
highlightColor: Theme.of(context).colorScheme.surface,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -158,8 +158,8 @@ class ShimmerDetailLoading extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor: Colors.grey[300]!,
|
baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
highlightColor: Colors.grey[100]!,
|
highlightColor: Theme.of(context).colorScheme.surface,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -169,7 +169,7 @@ class ShimmerDetailLoading extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
height: 200,
|
height: 200,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -178,14 +178,14 @@ class ShimmerDetailLoading extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
height: 24,
|
height: 24,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
color: Colors.white,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
// Subtitle
|
// Subtitle
|
||||||
Container(
|
Container(
|
||||||
height: 16,
|
height: 16,
|
||||||
width: 200,
|
width: 200,
|
||||||
color: Colors.white,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
// Content lines
|
// Content lines
|
||||||
@@ -195,7 +195,7 @@ class ShimmerDetailLoading extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
height: 12,
|
height: 12,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
color: Colors.white,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|||||||
90
lib/shared/widgets/powered_by_lions_dev.dart
Normal file
90
lib/shared/widgets/powered_by_lions_dev.dart
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import '../design_system/unionflow_design_system.dart';
|
||||||
|
|
||||||
|
/// Widget "Powered by Lions Dev" — affiche le logo Lions Dev adaptatif (dark/light)
|
||||||
|
/// avec lien cliquable vers https://www.lions.dev
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```dart
|
||||||
|
/// const PoweredByLionsDev()
|
||||||
|
/// // ou compact:
|
||||||
|
/// const PoweredByLionsDev(compact: true)
|
||||||
|
/// ```
|
||||||
|
class PoweredByLionsDev extends StatelessWidget {
|
||||||
|
/// Si true, affichage compact (logo plus petit, sans label "Powered by")
|
||||||
|
final bool compact;
|
||||||
|
|
||||||
|
/// Couleur du label "Powered by" (par défaut : couleur secondaire du thème)
|
||||||
|
final Color? labelColor;
|
||||||
|
|
||||||
|
/// Hauteur du logo (par défaut : 28 normal, 20 compact)
|
||||||
|
final double? logoHeight;
|
||||||
|
|
||||||
|
/// Force une variante (utile sur fond toujours sombre/clair comme login).
|
||||||
|
/// Si null, suit le thème courant.
|
||||||
|
final Brightness? forceBrightness;
|
||||||
|
|
||||||
|
const PoweredByLionsDev({
|
||||||
|
super.key,
|
||||||
|
this.compact = false,
|
||||||
|
this.labelColor,
|
||||||
|
this.logoHeight,
|
||||||
|
this.forceBrightness,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> _openLionsDev() async {
|
||||||
|
final uri = Uri.parse('https://www.lions.dev');
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final brightness = forceBrightness ?? Theme.of(context).brightness;
|
||||||
|
final isDark = brightness == Brightness.dark;
|
||||||
|
// Logo blanc sur fond sombre, logo noir sur fond clair
|
||||||
|
final logoAsset = isDark
|
||||||
|
? 'assets/images/branding/lions_dev_white.png'
|
||||||
|
: 'assets/images/branding/lions_dev_dark.png';
|
||||||
|
|
||||||
|
final effectiveLabelColor = labelColor ??
|
||||||
|
(isDark ? AppColors.textSecondaryDark : AppColors.textSecondary);
|
||||||
|
|
||||||
|
final effectiveHeight = logoHeight ?? (compact ? 20.0 : 28.0);
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
onTap: _openLionsDev,
|
||||||
|
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: SpacingTokens.md,
|
||||||
|
vertical: SpacingTokens.sm,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (!compact) ...[
|
||||||
|
Text(
|
||||||
|
'Powered by',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: effectiveLabelColor,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: SpacingTokens.sm),
|
||||||
|
],
|
||||||
|
Image.asset(
|
||||||
|
logoAsset,
|
||||||
|
height: effectiveHeight,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ library validated_text_field;
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import '../design_system/tokens/app_colors.dart';
|
||||||
|
|
||||||
/// Validated text field with consistent styling and behavior
|
/// Validated text field with consistent styling and behavior
|
||||||
class ValidatedTextField extends StatelessWidget {
|
class ValidatedTextField extends StatelessWidget {
|
||||||
@@ -61,6 +62,7 @@ class ValidatedTextField extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
@@ -73,30 +75,32 @@ class ValidatedTextField extends StatelessWidget {
|
|||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.grey.shade400,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: const OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.blue,
|
color: AppColors.primary,
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorBorder: const OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.red,
|
color: AppColors.error,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedErrorBorder: const OutlineInputBorder(
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.red,
|
color: AppColors.error,
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
filled: !enabled,
|
filled: !enabled,
|
||||||
fillColor: !enabled ? Colors.grey.shade100 : null,
|
fillColor: !enabled
|
||||||
|
? (isDark ? AppColors.surfaceVariantDark : AppColors.backgroundSubtle)
|
||||||
|
: null,
|
||||||
counterText: showCounter ? null : '',
|
counterText: showCounter ? null : '',
|
||||||
),
|
),
|
||||||
validator: validator,
|
validator: validator,
|
||||||
@@ -165,10 +169,10 @@ class ValidatedAmountField extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
currencySymbol,
|
currencySymbol,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.grey,
|
color: AppColors.textTertiary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -206,6 +210,7 @@ class ValidatedDropdownField<T> extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
return DropdownButtonFormField<T>(
|
return DropdownButtonFormField<T>(
|
||||||
value: value,
|
value: value,
|
||||||
items: items,
|
items: items,
|
||||||
@@ -216,24 +221,26 @@ class ValidatedDropdownField<T> extends StatelessWidget {
|
|||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.grey.shade400,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: const OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.blue,
|
color: AppColors.primary,
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorBorder: const OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.red,
|
color: AppColors.error,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
filled: !enabled,
|
filled: !enabled,
|
||||||
fillColor: !enabled ? Colors.grey.shade100 : null,
|
fillColor: !enabled
|
||||||
|
? (isDark ? AppColors.surfaceVariantDark : AppColors.backgroundSubtle)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
validator: validator,
|
validator: validator,
|
||||||
onChanged: enabled ? onChanged : null,
|
onChanged: enabled ? onChanged : null,
|
||||||
@@ -269,6 +276,7 @@ class ValidatedDateField extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: enabled
|
onTap: enabled
|
||||||
? () async {
|
? () async {
|
||||||
@@ -292,24 +300,26 @@ class ValidatedDateField extends StatelessWidget {
|
|||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.grey.shade400,
|
color: isDark ? AppColors.borderDark : AppColors.border,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: const OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.blue,
|
color: AppColors.primary,
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorBorder: const OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.red,
|
color: AppColors.error,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
filled: !enabled,
|
filled: !enabled,
|
||||||
fillColor: !enabled ? Colors.grey.shade100 : null,
|
fillColor: !enabled
|
||||||
|
? (isDark ? AppColors.surfaceVariantDark : AppColors.backgroundSubtle)
|
||||||
|
: null,
|
||||||
errorText: validator != null ? validator!(selectedDate) : null,
|
errorText: validator != null ? validator!(selectedDate) : null,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -317,7 +327,9 @@ class ValidatedDateField extends StatelessWidget {
|
|||||||
? '${selectedDate!.day}/${selectedDate!.month}/${selectedDate!.year}'
|
? '${selectedDate!.day}/${selectedDate!.month}/${selectedDate!.year}'
|
||||||
: hintText ?? 'Sélectionner une date',
|
: hintText ?? 'Sélectionner une date',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: selectedDate != null ? Colors.black87 : Colors.grey,
|
color: selectedDate != null
|
||||||
|
? (isDark ? AppColors.textPrimaryDark : AppColors.textPrimary)
|
||||||
|
: AppColors.textTertiary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user