feat(ui): RefreshIndicator + AlwaysScrollable + dark mode sur 14 pages

RefreshIndicator ajouté (dispatche les events BLoC appropriés) :
- adhesion_detail, adhesions_page, demande_aide_detail, demandes_aide_page
- event_detail, organization_detail, org_selector, org_types
- user_management_detail, reports (TabBarView), logs (Dashboard tab)
- profile (onglet Perso), backup (3 onglets), notifications

Fixes associés :
- AlwaysScrollableScrollPhysics sur tous les scroll widgets
  (permet pull-to-refresh même si contenu < écran)
- Empty states des listes : wrappés dans SingleChildScrollView pour refresh
- Dark mode adaptatif sur textes/surfaces/borders hardcodés
- backup_page : bouton retour ajouté dans le header gradient
- org_types : chevron/star/border adaptatifs
- reports : couleurs placeholders graphique + chevrons
This commit is contained in:
dahoud
2026-04-15 20:13:50 +00:00
parent f78892e5f6
commit 55f84da49a
14 changed files with 1565 additions and 1538 deletions

View File

@@ -4,6 +4,7 @@ import 'package:file_picker/file_picker.dart';
import 'package:share_plus/share_plus.dart';
import '../../../../shared/design_system/tokens/app_colors.dart';
import '../../../../shared/design_system/tokens/color_tokens.dart';
import '../../../../shared/design_system/tokens/module_colors.dart';
import '../../../../shared/design_system/tokens/spacing_tokens.dart';
import '../../../../core/di/injection_container.dart';
import '../../../../core/utils/logger.dart';
@@ -82,8 +83,10 @@ class _BackupPageState extends State<BackupPage>
},
builder: (context, state) {
return Scaffold(
backgroundColor: AppColors.lightBackground,
body: Column(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: SafeArea(
top: false,
child: Column(
children: [
_buildHeader(),
_buildTabBar(),
@@ -99,27 +102,28 @@ class _BackupPageState extends State<BackupPage>
),
],
),
),
);
},
),
);
}
/// Header harmonisé
/// Header harmonisé avec bouton retour
Widget _buildHeader() {
return Container(
margin: const EdgeInsets.symmetric(horizontal: SpacingTokens.sm, vertical: SpacingTokens.xs),
padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: ColorTokens.primaryGradient,
gradient: LinearGradient(
colors: ModuleColors.backupGradient,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(SpacingTokens.radiusXl),
boxShadow: [
BoxShadow(
color: ColorTokens.primary.withOpacity(0.3),
color: ModuleColors.backup.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 8),
),
@@ -129,6 +133,19 @@ class _BackupPageState extends State<BackupPage>
children: [
Row(
children: [
// Bouton retour
GestureDetector(
onTap: () => Navigator.of(context).maybePop(),
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.arrow_back_rounded, color: Colors.white, size: 18),
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
@@ -137,7 +154,7 @@ class _BackupPageState extends State<BackupPage>
),
child: const Icon(
Icons.backup,
color: Colors.white,
color: AppColors.onGradient,
size: 20,
),
),
@@ -151,14 +168,14 @@ class _BackupPageState extends State<BackupPage>
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
color: AppColors.onGradient,
),
),
Text(
'Gestion des sauvegardes système',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.8),
color: AppColors.onGradient.withOpacity(0.8),
),
),
],
@@ -173,7 +190,7 @@ class _BackupPageState extends State<BackupPage>
onPressed: () => _createBackupNow(),
icon: const Icon(
Icons.save,
color: Colors.white,
color: AppColors.onGradient,
),
tooltip: 'Sauvegarde immédiate',
),
@@ -275,21 +292,21 @@ class _BackupPageState extends State<BackupPage>
),
child: Column(
children: [
Icon(icon, color: Colors.white, size: 20),
Icon(icon, color: AppColors.onGradient, size: 20),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
color: AppColors.onGradient,
),
),
Text(
label,
style: TextStyle(
fontSize: 10,
color: Colors.white.withOpacity(0.8),
color: AppColors.onGradient.withOpacity(0.8),
),
textAlign: TextAlign.center,
),
@@ -303,11 +320,11 @@ class _BackupPageState extends State<BackupPage>
return Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: AppColors.shadow,
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -315,9 +332,9 @@ class _BackupPageState extends State<BackupPage>
),
child: TabBar(
controller: _tabController,
labelColor: AppColors.primaryGreen,
unselectedLabelColor: Colors.grey[600],
indicatorColor: AppColors.primaryGreen,
labelColor: AppColors.primary,
unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
indicatorColor: AppColors.primary,
indicatorWeight: 3,
labelStyle: const TextStyle(fontWeight: FontWeight.w600, fontSize: 12),
tabs: const [
@@ -331,16 +348,25 @@ class _BackupPageState extends State<BackupPage>
/// Onglet sauvegardes
Widget _buildBackupsTab(BackupState state) {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 8),
state is BackupLoading
? const Center(child: CircularProgressIndicator())
: _buildBackupsList(state is BackupsLoaded ? state.backups : (_cachedBackups ?? [])),
const SizedBox(height: 80),
],
return RefreshIndicator(
color: ModuleColors.backup,
onRefresh: () async {
context.read<BackupBloc>()
..add(LoadBackups())
..add(LoadBackupConfig());
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 8),
state is BackupLoading
? const Center(child: CircularProgressIndicator())
: _buildBackupsList(state is BackupsLoaded ? state.backups : (_cachedBackups ?? [])),
const SizedBox(height: 80),
],
),
),
);
}
@@ -358,11 +384,11 @@ class _BackupPageState extends State<BackupPage>
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: AppColors.shadow,
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -373,14 +399,14 @@ class _BackupPageState extends State<BackupPage>
children: [
Row(
children: [
const Icon(Icons.folder, color: AppColors.primaryGreen, size: 20),
const Icon(Icons.folder, color: AppColors.primary, size: 20),
const SizedBox(width: 8),
Text(
'Sauvegardes disponibles',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[800],
color: Theme.of(context).colorScheme.onSurface,
),
),
],
@@ -398,14 +424,14 @@ class _BackupPageState extends State<BackupPage>
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[50],
color: Theme.of(context).colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
backup['type'] == 'Auto' ? Icons.schedule : Icons.touch_app,
color: backup['type'] == 'Auto' ? AppColors.primaryGreen : AppColors.success,
color: backup['type'] == 'Auto' ? AppColors.primary : AppColors.success,
size: 20,
),
const SizedBox(width: 12),
@@ -415,17 +441,19 @@ class _BackupPageState extends State<BackupPage>
children: [
Text(
backup['name']!,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
color: Theme.of(context).brightness == Brightness.dark
? AppColors.textPrimaryDark
: AppColors.textPrimary,
),
),
Text(
'${backup['date']}${backup['size']}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
@@ -438,7 +466,12 @@ class _BackupPageState extends State<BackupPage>
const PopupMenuItem(value: 'download', child: Text('Télécharger')),
const PopupMenuItem(value: 'delete', child: Text('Supprimer')),
],
child: const Icon(Icons.more_vert, color: Colors.grey),
child: Icon(
Icons.more_vert,
color: Theme.of(context).brightness == Brightness.dark
? AppColors.textSecondaryDark
: AppColors.textTertiary,
),
),
],
),
@@ -447,14 +480,20 @@ class _BackupPageState extends State<BackupPage>
/// Onglet planification
Widget _buildScheduleTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
_buildScheduleSettings(),
const SizedBox(height: 80),
],
return RefreshIndicator(
color: ModuleColors.backup,
onRefresh: () async =>
context.read<BackupBloc>().add(LoadBackupConfig()),
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
_buildScheduleSettings(),
const SizedBox(height: 80),
],
),
),
);
}
@@ -464,11 +503,11 @@ class _BackupPageState extends State<BackupPage>
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: AppColors.shadow,
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -479,14 +518,14 @@ class _BackupPageState extends State<BackupPage>
children: [
Row(
children: [
const Icon(Icons.schedule, color: AppColors.primaryGreen, size: 20),
const Icon(Icons.schedule, color: AppColors.primary, size: 20),
const SizedBox(width: 8),
Text(
'Configuration automatique',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[800],
color: Theme.of(context).colorScheme.onSurface,
),
),
],
@@ -519,14 +558,20 @@ class _BackupPageState extends State<BackupPage>
/// Onglet restauration
Widget _buildRestoreTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
_buildRestoreOptions(),
const SizedBox(height: 80),
],
return RefreshIndicator(
color: ModuleColors.backup,
onRefresh: () async =>
context.read<BackupBloc>().add(LoadBackups()),
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(12),
child: Column(
children: [
const SizedBox(height: 16),
_buildRestoreOptions(),
const SizedBox(height: 80),
],
),
),
);
}
@@ -536,11 +581,11 @@ class _BackupPageState extends State<BackupPage>
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: AppColors.shadow,
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -551,14 +596,14 @@ class _BackupPageState extends State<BackupPage>
children: [
Row(
children: [
const Icon(Icons.restore, color: AppColors.primaryGreen, size: 20),
const Icon(Icons.restore, color: AppColors.primary, size: 20),
const SizedBox(width: 8),
Text(
'Options de restauration',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[800],
color: Theme.of(context).colorScheme.onSurface,
),
),
],
@@ -568,7 +613,7 @@ class _BackupPageState extends State<BackupPage>
'Restaurer depuis un fichier',
'Importer une sauvegarde externe',
Icons.file_upload,
AppColors.primaryGreen,
AppColors.primary,
() => _restoreFromFile(),
),
const SizedBox(height: 12),
@@ -601,11 +646,11 @@ class _BackupPageState extends State<BackupPage>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)),
Text(subtitle, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
Text(subtitle, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant)),
],
),
),
Switch(value: value, onChanged: onChanged, activeColor: AppColors.primaryGreen),
Switch(value: value, onChanged: onChanged, activeColor: AppColors.primary),
],
);
}
@@ -619,9 +664,9 @@ class _BackupPageState extends State<BackupPage>
Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: Colors.grey[50],
color: Theme.of(context).colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey[300]!),
border: Border.all(color: Theme.of(context).colorScheme.outline),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
@@ -656,11 +701,11 @@ class _BackupPageState extends State<BackupPage>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: color)),
Text(subtitle, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
Text(subtitle, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant)),
],
),
),
Icon(Icons.arrow_forward_ios, color: Colors.grey[400], size: 16),
Icon(Icons.arrow_forward_ios, color: Theme.of(context).colorScheme.onSurfaceVariant, size: 16),
],
),
),