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

@@ -10,6 +10,8 @@ import '../../bloc/organizations_state.dart';
import '../../bloc/org_types_bloc.dart';
import '../../domain/entities/type_reference_entity.dart';
import '../../../../shared/design_system/tokens/app_colors.dart';
import '../../../../shared/design_system/tokens/module_colors.dart';
import '../../../../shared/design_system/components/uf_app_bar.dart';
import '../../../../core/di/injection_container.dart';
const List<String> _devises = ['XOF', 'XAF', 'EUR', 'USD', 'GBP', 'CAD', 'CHF', 'MAD', 'GHS', 'NGN', 'CDF', 'KES'];
@@ -106,12 +108,10 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.lightBackground,
appBar: AppBar(
backgroundColor: AppColors.primaryGreen,
foregroundColor: Colors.white,
title: const Text('Nouvelle Organisation'),
elevation: 0,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: UFAppBar(
title: 'Nouvelle Organisation',
moduleGradient: ModuleColors.organisationsGradient,
actions: [
TextButton(
onPressed: _isFormValid() ? _saveOrganisation : null,
@@ -119,7 +119,9 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
),
],
),
body: BlocListener<OrganizationsBloc, OrganizationsState>(
body: SafeArea(
top: false,
child: BlocListener<OrganizationsBloc, OrganizationsState>(
listener: (context, state) {
if (state is OrganizationCreated) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -128,7 +130,7 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
Navigator.of(context).pop(true);
} else if (state is OrganizationsError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message), backgroundColor: Colors.red),
SnackBar(content: Text(state.message), backgroundColor: AppColors.error),
);
}
},
@@ -161,20 +163,21 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
),
),
),
),
);
}
Widget _buildSection(String title, IconData icon, List<Widget> children) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Icon(icon, size: 16, color: AppColors.primaryGreen),
Icon(icon, size: 16, color: AppColors.primary),
const SizedBox(width: 6),
Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold, color: AppColors.primaryGreen)),
Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold, color: AppColors.primary)),
]),
const SizedBox(height: 10),
...children,
@@ -237,9 +240,18 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
onTap: () => _pickDateFondation(context),
child: InputDecorator(
decoration: const InputDecoration(labelText: 'Date de fondation', border: OutlineInputBorder(), prefixIcon: Icon(Icons.cake)),
child: Text(
_dateFondation != null ? _formatDate(_dateFondation) : 'Sélectionner une date',
style: TextStyle(color: _dateFondation != null ? AppColors.textPrimaryLight : AppColors.textSecondaryLight),
child: Builder(
builder: (ctx) {
final isDark = Theme.of(ctx).brightness == Brightness.dark;
return Text(
_dateFondation != null ? _formatDate(_dateFondation) : 'Sélectionner une date',
style: TextStyle(
color: _dateFondation != null
? (isDark ? AppColors.textPrimaryDark : AppColors.textPrimary)
: (isDark ? AppColors.textSecondaryDark : AppColors.textSecondary),
),
);
},
),
),
),
@@ -355,7 +367,7 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
subtitle: const Text('Visible par tous les utilisateurs', style: TextStyle(fontSize: 12)),
value: _organisationPublique,
onChanged: (v) => setState(() => _organisationPublique = v),
activeColor: AppColors.primaryGreen,
activeColor: AppColors.primary,
),
SwitchListTile(
dense: true,
@@ -364,7 +376,7 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
subtitle: const Text('Les demandes d\'adhésion sont ouvertes', style: TextStyle(fontSize: 12)),
value: _accepteNouveauxMembres,
onChanged: (v) => setState(() => _accepteNouveauxMembres = v),
activeColor: AppColors.primaryGreen,
activeColor: AppColors.primary,
),
];
@@ -392,7 +404,7 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
title: const Text('Cotisation obligatoire', style: TextStyle(fontSize: 14)),
value: _cotisationObligatoire,
onChanged: (v) => setState(() => _cotisationObligatoire = v),
activeColor: AppColors.primaryGreen,
activeColor: AppColors.primary,
),
if (_cotisationObligatoire) ...[
const SizedBox(height: 8),
@@ -451,8 +463,8 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
icon: const Icon(Icons.save),
label: const Text('Créer l\'organisation'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryGreen,
foregroundColor: Colors.white,
backgroundColor: AppColors.primary,
foregroundColor: AppColors.onPrimary,
padding: const EdgeInsets.symmetric(vertical: 10),
textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
@@ -466,7 +478,7 @@ class _CreateOrganizationPageState extends State<CreateOrganizationPage> {
icon: const Icon(Icons.cancel),
label: const Text('Annuler'),
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.textSecondaryLight,
foregroundColor: Theme.of(context).brightness == Brightness.dark ? AppColors.textSecondaryDark : AppColors.textSecondary,
padding: const EdgeInsets.symmetric(vertical: 10),
),
),

View File

@@ -10,6 +10,8 @@ import '../../bloc/organizations_state.dart';
import '../../bloc/org_types_bloc.dart';
import '../../domain/entities/type_reference_entity.dart';
import '../../../../shared/design_system/tokens/app_colors.dart';
import '../../../../shared/design_system/tokens/module_colors.dart';
import '../../../../shared/design_system/components/uf_app_bar.dart';
import '../../../../core/di/injection_container.dart';
const List<String> _devisesEdit = ['XOF', 'XAF', 'EUR', 'USD', 'GBP', 'CAD', 'CHF', 'MAD', 'GHS', 'NGN', 'CDF', 'KES'];
@@ -185,12 +187,10 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.lightBackground,
appBar: AppBar(
backgroundColor: AppColors.primaryGreen,
foregroundColor: Colors.white,
title: const Text('Modifier Organisation'),
elevation: 0,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: UFAppBar(
title: 'Modifier Organisation',
moduleGradient: ModuleColors.organisationsGradient,
actions: [
TextButton(
onPressed: _hasChanges() ? _saveChanges : null,
@@ -212,7 +212,7 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
Navigator.of(context).pop(true);
} else if (state is OrganizationsError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message), backgroundColor: Colors.red),
SnackBar(content: Text(state.message), backgroundColor: AppColors.error),
);
}
},
@@ -254,14 +254,14 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
Widget _buildSection(String title, IconData icon, List<Widget> children) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Icon(icon, size: 16, color: AppColors.primaryGreen),
Icon(icon, size: 16, color: AppColors.primary),
const SizedBox(width: 6),
Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold, color: AppColors.primaryGreen)),
Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold, color: AppColors.primary)),
]),
const SizedBox(height: 10),
...children,
@@ -326,9 +326,18 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
onTap: () => _pickDateFondation(context),
child: InputDecorator(
decoration: const InputDecoration(labelText: 'Date de fondation', border: OutlineInputBorder(), prefixIcon: Icon(Icons.cake)),
child: Text(
_dateFondation != null ? _formatDate(_dateFondation) : 'Sélectionner une date',
style: TextStyle(color: _dateFondation != null ? AppColors.textPrimaryLight : AppColors.textSecondaryLight),
child: Builder(
builder: (ctx) {
final isDark = Theme.of(ctx).brightness == Brightness.dark;
return Text(
_dateFondation != null ? _formatDate(_dateFondation) : 'Sélectionner une date',
style: TextStyle(
color: _dateFondation != null
? (isDark ? AppColors.textPrimaryDark : AppColors.textPrimary)
: (isDark ? AppColors.textSecondaryDark : AppColors.textSecondary),
),
);
},
),
),
),
@@ -452,7 +461,7 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
subtitle: const Text('Visible par tous les utilisateurs', style: TextStyle(fontSize: 12)),
value: _organisationPublique,
onChanged: (v) => setState(() => _organisationPublique = v),
activeColor: AppColors.primaryGreen,
activeColor: AppColors.primary,
),
SwitchListTile(
dense: true,
@@ -461,7 +470,7 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
subtitle: const Text('Les demandes d\'adhésion sont ouvertes', style: TextStyle(fontSize: 12)),
value: _accepteNouveauxMembres,
onChanged: (v) => setState(() => _accepteNouveauxMembres = v),
activeColor: AppColors.primaryGreen,
activeColor: AppColors.primary,
),
];
@@ -490,7 +499,7 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
title: const Text('Cotisation obligatoire', style: TextStyle(fontSize: 14)),
value: _cotisationObligatoire,
onChanged: (v) => setState(() => _cotisationObligatoire = v),
activeColor: AppColors.primaryGreen,
activeColor: AppColors.primary,
),
if (_cotisationObligatoire) ...[
const SizedBox(height: 8),
@@ -559,12 +568,15 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
];
Widget _buildReadOnlyRow(IconData icon, String label, String value) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimary;
return Row(children: [
Icon(icon, size: 18, color: AppColors.textSecondaryLight),
Icon(icon, size: 18, color: textSecondary),
const SizedBox(width: 10),
Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(label, style: const TextStyle(fontSize: 11, color: AppColors.textSecondaryLight)),
Text(value, style: const TextStyle(fontSize: 13, color: AppColors.textPrimaryLight, fontWeight: FontWeight.w600)),
Text(label, style: TextStyle(fontSize: 11, color: textSecondary)),
Text(value, style: TextStyle(fontSize: 13, color: textPrimary, fontWeight: FontWeight.w600)),
])),
]);
}
@@ -578,8 +590,8 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
icon: const Icon(Icons.save),
label: const Text('Enregistrer les modifications'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryGreen,
foregroundColor: Colors.white,
backgroundColor: AppColors.primary,
foregroundColor: AppColors.onPrimary,
padding: const EdgeInsets.symmetric(vertical: 10),
textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
@@ -592,7 +604,7 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
onPressed: _showDiscardDialog,
icon: const Icon(Icons.cancel),
label: const Text('Annuler'),
style: OutlinedButton.styleFrom(foregroundColor: AppColors.textSecondaryLight, padding: const EdgeInsets.symmetric(vertical: 10)),
style: OutlinedButton.styleFrom(foregroundColor: Theme.of(context).brightness == Brightness.dark ? AppColors.textSecondaryDark : AppColors.textSecondary, padding: const EdgeInsets.symmetric(vertical: 10)),
),
),
]);
@@ -681,8 +693,8 @@ class _EditOrganizationPageState extends State<EditOrganizationPage> {
TextButton(onPressed: () => Navigator.of(ctx).pop(), child: const Text('Continuer l\'édition')),
ElevatedButton(
onPressed: () { Navigator.of(ctx).pop(); Navigator.of(context).pop(); },
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text('Abandonner', style: TextStyle(color: Colors.white)),
style: ElevatedButton.styleFrom(backgroundColor: AppColors.error),
child: const Text('Abandonner', style: TextStyle(color: AppColors.onPrimary)),
),
],
),