refactor: Simplifier DashboardLayout/AppScaffold (tâche #74)
Centralisation et simplification de l'architecture de navigation : CRÉATIONS : - navigation_config.dart : Configuration centralisée de la navigation * Toutes les destinations (admin/user) * Logique de navigation (index → route) * Résolution inverse (route → index) * Titres et utilitaires - backgrounds/dots_painter.dart : Painter de points décoratifs * Extrait depuis AppScaffold et AdminScaffold * Paramétrable (opacité, densité, seed) * Réutilisable - backgrounds/gradient_background.dart : Fond dégradé * Gère les couleurs admin (rouge) / user (vert) * Option pour afficher/masquer les points * Widget indépendant SIMPLIFICATIONS : - app_scaffold.dart : 426 → 192 lignes (-55%) * Utilise NavigationConfig au lieu de NavigationHelper * Utilise GradientBackground au lieu de code dupliqué * Suppression de DotsPainter local - dashboard_layout.dart : 140 → 77 lignes (-45%) * Suppression validations excessives (try/catch, vérifications) * Code épuré et plus lisible SUPPRESSIONS : - admin_scaffold.dart : Supprimé (207 lignes) * Obsolète depuis unification avec AppScaffold * Code dupliqué avec AppScaffold * AdminNavigationHelper fusionné dans NavigationConfig RÉSULTATS : - Avant : 773 lignes (AppScaffold + AdminScaffold + DashboardLayout) - Après : 623 lignes (tout inclus) - Réduction nette : -150 lignes (-19%) - Architecture plus claire et maintenable - Aucune duplication de code - Navigation centralisée en un seul endroit Résout tâche #74 du PLANNING-2026-Q1.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,207 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:geosector_app/presentation/widgets/dashboard_layout.dart';
|
||||
import 'package:geosector_app/presentation/widgets/badged_navigation_destination.dart';
|
||||
import 'package:geosector_app/app.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
/// Class pour dessiner les petits points blancs sur le fond
|
||||
class DotsPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = Colors.white.withOpacity(0.5)
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
final random = math.Random(42); // Seed fixe pour consistance
|
||||
final numberOfDots = (size.width * size.height) ~/ 1500;
|
||||
|
||||
for (int i = 0; i < numberOfDots; i++) {
|
||||
final x = random.nextDouble() * size.width;
|
||||
final y = random.nextDouble() * size.height;
|
||||
final radius = 1.0 + random.nextDouble() * 2.0;
|
||||
canvas.drawCircle(Offset(x, y), radius, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
/// Scaffold partagé pour toutes les pages d'administration
|
||||
/// Fournit le fond dégradé et la navigation commune
|
||||
class AdminScaffold extends StatelessWidget {
|
||||
/// Le contenu de la page
|
||||
final Widget body;
|
||||
|
||||
/// L'index de navigation sélectionné
|
||||
final int selectedIndex;
|
||||
|
||||
/// Le titre de la page
|
||||
final String pageTitle;
|
||||
|
||||
/// Callback optionnel pour gérer la navigation personnalisée
|
||||
final Function(int)? onDestinationSelected;
|
||||
|
||||
const AdminScaffold({
|
||||
super.key,
|
||||
required this.body,
|
||||
required this.selectedIndex,
|
||||
required this.pageTitle,
|
||||
this.onDestinationSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentUser = userRepository.getCurrentUser();
|
||||
final size = MediaQuery.of(context).size;
|
||||
final isMobile = size.width <= 900;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
// Fond dégradé avec petits points blancs
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Colors.white, Colors.red.shade300],
|
||||
),
|
||||
),
|
||||
child: CustomPaint(
|
||||
painter: DotsPainter(),
|
||||
child: const SizedBox(width: double.infinity, height: double.infinity),
|
||||
),
|
||||
),
|
||||
|
||||
// Contenu de la page avec navigation
|
||||
DashboardLayout(
|
||||
key: ValueKey('dashboard_layout_$selectedIndex'),
|
||||
title: 'Tableau de bord Administration',
|
||||
selectedIndex: selectedIndex,
|
||||
onDestinationSelected: onDestinationSelected ?? (index) {
|
||||
// Navigation par défaut si pas de callback personnalisé
|
||||
AdminNavigationHelper.navigateToIndex(context, index);
|
||||
},
|
||||
destinations: AdminNavigationHelper.getDestinations(
|
||||
currentUser: currentUser,
|
||||
isMobile: isMobile,
|
||||
),
|
||||
isAdmin: true,
|
||||
body: body,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper pour centraliser la logique de navigation admin
|
||||
class AdminNavigationHelper {
|
||||
/// Obtenir la liste des destinations de navigation selon le rôle et le device
|
||||
static List<NavigationDestination> getDestinations({
|
||||
required dynamic currentUser,
|
||||
required bool isMobile,
|
||||
}) {
|
||||
final destinations = <NavigationDestination>[
|
||||
// Pages de base toujours visibles
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.dashboard_outlined),
|
||||
selectedIcon: Icon(Icons.dashboard),
|
||||
label: 'Tableau de bord',
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.bar_chart_outlined),
|
||||
selectedIcon: Icon(Icons.bar_chart),
|
||||
label: 'Statistiques',
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.history_outlined),
|
||||
selectedIcon: Icon(Icons.history),
|
||||
label: 'Historique',
|
||||
),
|
||||
createBadgedNavigationDestination(
|
||||
icon: const Icon(Icons.chat_outlined),
|
||||
selectedIcon: const Icon(Icons.chat),
|
||||
label: 'Messages',
|
||||
showBadge: true,
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.map_outlined),
|
||||
selectedIcon: Icon(Icons.map),
|
||||
label: 'Carte',
|
||||
),
|
||||
];
|
||||
|
||||
// Ajouter les pages admin (role 2) seulement sur desktop
|
||||
if (currentUser?.role == 2 && !isMobile) {
|
||||
destinations.addAll([
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.business_outlined),
|
||||
selectedIcon: Icon(Icons.business),
|
||||
label: 'Amicale & membres',
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.calendar_today_outlined),
|
||||
selectedIcon: Icon(Icons.calendar_today),
|
||||
label: 'Opérations',
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
return destinations;
|
||||
}
|
||||
|
||||
/// Naviguer vers une page selon l'index
|
||||
static void navigateToIndex(BuildContext context, int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
context.go('/admin');
|
||||
break;
|
||||
case 1:
|
||||
context.go('/admin/statistics');
|
||||
break;
|
||||
case 2:
|
||||
context.go('/admin/history');
|
||||
break;
|
||||
case 3:
|
||||
context.go('/admin/messages');
|
||||
break;
|
||||
case 4:
|
||||
context.go('/admin/map');
|
||||
break;
|
||||
case 5:
|
||||
context.go('/admin/amicale');
|
||||
break;
|
||||
case 6:
|
||||
context.go('/admin/operations');
|
||||
break;
|
||||
default:
|
||||
context.go('/admin');
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtenir l'index selon la route actuelle
|
||||
static int getIndexFromRoute(String route) {
|
||||
if (route.contains('/statistics')) return 1;
|
||||
if (route.contains('/history')) return 2;
|
||||
if (route.contains('/messages')) return 3;
|
||||
if (route.contains('/map')) return 4;
|
||||
if (route.contains('/amicale')) return 5;
|
||||
if (route.contains('/operations')) return 6;
|
||||
return 0; // Dashboard par défaut
|
||||
}
|
||||
|
||||
/// Obtenir le nom de la page selon l'index
|
||||
static String getPageNameFromIndex(int index) {
|
||||
switch (index) {
|
||||
case 0: return 'dashboard';
|
||||
case 1: return 'statistics';
|
||||
case 2: return 'history';
|
||||
case 3: return 'messages';
|
||||
case 4: return 'map';
|
||||
case 5: return 'amicale';
|
||||
case 6: return 'operations';
|
||||
default: return 'dashboard';
|
||||
}
|
||||
}
|
||||
}
|
||||
265
app/lib/presentation/widgets/app_scaffold.dart
Normal file → Executable file
265
app/lib/presentation/widgets/app_scaffold.dart
Normal file → Executable file
@@ -1,33 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:geosector_app/presentation/widgets/dashboard_layout.dart';
|
||||
import 'package:geosector_app/presentation/widgets/badged_navigation_destination.dart';
|
||||
import 'package:geosector_app/presentation/widgets/backgrounds/gradient_background.dart';
|
||||
import 'package:geosector_app/core/config/navigation_config.dart';
|
||||
import 'package:geosector_app/core/services/current_user_service.dart';
|
||||
import 'package:geosector_app/app.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
/// Classe pour dessiner les petits points blancs sur le fond
|
||||
class DotsPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = Colors.white.withOpacity(0.5)
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
final random = math.Random(42); // Seed fixe pour consistance
|
||||
final numberOfDots = (size.width * size.height) ~/ 1500;
|
||||
|
||||
for (int i = 0; i < numberOfDots; i++) {
|
||||
final x = random.nextDouble() * size.width;
|
||||
final y = random.nextDouble() * size.height;
|
||||
final radius = 1.0 + random.nextDouble() * 2.0;
|
||||
canvas.drawCircle(Offset(x, y), radius, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
/// Scaffold unifié pour toutes les pages (admin et user)
|
||||
/// Adapte automatiquement son apparence selon le rôle de l'utilisateur
|
||||
@@ -102,43 +78,26 @@ class AppScaffold extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// Couleurs de fond selon le rôle
|
||||
final gradientColors = isAdmin
|
||||
? [Colors.white, Colors.red.shade300] // Admin: dégradé rouge
|
||||
: [Colors.white, Colors.green.shade300]; // User: dégradé vert
|
||||
|
||||
// Titre avec suffixe selon le rôle
|
||||
final dashboardTitle = isAdmin
|
||||
? 'Tableau de bord Administration'
|
||||
: 'GEOSECTOR';
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
// Fond dégradé avec petits points blancs (optionnel)
|
||||
if (showBackground)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: gradientColors,
|
||||
),
|
||||
),
|
||||
child: CustomPaint(
|
||||
painter: DotsPainter(),
|
||||
child: const SizedBox(width: double.infinity, height: double.infinity),
|
||||
),
|
||||
GradientBackground(
|
||||
isAdmin: isAdmin,
|
||||
showDots: true,
|
||||
),
|
||||
|
||||
// Contenu de la page avec navigation
|
||||
DashboardLayout(
|
||||
key: ValueKey('dashboard_layout_${isAdmin ? 'admin' : 'user'}_$selectedIndex'),
|
||||
title: dashboardTitle,
|
||||
key: ValueKey(
|
||||
'dashboard_layout_${isAdmin ? 'admin' : 'user'}_$selectedIndex'),
|
||||
title: NavigationConfig.getDashboardTitle(isAdmin),
|
||||
selectedIndex: selectedIndex,
|
||||
onDestinationSelected: onDestinationSelected ?? (index) {
|
||||
NavigationHelper.navigateToIndex(context, index, isAdmin);
|
||||
},
|
||||
destinations: NavigationHelper.getDestinations(
|
||||
onDestinationSelected: onDestinationSelected ??
|
||||
(index) {
|
||||
NavigationConfig.navigateToIndex(context, index, isAdmin);
|
||||
},
|
||||
destinations: NavigationConfig.getDestinations(
|
||||
isAdmin: isAdmin,
|
||||
isMobile: isMobile,
|
||||
),
|
||||
@@ -159,27 +118,13 @@ class AppScaffold extends StatelessWidget {
|
||||
}) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
// Utiliser le même fond que pour un utilisateur normal (vert)
|
||||
final gradientColors = isAdmin
|
||||
? [Colors.white, Colors.red.shade300]
|
||||
: [Colors.white, Colors.green.shade300];
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
// Fond dégradé (optionnel)
|
||||
if (showBackground)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: gradientColors,
|
||||
),
|
||||
),
|
||||
child: CustomPaint(
|
||||
painter: DotsPainter(),
|
||||
child: const SizedBox(width: double.infinity, height: double.infinity),
|
||||
),
|
||||
GradientBackground(
|
||||
isAdmin: isAdmin,
|
||||
showDots: true,
|
||||
),
|
||||
|
||||
// Message d'accès restreint
|
||||
@@ -245,182 +190,4 @@ class AppScaffold extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper centralisé pour la navigation
|
||||
class NavigationHelper {
|
||||
/// Obtenir la liste des destinations selon le mode d'affichage et le device
|
||||
static List<NavigationDestination> getDestinations({
|
||||
required bool isAdmin,
|
||||
required bool isMobile,
|
||||
}) {
|
||||
final destinations = <NavigationDestination>[];
|
||||
|
||||
// Pages communes à tous les rôles
|
||||
destinations.addAll([
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.dashboard_outlined),
|
||||
selectedIcon: Icon(Icons.dashboard),
|
||||
label: 'Tableau de bord',
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.history_outlined),
|
||||
selectedIcon: Icon(Icons.history),
|
||||
label: 'Historique',
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.map_outlined),
|
||||
selectedIcon: Icon(Icons.map),
|
||||
label: 'Carte',
|
||||
),
|
||||
createBadgedNavigationDestination(
|
||||
icon: const Icon(Icons.chat_outlined),
|
||||
selectedIcon: const Icon(Icons.chat),
|
||||
label: 'Messages',
|
||||
showBadge: true,
|
||||
),
|
||||
]);
|
||||
|
||||
// Pages spécifiques aux utilisateurs standards
|
||||
if (!isAdmin) {
|
||||
destinations.add(
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.explore_outlined),
|
||||
selectedIcon: Icon(Icons.explore),
|
||||
label: 'Terrain',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Pages spécifiques aux admins (seulement sur desktop)
|
||||
if (isAdmin && !isMobile) {
|
||||
destinations.addAll([
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.business_outlined),
|
||||
selectedIcon: Icon(Icons.business),
|
||||
label: 'Amicale & membres',
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.calendar_today_outlined),
|
||||
selectedIcon: Icon(Icons.calendar_today),
|
||||
label: 'Opérations',
|
||||
),
|
||||
const NavigationDestination(
|
||||
icon: Icon(Icons.analytics_outlined),
|
||||
selectedIcon: Icon(Icons.analytics),
|
||||
label: 'Connexions',
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
return destinations;
|
||||
}
|
||||
|
||||
/// Naviguer vers une page selon l'index et le rôle
|
||||
static void navigateToIndex(BuildContext context, int index, bool isAdmin) {
|
||||
if (isAdmin) {
|
||||
_navigateAdminIndex(context, index);
|
||||
} else {
|
||||
_navigateUserIndex(context, index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Navigation pour les admins
|
||||
static void _navigateAdminIndex(BuildContext context, int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
context.go('/admin');
|
||||
break;
|
||||
case 1:
|
||||
context.go('/admin/history');
|
||||
break;
|
||||
case 2:
|
||||
context.go('/admin/map');
|
||||
break;
|
||||
case 3:
|
||||
context.go('/admin/messages');
|
||||
break;
|
||||
case 4:
|
||||
context.go('/admin/amicale');
|
||||
break;
|
||||
case 5:
|
||||
context.go('/admin/operations');
|
||||
break;
|
||||
case 6:
|
||||
context.go('/admin/connexions');
|
||||
break;
|
||||
default:
|
||||
context.go('/admin');
|
||||
}
|
||||
}
|
||||
|
||||
/// Navigation pour les utilisateurs standards
|
||||
static void _navigateUserIndex(BuildContext context, int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
context.go('/user/dashboard');
|
||||
break;
|
||||
case 1:
|
||||
context.go('/user/history');
|
||||
break;
|
||||
case 2:
|
||||
context.go('/user/map');
|
||||
break;
|
||||
case 3:
|
||||
context.go('/user/messages');
|
||||
break;
|
||||
case 4:
|
||||
context.go('/user/field-mode');
|
||||
break;
|
||||
default:
|
||||
context.go('/user/dashboard');
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtenir l'index selon la route actuelle et le rôle
|
||||
static int getIndexFromRoute(String route, bool isAdmin) {
|
||||
// Enlever les paramètres de query si présents
|
||||
final cleanRoute = route.split('?').first;
|
||||
|
||||
if (isAdmin) {
|
||||
if (cleanRoute.contains('/admin/history')) return 1;
|
||||
if (cleanRoute.contains('/admin/map')) return 2;
|
||||
if (cleanRoute.contains('/admin/messages')) return 3;
|
||||
if (cleanRoute.contains('/admin/amicale')) return 4;
|
||||
if (cleanRoute.contains('/admin/operations')) return 5;
|
||||
if (cleanRoute.contains('/admin/connexions')) return 6;
|
||||
return 0; // Dashboard par défaut
|
||||
} else {
|
||||
if (cleanRoute.contains('/user/history')) return 1;
|
||||
if (cleanRoute.contains('/user/map')) return 2;
|
||||
if (cleanRoute.contains('/user/messages')) return 3;
|
||||
if (cleanRoute.contains('/user/field-mode')) return 4;
|
||||
return 0; // Dashboard par défaut
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtenir le nom de la page selon l'index et le rôle
|
||||
static String getPageNameFromIndex(int index, bool isAdmin) {
|
||||
if (isAdmin) {
|
||||
switch (index) {
|
||||
case 0: return 'dashboard';
|
||||
case 1: return 'history';
|
||||
case 2: return 'map';
|
||||
case 3: return 'messages';
|
||||
case 4: return 'amicale';
|
||||
case 5: return 'operations';
|
||||
case 6: return 'connexions';
|
||||
default: return 'dashboard';
|
||||
}
|
||||
} else {
|
||||
switch (index) {
|
||||
case 0: return 'dashboard';
|
||||
case 1: return 'history';
|
||||
case 2: return 'map';
|
||||
case 3: return 'messages';
|
||||
case 4: return 'field-mode';
|
||||
default: return 'dashboard';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
app/lib/presentation/widgets/backgrounds/dots_painter.dart
Executable file
46
app/lib/presentation/widgets/backgrounds/dots_painter.dart
Executable file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
/// Painter pour dessiner des petits points blancs semi-transparents sur un fond
|
||||
/// Utilisé pour créer un effet visuel subtil sur les fonds dégradés
|
||||
class DotsPainter extends CustomPainter {
|
||||
/// Opacité des points (0.0 à 1.0)
|
||||
final double opacity;
|
||||
|
||||
/// Seed pour le générateur aléatoire (pour consistance du pattern)
|
||||
final int seed;
|
||||
|
||||
/// Densité des points (nombre de pixels par point)
|
||||
/// Plus la valeur est élevée, moins il y a de points
|
||||
final int density;
|
||||
|
||||
const DotsPainter({
|
||||
this.opacity = 0.5,
|
||||
this.seed = 42,
|
||||
this.density = 1500,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = Colors.white.withOpacity(opacity)
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
final random = math.Random(seed);
|
||||
final numberOfDots = (size.width * size.height) ~/ density;
|
||||
|
||||
for (int i = 0; i < numberOfDots; i++) {
|
||||
final x = random.nextDouble() * size.width;
|
||||
final y = random.nextDouble() * size.height;
|
||||
final radius = 1.0 + random.nextDouble() * 2.0;
|
||||
canvas.drawCircle(Offset(x, y), radius, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant DotsPainter oldDelegate) {
|
||||
return oldDelegate.opacity != opacity ||
|
||||
oldDelegate.seed != seed ||
|
||||
oldDelegate.density != density;
|
||||
}
|
||||
}
|
||||
49
app/lib/presentation/widgets/backgrounds/gradient_background.dart
Executable file
49
app/lib/presentation/widgets/backgrounds/gradient_background.dart
Executable file
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geosector_app/presentation/widgets/backgrounds/dots_painter.dart';
|
||||
|
||||
/// Widget de fond dégradé avec points décoratifs
|
||||
/// Utilisé comme fond pour les pages admin et user avec des couleurs différentes
|
||||
class GradientBackground extends StatelessWidget {
|
||||
/// Indique si le fond est pour un admin (rouge) ou user (vert)
|
||||
final bool isAdmin;
|
||||
|
||||
/// Afficher ou non les points décoratifs
|
||||
final bool showDots;
|
||||
|
||||
/// Opacité des points (0.0 à 1.0)
|
||||
final double dotsOpacity;
|
||||
|
||||
const GradientBackground({
|
||||
super.key,
|
||||
required this.isAdmin,
|
||||
this.showDots = true,
|
||||
this.dotsOpacity = 0.5,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Couleurs de fond selon le rôle
|
||||
final gradientColors = isAdmin
|
||||
? [Colors.white, Colors.red.shade300] // Admin: dégradé rouge
|
||||
: [Colors.white, Colors.green.shade300]; // User: dégradé vert
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: gradientColors,
|
||||
),
|
||||
),
|
||||
child: showDots
|
||||
? CustomPaint(
|
||||
painter: DotsPainter(opacity: dotsOpacity),
|
||||
child: const SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -48,92 +48,30 @@ class DashboardLayout extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
try {
|
||||
debugPrint('Building DashboardLayout');
|
||||
|
||||
// Vérifier que les destinations ne sont pas vides
|
||||
if (destinations.isEmpty) {
|
||||
debugPrint('ERREUR: destinations est vide dans DashboardLayout');
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: Text('Erreur: Aucune destination de navigation disponible'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier que selectedIndex est valide
|
||||
if (selectedIndex < 0 || selectedIndex >= destinations.length) {
|
||||
debugPrint('ERREUR: selectedIndex invalide dans DashboardLayout');
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child:
|
||||
Text('Erreur: Index de navigation invalide ($selectedIndex)'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Scaffold avec fond transparent (le fond est géré par AppScaffold)
|
||||
return Scaffold(
|
||||
key: ValueKey('dashboard_scaffold_$selectedIndex'),
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: DashboardAppBar(
|
||||
key: ValueKey('dashboard_appbar_$selectedIndex'),
|
||||
title: title,
|
||||
pageTitle: destinations[selectedIndex].label,
|
||||
isAdmin: isAdmin,
|
||||
onLogoutPressed: onLogoutPressed,
|
||||
),
|
||||
body: ResponsiveNavigation(
|
||||
key: ValueKey('responsive_nav_$selectedIndex'),
|
||||
title:
|
||||
title, // Même si le titre n'est pas affiché dans la navigation, il est utilisé pour la cohérence
|
||||
body: body,
|
||||
selectedIndex: selectedIndex,
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
destinations: destinations,
|
||||
// Ne pas afficher le bouton "Nouveau passage" dans la navigation
|
||||
showNewPassageButton: false,
|
||||
onNewPassagePressed: null,
|
||||
sidebarBottomItems: sidebarBottomItems,
|
||||
isAdmin: isAdmin,
|
||||
// Ne pas afficher l'AppBar dans la navigation car nous utilisons DashboardAppBar
|
||||
showAppBar: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('ERREUR CRITIQUE dans DashboardLayout.build: $e');
|
||||
// Afficher une interface de secours en cas d'erreur
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Erreur - $title'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error_outline, color: Colors.red, size: 64),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Une erreur est survenue',
|
||||
style: TextStyle(
|
||||
fontSize: AppTheme.r(context, 20),
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text('Détails: $e'),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pushNamedAndRemoveUntil('/', (route) => false);
|
||||
},
|
||||
child: const Text('Retour à l\'accueil'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// Scaffold avec fond transparent (le fond est géré par AppScaffold)
|
||||
return Scaffold(
|
||||
key: ValueKey('dashboard_scaffold_$selectedIndex'),
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: DashboardAppBar(
|
||||
key: ValueKey('dashboard_appbar_$selectedIndex'),
|
||||
title: title,
|
||||
pageTitle: destinations[selectedIndex].label,
|
||||
isAdmin: isAdmin,
|
||||
onLogoutPressed: onLogoutPressed,
|
||||
),
|
||||
body: ResponsiveNavigation(
|
||||
key: ValueKey('responsive_nav_$selectedIndex'),
|
||||
title: title,
|
||||
body: body,
|
||||
selectedIndex: selectedIndex,
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
destinations: destinations,
|
||||
showNewPassageButton: false,
|
||||
onNewPassagePressed: null,
|
||||
sidebarBottomItems: sidebarBottomItems,
|
||||
isAdmin: isAdmin,
|
||||
showAppBar: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user