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:
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user