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