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

@@ -33,16 +33,16 @@ class ConnectedRecentActivities extends StatelessWidget {
BlocBuilder<DashboardBloc, DashboardState>(
builder: (context, state) {
if (state is DashboardLoading) {
return _buildLoadingList();
return _buildLoadingList(context);
} else if (state is DashboardLoaded || state is DashboardRefreshing) {
final data = state is DashboardLoaded
? state.dashboardData
final data = state is DashboardLoaded
? state.dashboardData
: (state as DashboardRefreshing).dashboardData;
return _buildActivitiesList(context, data.recentActivities);
} else if (state is DashboardError) {
return _buildErrorState(state.message);
}
return _buildEmptyState();
return _buildEmptyState(context);
},
),
],
@@ -55,7 +55,7 @@ class ConnectedRecentActivities extends StatelessWidget {
children: [
const Icon(
Icons.history,
color: AppColors.primaryGreen,
color: AppColors.primary,
size: 18,
),
const SizedBox(width: 8),
@@ -71,7 +71,7 @@ class ConnectedRecentActivities extends StatelessWidget {
child: Text(
'TOUT VOIR',
style: AppTypography.badgeText.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
),
),
@@ -82,7 +82,7 @@ class ConnectedRecentActivities extends StatelessWidget {
Widget _buildActivitiesList(BuildContext context, List<RecentActivityEntity> activities) {
if (activities.isEmpty) {
return _buildEmptyState();
return _buildEmptyState(context);
}
final displayActivities = activities.take(maxItems).toList();
@@ -142,7 +142,7 @@ class ConnectedRecentActivities extends StatelessWidget {
activity.userName,
style: AppTypography.subtitleSmall.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.primaryGreen,
color: AppColors.primary,
fontSize: 9,
),
),
@@ -157,11 +157,11 @@ class ConnectedRecentActivities extends StatelessWidget {
),
// Action button si disponible
if (activity.hasAction)
const Icon(
Icon(
Icons.chevron_right,
size: 14,
color: AppColors.textSecondaryLight,
),
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
],
),
),
@@ -189,25 +189,27 @@ class ConnectedRecentActivities extends StatelessWidget {
}
}
Widget _buildLoadingList() {
Widget _buildLoadingList(BuildContext context) {
return Column(
children: List.generate(3, (index) => Column(
children: [
_buildLoadingItem(),
_buildLoadingItem(context),
if (index < 2) const SizedBox(height: 12),
],
)),
);
}
Widget _buildLoadingItem() {
Widget _buildLoadingItem(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final borderColor = isDark ? AppColors.borderDark : AppColors.border;
return Row(
children: [
Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: AppColors.lightBorder,
color: borderColor,
borderRadius: BorderRadius.circular(14),
),
),
@@ -220,7 +222,7 @@ class ConnectedRecentActivities extends StatelessWidget {
height: 16,
width: double.infinity,
decoration: BoxDecoration(
color: AppColors.lightBorder,
color: borderColor,
borderRadius: BorderRadius.circular(4),
),
),
@@ -229,7 +231,7 @@ class ConnectedRecentActivities extends StatelessWidget {
height: 12,
width: 200,
decoration: BoxDecoration(
color: AppColors.lightBorder.withOpacity(0.5),
color: borderColor.withOpacity(0.5),
borderRadius: BorderRadius.circular(4),
),
),
@@ -238,7 +240,7 @@ class ConnectedRecentActivities extends StatelessWidget {
height: 12,
width: 120,
decoration: BoxDecoration(
color: AppColors.lightBorder.withOpacity(0.5),
color: borderColor.withOpacity(0.5),
borderRadius: BorderRadius.circular(4),
),
),
@@ -261,11 +263,12 @@ class ConnectedRecentActivities extends StatelessWidget {
);
}
Widget _buildEmptyState() {
Widget _buildEmptyState(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Center(
child: Column(
children: [
const Icon(Icons.history, color: AppColors.textSecondaryLight, size: 32),
Icon(Icons.history, color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary, size: 32),
const SizedBox(height: 8),
const Text('AUCUNE ACTIVITÉ', style: AppTypography.subtitleSmall),
Text('Les activités apparaîtront ici', style: AppTypography.subtitleSmall.copyWith(fontSize: 10)),
@@ -291,20 +294,20 @@ class ConnectedRecentActivities extends StatelessWidget {
}
}
Color _getActivityColor(String type) {
Color _getActivityColor(String type, {bool isDark = false}) {
switch (type.toLowerCase()) {
case 'member':
return AppColors.success;
case 'event':
return AppColors.info;
case 'contribution':
return AppColors.brandGreen;
return AppColors.primaryDark;
case 'organization':
return AppColors.primaryGreen;
return AppColors.primary;
case 'system':
return AppColors.warning;
default:
return AppColors.textSecondaryLight;
return isDark ? AppColors.textSecondaryDark : AppColors.textSecondary;
}
}
}

View File

@@ -30,14 +30,14 @@ class ConnectedUpcomingEvents extends StatelessWidget {
if (state is DashboardLoading) {
return _buildLoadingList();
} else if (state is DashboardLoaded || state is DashboardRefreshing) {
final data = state is DashboardLoaded
? state.dashboardData
final data = state is DashboardLoaded
? state.dashboardData
: (state as DashboardRefreshing).dashboardData;
return _buildEventsList(context, data.upcomingEvents);
} else if (state is DashboardError) {
return _buildErrorState(state.message);
}
return _buildEmptyState();
return _buildEmptyState(context);
},
),
],
@@ -50,7 +50,7 @@ class ConnectedUpcomingEvents extends StatelessWidget {
children: [
const Icon(
Icons.event_outlined,
color: AppColors.primaryGreen,
color: AppColors.primary,
size: 18,
),
const SizedBox(width: 8),
@@ -66,7 +66,7 @@ class ConnectedUpcomingEvents extends StatelessWidget {
child: Text(
'TOUT VOIR',
style: AppTypography.badgeText.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
),
),
@@ -77,7 +77,7 @@ class ConnectedUpcomingEvents extends StatelessWidget {
Widget _buildEventsList(BuildContext context, List<UpcomingEventEntity> events) {
if (events.isEmpty) {
return _buildEmptyState();
return _buildEmptyState(context);
}
final displayEvents = events.take(maxItems).toList();
@@ -99,7 +99,7 @@ class ConnectedUpcomingEvents extends StatelessWidget {
}
Widget _buildEventCard(BuildContext context, UpcomingEventEntity event) {
final statusColor = event.isToday ? AppColors.success : (event.isTomorrow ? AppColors.warning : AppColors.primaryGreen);
final statusColor = event.isToday ? AppColors.success : (event.isTomorrow ? AppColors.warning : AppColors.primary);
return CoreCard(
backgroundColor: Theme.of(context).cardColor,
@@ -140,7 +140,7 @@ class ConnectedUpcomingEvents extends StatelessWidget {
),
Row(
children: [
const Icon(Icons.location_on_outlined, size: 10, color: AppColors.textSecondaryLight),
Icon(Icons.location_on_outlined, size: 10, color: Theme.of(context).colorScheme.onSurfaceVariant),
const SizedBox(width: 4),
Expanded(
child: Text(
@@ -188,7 +188,7 @@ class ConnectedUpcomingEvents extends StatelessWidget {
child: LinearProgressIndicator(
value: event.fillPercentage,
minHeight: 4,
backgroundColor: AppColors.lightBorder,
backgroundColor: Theme.of(context).brightness == Brightness.dark ? AppColors.borderDark : AppColors.border,
valueColor: AlwaysStoppedAnimation<Color>(
event.isFull ? AppColors.error : (event.isAlmostFull ? AppColors.warning : AppColors.success),
),
@@ -230,11 +230,12 @@ class ConnectedUpcomingEvents extends StatelessWidget {
);
}
Widget _buildEmptyState() {
Widget _buildEmptyState(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Center(
child: Column(
children: [
const Icon(Icons.event_outlined, color: AppColors.textSecondaryLight, size: 32),
Icon(Icons.event_outlined, color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary, size: 32),
const SizedBox(height: 8),
const Text('AUCUN ÉVÉNEMENT', style: AppTypography.subtitleSmall),
Text('Les événements apparaîtront ici', style: AppTypography.subtitleSmall.copyWith(fontSize: 10)),

View File

@@ -37,7 +37,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
} else if (state is DashboardError) {
return _buildErrorNotifications();
}
return _buildEmptyNotifications();
return _buildEmptyNotifications(context);
},
),
],
@@ -49,7 +49,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.primaryGreen.withOpacity(0.05),
color: AppColors.primary.withOpacity(0.05),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
@@ -60,7 +60,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: AppColors.primaryGreen,
color: AppColors.primary,
borderRadius: BorderRadius.circular(4),
),
child: const Icon(
@@ -74,7 +74,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
child: Text(
'NOTIFICATIONS',
style: AppTypography.subtitleSmall.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
letterSpacing: 1.1,
),
@@ -119,25 +119,26 @@ class DashboardNotificationsWidget extends StatelessWidget {
Widget _buildNotifications(BuildContext context, DashboardEntity data) {
final notifications = _generateNotifications(context, data);
if (notifications.isEmpty) {
return _buildEmptyNotifications();
return _buildEmptyNotifications(context);
}
return Column(
children: notifications.take(maxNotifications).map((notification) {
return _buildNotificationItem(notification);
return _buildNotificationItem(context, notification);
}).toList(),
);
}
Widget _buildNotificationItem(DashboardNotification notification) {
Widget _buildNotificationItem(BuildContext context, DashboardNotification notification) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: AppColors.lightBorder,
color: isDark ? AppColors.borderDark : AppColors.border,
width: 1,
),
),
@@ -199,7 +200,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
Text(
notification.message,
style: AppTypography.subtitleSmall.copyWith(
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
fontSize: 10,
),
),
@@ -209,7 +210,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
Text(
notification.timeAgo,
style: AppTypography.subtitleSmall.copyWith(
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
fontSize: 9,
),
),
@@ -220,7 +221,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
child: Text(
notification.actionLabel!,
style: AppTypography.badgeText.copyWith(
color: AppColors.primaryGreen,
color: AppColors.primary,
fontWeight: FontWeight.bold,
fontSize: 9,
),
@@ -268,15 +269,16 @@ class DashboardNotificationsWidget extends StatelessWidget {
);
}
Widget _buildEmptyNotifications() {
Widget _buildEmptyNotifications(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Container(
padding: const EdgeInsets.all(24),
child: Center(
child: Column(
children: [
const Icon(
Icon(
Icons.notifications_none_outlined,
color: AppColors.textSecondaryLight,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary,
size: 24,
),
const SizedBox(height: 8),
@@ -359,7 +361,7 @@ class DashboardNotificationsWidget extends StatelessWidget {
title: 'Nouvelles activités',
message: '${data.recentActivitiesCount} activités récentes',
icon: Icons.fiber_new_outlined,
color: AppColors.brandGreen,
color: AppColors.primaryDark,
timeAgo: '15min',
isUrgent: false,
actionLabel: 'Voir',