Projet
import 'package:flutter/material.dart'; import 'dart:math' as math;
class AnalysesPage extends StatefulWidget { const AnalysesPage({super.key});
@override State createState() => _AnalysesPageState(); }
class _AnalysesPageState extends State with TickerProviderStateMixin { late AnimationController _gradientController; late AnimationController _cardAnimationController; late AnimationController _chartAnimationController; late AnimationController _tableAnimationController; late AnimationController _kpiAnimationController;
late Animation _cardSlideAnimation; late Animation _chartScaleAnimation; late Animation _tableOpacityAnimation; late Animation _kpiCounterAnimation;
String _selectedPeriod = 'Année en cours'; String _selectedAnalysisType = 'Vue d'ensemble'; String _selectedSector = '';
final List _periods = ['Année en cours', 'Trimestre', 'Semestre', 'Législature']; final List _analysisTypes = ['Vue d'ensemble', 'Efficacité législative', 'Participation', 'Thématiques', 'Tendances'];
@override void initState() { super.initState();
_gradientController = AnimationController(
duration: const Duration(seconds: 8),
vsync: this,
)..repeat();
_cardAnimationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_chartAnimationController = AnimationController(
duration: const Duration(milliseconds: 1800),
vsync: this,
);
_tableAnimationController = AnimationController(
duration: const Duration(milliseconds: 900),
vsync: this,
);
_kpiAnimationController = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
_setupAnimations();
WidgetsBinding.instance.addPostFrameCallback((_) {
_cardAnimationController.forward();
_chartAnimationController.forward();
_tableAnimationController.forward();
_kpiAnimationController.forward();
});
}
void _setupAnimations() { _cardSlideAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _cardAnimationController, curve: Curves.easeOutCubic, ), );
_chartScaleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _chartAnimationController,
curve: Curves.elasticOut,
),
);
_tableOpacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _tableAnimationController,
curve: Curves.easeInOut,
),
);
_kpiCounterAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _kpiAnimationController,
curve: Curves.easeOutCubic,
),
);
}
@override void dispose() { _gradientController.dispose(); _cardAnimationController.dispose(); _chartAnimationController.dispose(); _tableAnimationController.dispose(); _kpiAnimationController.dispose(); super.dispose(); }
void _showSectorDetails(String sector, double value, Color color) { final isDark = Theme.of(context).brightness == Brightness.dark;
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
backgroundColor: isDark ? const Color(0xFF1E1E48) : Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
width: 400,
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(Icons.analytics, color: color, size: 24),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Analyse - $sector',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
Text(
'Efficacité: ${(value * 100).toInt()}%',
style: TextStyle(
fontSize: 14,
color: color,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
const SizedBox(height: 20),
_buildDetailRow('Projets déposés', '${(value * 45).toInt()}', Icons.description),
_buildDetailRow('Projets adoptés', '${(value * 32).toInt()}', Icons.check_circle),
_buildDetailRow('Temps moyen', '${(value * 6.5).toStringAsFixed(1)} jours', Icons.schedule),
_buildDetailRow('Amendements', '${(value * 156).toInt()}', Icons.edit),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[300],
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('Fermer'),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
setState(() {
_selectedSector = sector;
});
},
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('Détails complets'),
),
),
],
),
],
),
),
);
},
);
}
Widget _buildDetailRow(String label, String value, IconData icon) { final isDark = Theme.of(context).brightness == Brightness.dark;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Icon(icon, size: 20, color: isDark ? Colors.white70 : Colors.grey[600]),
const SizedBox(width: 12),
Expanded(
child: Text(
label,
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.white70 : Colors.grey[700],
),
),
),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
],
),
);
}
void _showKpiDetails(String title, String value, String description) { final isDark = Theme.of(context).brightness == Brightness.dark;
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
backgroundColor: isDark ? const Color(0xFF1E1E48) : Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
const SizedBox(height: 16),
Text(
value,
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Color(0xFF6366F1),
),
),
const SizedBox(height: 12),
Text(
description,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: isDark ? Colors.white70 : Colors.grey[600],
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6366F1),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('Fermer'),
),
],
),
),
);
},
);
}
Widget _buildKpiCard(String title, String value, String subtitle, IconData icon, Color color, double targetValue, int index) { final isDark = Theme.of(context).brightness == Brightness.dark;
return AnimatedBuilder(
animation: _kpiAnimationController,
builder: (context, child) {
final animationDelay = index * 0.2;
final delayedAnimation = math.max(0.0, _kpiCounterAnimation.value - animationDelay);
final displayValue = (targetValue * delayedAnimation).toInt();
return Transform.translate(
offset: Offset(0, 30 * (1 - delayedAnimation.clamp(0.0, 1.0))),
child: Opacity(
opacity: delayedAnimation.clamp(0.0, 1.0),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => _showKpiDetails(title, value, subtitle),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1E1E48) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
border: Border.all(
color: color.withOpacity(0.3),
width: 1.5,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: color, size: 24),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
'+${(displayValue * 0.12).toInt()}%',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: color,
),
),
),
],
),
const SizedBox(height: 16),
Text(
displayValue.toString(),
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: color,
),
),
const SizedBox(height: 4),
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black,
),
),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.white60 : Colors.grey[600],
),
),
],
),
),
),
),
),
);
},
);
}
Widget _buildAnalysisChart() { final isDark = Theme.of(context).brightness == Brightness.dark;
return AnimatedBuilder(
animation: _chartAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _chartScaleAnimation.value.clamp(0.0, 1.0),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1E1E48) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'Efficacité législative par secteur',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: const Color(0xFF6366F1).withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'Données en temps réel',
style: TextStyle(
fontSize: 12,
color: Color(0xFF6366F1),
fontWeight: FontWeight.w600,
),
),
),
],
),
const SizedBox(height: 20),
Expanded(
child: Row(
children: [
Expanded(
flex: 2,
child: Column(
children: [
_buildClickableHorizontalBar('Économie', 0.85, const Color(0xFF6366F1)),
_buildClickableHorizontalBar('Justice', 0.72, const Color(0xFFF59E0B)),
_buildClickableHorizontalBar('Infrastructure', 0.65, const Color(0xFF10B981)),
_buildClickableHorizontalBar('Social', 0.58, const Color(0xFFEC4899)),
_buildClickableHorizontalBar('Éducation', 0.45, const Color(0xFF8B5CF6)),
_buildClickableHorizontalBar('Santé', 0.38, const Color(0xFF06B6D4)),
],
),
),
const SizedBox(width: 20),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildClickableCircularIndicator('Taux adoption', 0.73, const Color(0xFF10B981)),
_buildClickableCircularIndicator('Temps moyen', 0.45, const Color(0xFFF59E0B)),
_buildClickableCircularIndicator('Qualité débats', 0.82, const Color(0xFF6366F1)),
],
),
),
],
),
),
],
),
),
);
},
);
}
Widget _buildClickableHorizontalBar(String label, double value, Color color) { final isDark = Theme.of(context).brightness == Brightness.dark;
return AnimatedBuilder(
animation: _chartAnimationController,
builder: (context, child) {
final animatedValue = (value * _chartScaleAnimation.value).clamp(0.0, 1.0);
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => _showSectorDetails(label, value, color),
child: Container(
margin: const EdgeInsets.symmetric(vertical: 4),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: _selectedSector == label
? color.withOpacity(0.1)
: Colors.transparent,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: _selectedSector == label ? FontWeight.bold : FontWeight.w600,
color: _selectedSector == label
? color
: (isDark ? Colors.white : Colors.black),
),
),
Text(
'${(value * 100).toInt()}%',Source: Doriandarko/claude-engineer