feat(ui): dark mode adaptatif sur 15 pages/widgets restants

Pattern AppColors pair (isDark ternaries) appliqué sur :
- login_page : SnackBar error color Color(0xFFDC2626) → AppColors.error
  (gradient brand intentionnel non modifié)
- help_support : barre de recherche + ExpansionTile + chevrons → scheme adaptatif
- system_settings : état 'Accès réservé' + unselectedLabelColor TabBar
- epargne : date/description/boutons OutlinedButton foregroundColor adaptatifs
- conversation_tile, connected_recent_activities, connected_upcoming_events
- dashboard_notifications_widget
- budgets_list_page, pending_approvals_page, approve/reject_dialog
- create_organization_page, edit_organization_page, about_page

Les couleurs sémantiques (error, success, warning, primary) restent inchangées.
Les blancs/gradients intentionnels (AppBars brand, logos payment) préservés.
This commit is contained in:
dahoud
2026-04-15 20:14:59 +00:00
parent b2f29922d3
commit ba779a7a40
15 changed files with 1002 additions and 1146 deletions

View File

@@ -38,9 +38,10 @@ class _BudgetsListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorTokens.background,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: UFAppBar(
title: 'BUDGETS',
title: 'Budgets',
moduleGradient: ModuleColors.financeWorkflowGradient,
automaticallyImplyLeading: true,
actions: [
IconButton(
@@ -57,7 +58,9 @@ class _BudgetsListView extends StatelessWidget {
),
],
),
body: BlocConsumer<BudgetBloc, BudgetState>(
body: SafeArea(
top: false,
child: BlocConsumer<BudgetBloc, BudgetState>(
listener: (context, state) {
if (state is BudgetCreated) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -98,13 +101,14 @@ class _BudgetsListView extends StatelessWidget {
return const SizedBox.shrink();
},
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
SnackbarHelper.showNotImplemented(context, 'Création de budget');
},
icon: const Icon(Icons.add),
label: const Text('Nouveau budget'),
backgroundColor: AppColors.primaryGreen,
backgroundColor: AppColors.primary,
),
);
}
@@ -143,7 +147,7 @@ class _BudgetsListView extends StatelessWidget {
Widget _buildFilterChips(BuildContext context, BudgetsLoaded state) {
return Container(
padding: const EdgeInsets.all(SpacingTokens.md),
color: AppColors.lightBackground,
color: Theme.of(context).scaffoldBackgroundColor,
child: Wrap(
spacing: SpacingTokens.sm,
runSpacing: SpacingTokens.sm,
@@ -178,28 +182,36 @@ class _BudgetsListView extends StatelessWidget {
}
Widget _buildEmptyState(String message) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.account_balance_wallet_outlined,
size: 48,
color: AppColors.textSecondaryLight.withOpacity(0.5),
return Builder(
builder: (context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.account_balance_wallet_outlined,
size: 48,
color: textSecondary.withOpacity(0.5),
),
const SizedBox(height: SpacingTokens.lg),
Text(
message,
style: AppTypography.headerSmall.copyWith(
color: textSecondary,
),
),
],
),
const SizedBox(height: SpacingTokens.lg),
Text(
message,
style: AppTypography.headerSmall.copyWith(
color: AppColors.textSecondaryLight,
),
),
],
),
);
},
);
}
Widget _buildErrorState(BuildContext context, String message) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -213,7 +225,7 @@ class _BudgetsListView extends StatelessWidget {
Text(
message,
style: AppTypography.headerSmall.copyWith(
color: AppColors.textSecondaryLight,
color: textSecondary,
),
textAlign: TextAlign.center,
),
@@ -274,14 +286,14 @@ class _BudgetCard extends StatelessWidget {
}
}
Color _getStatusColor(BudgetStatus status) {
Color _getStatusColor(BudgetStatus status, {bool isDark = false}) {
switch (status) {
case BudgetStatus.draft:
return AppColors.textSecondaryLight;
return isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
case BudgetStatus.active:
return AppColors.brandGreen;
return AppColors.primaryDark;
case BudgetStatus.closed:
return AppColors.textSecondaryLight;
return isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
case BudgetStatus.cancelled:
return AppColors.error;
}
@@ -302,17 +314,20 @@ class _BudgetCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final currencyFormat = NumberFormat.currency(symbol: budget.currency);
return Container(
padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
border: Border.all(color: AppColors.lightBorder),
border: Border.all(
color: isDark ? AppColors.borderDark : AppColors.border,
),
boxShadow: const [
BoxShadow(
color: Color(0x0A000000),
color: AppColors.shadow,
blurRadius: 8,
offset: Offset(0, 2),
),
@@ -335,13 +350,13 @@ class _BudgetCard extends StatelessWidget {
vertical: 4,
),
decoration: BoxDecoration(
color: _getStatusColor(budget.status).withOpacity(0.1),
color: _getStatusColor(budget.status, isDark: isDark).withOpacity(0.1),
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
),
child: Text(
_getStatusLabel(budget.status),
style: AppTypography.badgeText.copyWith(
color: _getStatusColor(budget.status),
color: _getStatusColor(budget.status, isDark: isDark),
fontWeight: FontWeight.bold,
),
),
@@ -368,7 +383,7 @@ class _BudgetCard extends StatelessWidget {
Text(
currencyFormat.format(budget.totalPlanned),
style: AppTypography.headerSmall.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
),
),
@@ -389,7 +404,7 @@ class _BudgetCard extends StatelessWidget {
style: AppTypography.headerSmall.copyWith(
color: budget.isOverBudget
? AppColors.error
: AppColors.brandGreen,
: AppColors.primaryDark,
fontWeight: FontWeight.bold,
),
),
@@ -401,10 +416,10 @@ class _BudgetCard extends StatelessWidget {
const SizedBox(height: SpacingTokens.sm),
LinearProgressIndicator(
value: budget.realizationRate / 100,
backgroundColor: AppColors.lightBorder,
backgroundColor: isDark ? AppColors.borderDark : AppColors.border,
color: budget.isOverBudget
? AppColors.error
: AppColors.brandGreen,
: AppColors.primaryDark,
),
const SizedBox(height: SpacingTokens.xs),
Text(

View File

@@ -39,9 +39,10 @@ class _PendingApprovalsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorTokens.background,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: UFAppBar(
title: 'APPROBATIONS EN ATTENTE',
title: 'Approbations en attente',
moduleGradient: ModuleColors.financeWorkflowGradient,
automaticallyImplyLeading: true,
actions: [
IconButton(
@@ -155,6 +156,7 @@ class _PendingApprovalsView extends StatelessWidget {
}
Widget _buildErrorState(BuildContext context, String message) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -168,7 +170,7 @@ class _PendingApprovalsView extends StatelessWidget {
Text(
message,
style: AppTypography.headerSmall.copyWith(
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
),
textAlign: TextAlign.center,
),
@@ -188,31 +190,37 @@ class _PendingApprovalsView extends StatelessWidget {
}
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.check_circle_outline,
size: 48,
color: AppColors.success.withOpacity(0.5),
return Builder(
builder: (context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.check_circle_outline,
size: 48,
color: AppColors.success.withOpacity(0.5),
),
const SizedBox(height: SpacingTokens.lg),
Text(
'Aucune approbation en attente',
style: AppTypography.headerSmall.copyWith(
color: textSecondary,
),
),
const SizedBox(height: SpacingTokens.sm),
Text(
'Toutes les transactions sont approuvées',
style: AppTypography.bodyTextSmall.copyWith(
color: textSecondary,
),
),
],
),
const SizedBox(height: SpacingTokens.lg),
Text(
'Aucune approbation en attente',
style: AppTypography.headerSmall.copyWith(
color: AppColors.textSecondaryLight,
),
),
const SizedBox(height: SpacingTokens.sm),
Text(
'Toutes les transactions sont approuvées',
style: AppTypography.bodyTextSmall.copyWith(
color: AppColors.textSecondaryLight,
),
),
],
),
);
},
);
}
@@ -248,12 +256,12 @@ class _ApprovalCard extends StatelessWidget {
}
}
Color _getLevelColor(ApprovalLevel level) {
Color _getLevelColor(ApprovalLevel level, {bool isDark = false}) {
switch (level) {
case ApprovalLevel.none:
return AppColors.textSecondaryLight;
return isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
case ApprovalLevel.level1:
return AppColors.brandGreen;
return AppColors.primaryDark;
case ApprovalLevel.level2:
return AppColors.warning;
case ApprovalLevel.level3:
@@ -263,17 +271,18 @@ class _ApprovalCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final currencyFormat = NumberFormat.currency(symbol: approval.currency);
return Container(
padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
border: Border.all(color: AppColors.lightBorder),
border: Border.all(color: isDark ? AppColors.borderDark : AppColors.border),
boxShadow: const [
BoxShadow(
color: Color(0x0A000000),
color: AppColors.shadow,
blurRadius: 8,
offset: Offset(0, 2),
),
@@ -296,13 +305,13 @@ class _ApprovalCard extends StatelessWidget {
vertical: 4,
),
decoration: BoxDecoration(
color: _getLevelColor(approval.requiredLevel).withOpacity(0.1),
color: _getLevelColor(approval.requiredLevel, isDark: isDark).withOpacity(0.1),
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
),
child: Text(
'Niveau ${approval.requiredApprovals}',
style: AppTypography.badgeText.copyWith(
color: _getLevelColor(approval.requiredLevel),
color: _getLevelColor(approval.requiredLevel, isDark: isDark),
fontWeight: FontWeight.bold,
),
),
@@ -313,7 +322,7 @@ class _ApprovalCard extends StatelessWidget {
Text(
currencyFormat.format(approval.amount),
style: AppTypography.headerSmall.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
),
),
@@ -323,7 +332,7 @@ class _ApprovalCard extends StatelessWidget {
Icon(
Icons.person_outline,
size: 16,
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
),
const SizedBox(width: 4),
Expanded(
@@ -340,7 +349,7 @@ class _ApprovalCard extends StatelessWidget {
Icon(
Icons.access_time,
size: 16,
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
),
const SizedBox(width: 4),
Text(

View File

@@ -53,6 +53,7 @@ class _ApproveDialogState extends State<ApproveDialog> {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final currencyFormat = NumberFormat.currency(symbol: widget.approval.currency);
return AlertDialog(
@@ -72,9 +73,9 @@ class _ApproveDialogState extends State<ApproveDialog> {
Container(
padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration(
color: AppColors.lightBackground,
color: isDark ? AppColors.surfaceDark : AppColors.surface,
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
border: Border.all(color: AppColors.lightBorder),
border: Border.all(color: isDark ? AppColors.borderDark : AppColors.border),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -88,7 +89,7 @@ class _ApproveDialogState extends State<ApproveDialog> {
'Montant',
currencyFormat.format(widget.approval.amount),
valueStyle: AppTypography.actionText.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
),
),
@@ -145,7 +146,7 @@ class _ApproveDialogState extends State<ApproveDialog> {
label: const Text('Approuver'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.success,
foregroundColor: Colors.white,
foregroundColor: AppColors.onPrimary,
),
),
],

View File

@@ -53,6 +53,7 @@ class _RejectDialogState extends State<RejectDialog> {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final currencyFormat = NumberFormat.currency(symbol: widget.approval.currency);
return AlertDialog(
@@ -75,9 +76,9 @@ class _RejectDialogState extends State<RejectDialog> {
Container(
padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration(
color: AppColors.lightBackground,
color: isDark ? AppColors.surfaceDark : AppColors.surface,
borderRadius: BorderRadius.circular(SpacingTokens.radiusSm),
border: Border.all(color: AppColors.lightBorder),
border: Border.all(color: isDark ? AppColors.borderDark : AppColors.border),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -91,7 +92,7 @@ class _RejectDialogState extends State<RejectDialog> {
'Montant',
currencyFormat.format(widget.approval.amount),
valueStyle: AppTypography.actionText.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
),
),
@@ -141,7 +142,7 @@ class _RejectDialogState extends State<RejectDialog> {
label: const Text('Rejeter'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.error,
foregroundColor: Colors.white,
foregroundColor: AppColors.onError,
),
),
],