Amélioration de la splash_page et du login
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
||||
import 'package:flutter/foundation.dart' show listEquals, mapEquals;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter/foundation.dart' show listEquals;
|
||||
import 'package:syncfusion_flutter_charts/charts.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
import 'package:geosector_app/core/repositories/passage_repository.dart';
|
||||
import 'package:geosector_app/core/repositories/user_repository.dart';
|
||||
import 'package:geosector_app/core/services/passage_data_service.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:geosector_app/core/data/models/passage_model.dart';
|
||||
|
||||
/// Modèle de données pour le graphique en camembert des passages
|
||||
class PassageChartData {
|
||||
@@ -38,7 +35,7 @@ class PassageChartData {
|
||||
/// Widget de graphique en camembert pour représenter la répartition des passages par type
|
||||
class PassagePieChart extends StatefulWidget {
|
||||
/// Liste des données de passages par type sous forme de Map avec typeId et count
|
||||
/// Si loadFromHive est true, ce paramètre est ignoré
|
||||
/// Si useValueListenable est true, ce paramètre est ignoré
|
||||
final Map<int, int> passagesByType;
|
||||
|
||||
/// Taille du graphique
|
||||
@@ -62,18 +59,21 @@ class PassagePieChart extends StatefulWidget {
|
||||
/// Rayon central pour le format donut (en pourcentage)
|
||||
final String innerRadius;
|
||||
|
||||
/// Charger les données depuis Hive
|
||||
/// Charger les données depuis Hive (obsolète, utiliser useValueListenable)
|
||||
final bool loadFromHive;
|
||||
|
||||
/// ID de l'utilisateur pour filtrer les passages (utilisé seulement si loadFromHive est true)
|
||||
/// ID de l'utilisateur pour filtrer les passages
|
||||
final int? userId;
|
||||
|
||||
/// Types de passages à exclure (utilisé seulement si loadFromHive est true)
|
||||
/// Types de passages à exclure
|
||||
final List<int> excludePassageTypes;
|
||||
|
||||
/// Afficher tous les passages sans filtrer par utilisateur (utilisé seulement si loadFromHive est true)
|
||||
/// Afficher tous les passages sans filtrer par utilisateur
|
||||
final bool showAllPassages;
|
||||
|
||||
/// Utiliser ValueListenableBuilder pour la mise à jour automatique
|
||||
final bool useValueListenable;
|
||||
|
||||
const PassagePieChart({
|
||||
super.key,
|
||||
this.passagesByType = const {},
|
||||
@@ -88,6 +88,7 @@ class PassagePieChart extends StatefulWidget {
|
||||
this.userId,
|
||||
this.excludePassageTypes = const [2],
|
||||
this.showAllPassages = false,
|
||||
this.useValueListenable = true,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -98,20 +99,9 @@ class _PassagePieChartState extends State<PassagePieChart>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
|
||||
/// Données de passages par type
|
||||
late Map<int, int> _passagesByType;
|
||||
|
||||
/// Variables pour la mise en cache et l'optimisation
|
||||
bool _dataLoaded = false;
|
||||
bool _isLoading = false;
|
||||
List<PassageChartData>? _cachedChartData;
|
||||
List<CircularChartAnnotation>? _cachedAnnotations;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_passagesByType = widget.passagesByType;
|
||||
|
||||
// Initialiser le contrôleur d'animation
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
@@ -119,235 +109,21 @@ class _PassagePieChartState extends State<PassagePieChart>
|
||||
);
|
||||
|
||||
_animationController.forward();
|
||||
|
||||
// Si nous n'utilisons pas Hive, préparer les données immédiatement
|
||||
if (!widget.loadFromHive) {
|
||||
_prepareChartData();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (widget.loadFromHive && !_dataLoaded && !_isLoading) {
|
||||
_isLoading = true; // Prévenir les chargements multiples
|
||||
_loadPassageDataFromHive(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(PassagePieChart oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
// Vérifier si les propriétés importantes ont changé
|
||||
final bool dataSourceChanged = widget.loadFromHive
|
||||
? false
|
||||
: !mapEquals(oldWidget.passagesByType, widget.passagesByType);
|
||||
final bool filteringChanged = oldWidget.userId != widget.userId ||
|
||||
!listEquals(
|
||||
oldWidget.excludePassageTypes, widget.excludePassageTypes) ||
|
||||
oldWidget.showAllPassages != widget.showAllPassages;
|
||||
final bool visualChanged = oldWidget.size != widget.size ||
|
||||
oldWidget.labelSize != widget.labelSize ||
|
||||
oldWidget.showPercentage != widget.showPercentage ||
|
||||
oldWidget.showIcons != widget.showIcons ||
|
||||
oldWidget.showLegend != widget.showLegend ||
|
||||
oldWidget.isDonut != widget.isDonut ||
|
||||
oldWidget.innerRadius != widget.innerRadius;
|
||||
// Relancer l'animation si les paramètres importants ont changé
|
||||
final bool shouldResetAnimation = oldWidget.userId != widget.userId ||
|
||||
!listEquals(oldWidget.excludePassageTypes, widget.excludePassageTypes) ||
|
||||
oldWidget.showAllPassages != widget.showAllPassages ||
|
||||
oldWidget.useValueListenable != widget.useValueListenable;
|
||||
|
||||
// Si les paramètres de filtrage ou de source de données ont changé, recharger les données
|
||||
if (dataSourceChanged || filteringChanged) {
|
||||
_cachedChartData = null;
|
||||
_cachedAnnotations = null;
|
||||
|
||||
// Relancer l'animation si les données ont changé
|
||||
if (shouldResetAnimation) {
|
||||
_animationController.reset();
|
||||
_animationController.forward();
|
||||
|
||||
if (!widget.loadFromHive) {
|
||||
_passagesByType = widget.passagesByType;
|
||||
_prepareChartData();
|
||||
} else if (!_isLoading) {
|
||||
_dataLoaded = false;
|
||||
_isLoading = true;
|
||||
_loadPassageDataFromHive(context);
|
||||
}
|
||||
}
|
||||
// Si seuls les paramètres visuels ont changé, recalculer les annotations sans recharger les données
|
||||
else if (visualChanged) {
|
||||
_cachedAnnotations = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Charge les données de passage depuis Hive en utilisant le service PassageDataService
|
||||
void _loadPassageDataFromHive(BuildContext context) {
|
||||
// Éviter les appels multiples pendant le chargement
|
||||
if (_isLoading) {
|
||||
debugPrint('PassagePieChart: Déjà en cours de chargement, ignoré');
|
||||
return;
|
||||
}
|
||||
|
||||
// Si les données sont déjà chargées et non vides, ne pas recharger
|
||||
if (_dataLoaded && _passagesByType.isNotEmpty) {
|
||||
debugPrint('PassagePieChart: Données déjà chargées, ignoré');
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrint('PassagePieChart: Début du chargement des données');
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
// Charger les données dans un addPostFrameCallback pour éviter les problèmes de cycle de vie
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// Vérifier si le widget est toujours monté
|
||||
if (!mounted) {
|
||||
debugPrint('PassagePieChart: Widget démonté, chargement annulé');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
debugPrint('PassagePieChart: Création du service de données');
|
||||
// Utiliser les instances globales définies dans app.dart
|
||||
|
||||
// Vérifier que les repositories sont disponibles
|
||||
if (passageRepository == null) {
|
||||
debugPrint('PassagePieChart: ERREUR - passageRepository est null');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (userRepository == null) {
|
||||
debugPrint('PassagePieChart: ERREUR - userRepository est null');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Créer une instance du service de données
|
||||
final passageDataService = PassageDataService(
|
||||
passageRepository: passageRepository,
|
||||
userRepository: userRepository,
|
||||
);
|
||||
|
||||
debugPrint(
|
||||
'PassagePieChart: Chargement des données avec excludePassageTypes=${widget.excludePassageTypes}, userId=${widget.userId}, showAllPassages=${widget.showAllPassages}');
|
||||
|
||||
// Utiliser le service pour charger les données
|
||||
final data = passageDataService.loadPassageDataForPieChart(
|
||||
excludePassageTypes: widget.excludePassageTypes,
|
||||
userId: widget.userId,
|
||||
showAllPassages: widget.showAllPassages,
|
||||
);
|
||||
|
||||
debugPrint('PassagePieChart: Données chargées: $data');
|
||||
|
||||
// Mettre à jour les données et les états
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_passagesByType = data;
|
||||
_dataLoaded = true;
|
||||
_isLoading = false;
|
||||
_cachedChartData =
|
||||
null; // Forcer la régénération des données du graphique
|
||||
_cachedAnnotations = null;
|
||||
});
|
||||
|
||||
// Préparer les données du graphique
|
||||
_prepareChartData();
|
||||
debugPrint('PassagePieChart: Données préparées pour le graphique');
|
||||
}
|
||||
} catch (e) {
|
||||
// Gérer les erreurs et réinitialiser l'état pour permettre une future tentative
|
||||
debugPrint(
|
||||
'PassagePieChart: ERREUR lors du chargement des données: $e');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Prépare les données pour le graphique en camembert avec mise en cache
|
||||
List<PassageChartData> _prepareChartData() {
|
||||
// Utiliser les données en cache si disponibles
|
||||
if (_cachedChartData != null) {
|
||||
debugPrint('PassagePieChart: Utilisation des données en cache');
|
||||
return _cachedChartData!;
|
||||
}
|
||||
|
||||
debugPrint('PassagePieChart: Préparation des données pour le graphique');
|
||||
debugPrint('PassagePieChart: Données brutes: $_passagesByType');
|
||||
|
||||
// Vérifier si les données sont vides
|
||||
if (_passagesByType.isEmpty) {
|
||||
debugPrint('PassagePieChart: Aucune donnée disponible');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Vérifier si les données contiennent uniquement des passages de type 2
|
||||
bool onlyType2 = true;
|
||||
_passagesByType.forEach((typeId, count) {
|
||||
if (typeId != 2 && count > 0) {
|
||||
onlyType2 = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (onlyType2) {
|
||||
debugPrint(
|
||||
'PassagePieChart: Les données contiennent uniquement des passages de type 2');
|
||||
}
|
||||
|
||||
final List<PassageChartData> chartData = [];
|
||||
|
||||
// Créer les données du graphique
|
||||
_passagesByType.forEach((typeId, count) {
|
||||
// Vérifier que le type existe et que le compteur est positif
|
||||
if (count > 0 && AppKeys.typesPassages.containsKey(typeId)) {
|
||||
// Vérifier si le type est exclu
|
||||
bool isExcluded = widget.excludePassageTypes.contains(typeId);
|
||||
if (isExcluded) {
|
||||
debugPrint('PassagePieChart: Type $typeId exclu');
|
||||
} else {
|
||||
final typeInfo = AppKeys.typesPassages[typeId]!;
|
||||
final typeName = typeInfo['titre'] as String;
|
||||
debugPrint(
|
||||
'PassagePieChart: Ajout du type $typeId ($typeName) avec $count passages');
|
||||
|
||||
chartData.add(PassageChartData(
|
||||
typeId: typeId,
|
||||
count: count,
|
||||
title: typeName,
|
||||
color: Color(typeInfo['couleur2'] as int),
|
||||
icon: typeInfo['icon_data'] as IconData,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if (count <= 0) {
|
||||
debugPrint('PassagePieChart: Type $typeId ignoré car count=$count');
|
||||
} else if (!AppKeys.typesPassages.containsKey(typeId)) {
|
||||
debugPrint(
|
||||
'PassagePieChart: Type $typeId ignoré car non défini dans AppKeys.typesPassages');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
debugPrint(
|
||||
'PassagePieChart: ${chartData.length} types de passages ajoutés au graphique');
|
||||
|
||||
// Mettre en cache les données générées
|
||||
_cachedChartData = chartData;
|
||||
|
||||
return chartData;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -358,19 +134,100 @@ class _PassagePieChartState extends State<PassagePieChart>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Si les données doivent être chargées depuis Hive mais ne sont pas encore prêtes
|
||||
if (widget.loadFromHive && !_dataLoaded) {
|
||||
return SizedBox(
|
||||
width: widget.size,
|
||||
height: widget.size,
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
if (widget.useValueListenable) {
|
||||
return _buildWithValueListenable();
|
||||
} else {
|
||||
return _buildWithStaticData();
|
||||
}
|
||||
}
|
||||
|
||||
final chartData = _prepareChartData();
|
||||
/// Construction du widget avec ValueListenableBuilder pour mise à jour automatique
|
||||
Widget _buildWithValueListenable() {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
|
||||
builder: (context, Box<PassageModel> passagesBox, child) {
|
||||
final chartData = _calculatePassageData(passagesBox);
|
||||
return _buildChart(chartData);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Construction du widget avec des données statiques (ancien système)
|
||||
Widget _buildWithStaticData() {
|
||||
final chartData = _prepareChartDataFromMap(widget.passagesByType);
|
||||
return _buildChart(chartData);
|
||||
}
|
||||
|
||||
/// Calcule les données de passage depuis la Hive box
|
||||
List<PassageChartData> _calculatePassageData(Box<PassageModel> passagesBox) {
|
||||
try {
|
||||
final passages = passagesBox.values.toList();
|
||||
final currentUser = userRepository.getCurrentUser();
|
||||
|
||||
// Calculer les données selon les filtres
|
||||
final Map<int, int> passagesByType = {};
|
||||
|
||||
// Initialiser tous les types de passage possibles
|
||||
for (final typeId in AppKeys.typesPassages.keys) {
|
||||
if (!widget.excludePassageTypes.contains(typeId)) {
|
||||
passagesByType[typeId] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (final passage in passages) {
|
||||
// Appliquer les filtres
|
||||
bool shouldInclude = true;
|
||||
|
||||
// Filtrer par utilisateur si nécessaire
|
||||
if (!widget.showAllPassages && widget.userId != null) {
|
||||
shouldInclude = passage.fkUser == widget.userId;
|
||||
} else if (!widget.showAllPassages && currentUser != null) {
|
||||
shouldInclude = passage.fkUser == currentUser.id;
|
||||
}
|
||||
|
||||
// Exclure certains types
|
||||
if (widget.excludePassageTypes.contains(passage.fkType)) {
|
||||
shouldInclude = false;
|
||||
}
|
||||
|
||||
if (shouldInclude) {
|
||||
passagesByType[passage.fkType] =
|
||||
(passagesByType[passage.fkType] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return _prepareChartDataFromMap(passagesByType);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du calcul des données de passage: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Prépare les données pour le graphique en camembert à partir d'une Map
|
||||
List<PassageChartData> _prepareChartDataFromMap(Map<int, int> passagesByType) {
|
||||
final List<PassageChartData> chartData = [];
|
||||
|
||||
// Créer les données du graphique
|
||||
passagesByType.forEach((typeId, count) {
|
||||
// Vérifier que le type existe et que le compteur est positif
|
||||
if (count > 0 && AppKeys.typesPassages.containsKey(typeId)) {
|
||||
final typeInfo = AppKeys.typesPassages[typeId]!;
|
||||
|
||||
chartData.add(PassageChartData(
|
||||
typeId: typeId,
|
||||
count: count,
|
||||
title: typeInfo['titre'] as String,
|
||||
color: Color(typeInfo['couleur2'] as int),
|
||||
icon: typeInfo['icon_data'] as IconData,
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
return chartData;
|
||||
}
|
||||
|
||||
/// Construit le graphique avec les données fournies
|
||||
Widget _buildChart(List<PassageChartData> chartData) {
|
||||
// Si aucune donnée, afficher un message
|
||||
if (chartData.isEmpty) {
|
||||
return SizedBox(
|
||||
@@ -448,8 +305,7 @@ class _PassagePieChartState extends State<PassagePieChart>
|
||||
explodeOffset:
|
||||
'${(5 * explodeAnimation.value).toStringAsFixed(1)}%',
|
||||
opacity: opacityAnimation.value,
|
||||
animationDuration:
|
||||
0, // On désactive l'animation intégrée car nous utilisons notre propre animation
|
||||
animationDuration: 0,
|
||||
startAngle: 270,
|
||||
endAngle: 270 + (360 * progressAnimation.value).toInt(),
|
||||
)
|
||||
@@ -485,8 +341,7 @@ class _PassagePieChartState extends State<PassagePieChart>
|
||||
explodeOffset:
|
||||
'${(5 * explodeAnimation.value).toStringAsFixed(1)}%',
|
||||
opacity: opacityAnimation.value,
|
||||
animationDuration:
|
||||
0, // On désactive l'animation intégrée car nous utilisons notre propre animation
|
||||
animationDuration: 0,
|
||||
startAngle: 270,
|
||||
endAngle: 270 + (360 * progressAnimation.value).toInt(),
|
||||
),
|
||||
@@ -499,14 +354,9 @@ class _PassagePieChartState extends State<PassagePieChart>
|
||||
);
|
||||
}
|
||||
|
||||
/// Crée les annotations d'icônes pour le graphique avec mise en cache
|
||||
/// Crée les annotations d'icônes pour le graphique
|
||||
List<CircularChartAnnotation> _buildIconAnnotations(
|
||||
List<PassageChartData> chartData) {
|
||||
// Utiliser les annotations en cache si disponibles
|
||||
if (_cachedAnnotations != null) {
|
||||
return _cachedAnnotations!;
|
||||
}
|
||||
|
||||
final List<CircularChartAnnotation> annotations = [];
|
||||
|
||||
// Calculer le total pour les pourcentages
|
||||
@@ -541,9 +391,6 @@ class _PassagePieChartState extends State<PassagePieChart>
|
||||
currentAngle += segmentAngle;
|
||||
}
|
||||
|
||||
// Mettre en cache les annotations générées
|
||||
_cachedAnnotations = annotations;
|
||||
|
||||
return annotations;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user