Files
geo/app/lib/presentation/widgets/sector_distribution_card.dart
2025-06-04 16:51:40 +02:00

182 lines
5.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
class SectorDistributionCard extends StatelessWidget {
final String title;
final double? height;
final EdgeInsetsGeometry? padding;
const SectorDistributionCard({
Key? key,
this.title = 'Répartition par secteur',
this.height,
this.padding,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: height,
padding: padding ?? const EdgeInsets.all(AppTheme.spacingM),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: AppTheme.spacingM),
Expanded(
child: _buildAutoRefreshContent(),
),
],
),
);
}
Widget _buildAutoRefreshContent() {
// Écouter les changements des deux boîtes
return ValueListenableBuilder(
valueListenable: Hive.box<SectorModel>(AppKeys.sectorsBoxName).listenable(),
builder: (context, Box<SectorModel> sectorsBox, child) {
return ValueListenableBuilder(
valueListenable: Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
builder: (context, Box<PassageModel> passagesBox, child) {
return _buildContent(sectorsBox, passagesBox);
},
);
},
);
}
Widget _buildContent(Box<SectorModel> sectorsBox, Box<PassageModel> passagesBox) {
try {
// Calculer les statistiques
final sectorStats = _calculateSectorStats(sectorsBox, passagesBox);
if (sectorStats.isEmpty) {
return const Center(
child: Text('Aucune donnée de secteur disponible'),
);
}
return ListView.builder(
itemCount: sectorStats.length,
itemBuilder: (context, index) {
final sector = sectorStats[index];
return _buildSectorItem(
sector['name'],
sector['count'],
Color(sector['color']),
sectorStats,
);
},
);
} catch (e) {
debugPrint('Erreur lors du calcul des statistiques: $e');
return Center(
child: Text('Erreur: ${e.toString()}'),
);
}
}
List<Map<String, dynamic>> _calculateSectorStats(
Box<SectorModel> sectorsBox,
Box<PassageModel> passagesBox,
) {
// Récupérer tous les secteurs et passages
final List<SectorModel> sectors = sectorsBox.values.toList();
final List<PassageModel> passages = passagesBox.values.toList();
// Compter les passages par secteur (en excluant ceux où fkType==2)
final Map<int, int> sectorCounts = {};
for (final passage in passages) {
// Exclure les passages où fkType==2
if (passage.fkSector != null && passage.fkType != 2) {
sectorCounts[passage.fkSector!] =
(sectorCounts[passage.fkSector!] ?? 0) + 1;
}
}
// Préparer les données pour l'affichage
List<Map<String, dynamic>> stats = [];
for (final sector in sectors) {
final count = sectorCounts[sector.id] ?? 0;
if (count > 0) {
stats.add({
'name': sector.libelle,
'count': count,
'color': sector.color.isEmpty
? 0xFF4B77BE
: int.tryParse(sector.color.replaceAll('#', '0xFF')) ??
0xFF4B77BE,
});
}
}
// Trier par nombre de passages (décroissant)
stats.sort((a, b) => (b['count'] as int).compareTo(a['count'] as int));
return stats;
}
Widget _buildSectorItem(
String name,
int count,
Color color,
List<Map<String, dynamic>> allStats,
) {
final totalCount =
allStats.fold(0, (sum, item) => sum + (item['count'] as int));
final percentage = totalCount > 0 ? (count / totalCount) * 100 : 0;
return Padding(
padding: const EdgeInsets.only(bottom: AppTheme.spacingS),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
name,
style: const TextStyle(fontSize: 14),
overflow: TextOverflow.ellipsis,
),
),
Text(
'$count (${percentage.toInt()}%)',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
],
),
const SizedBox(height: 4),
LinearProgressIndicator(
value: percentage / 100,
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation<Color>(color),
minHeight: 8,
borderRadius: BorderRadius.circular(4),
),
],
),
);
}
}