335 lines
9.5 KiB
Dart
335 lines
9.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:fl_chart/fl_chart.dart';
|
|
import '../../../../shared/theme/app_theme.dart';
|
|
|
|
class ChartCard extends StatelessWidget {
|
|
final String title;
|
|
final Widget chart;
|
|
final String? subtitle;
|
|
final VoidCallback? onTap;
|
|
|
|
const ChartCard({
|
|
super.key,
|
|
required this.title,
|
|
required this.chart,
|
|
this.subtitle,
|
|
this.onTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.08),
|
|
blurRadius: 15,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppTheme.textPrimary,
|
|
),
|
|
),
|
|
if (subtitle != null) ...[
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
subtitle!,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: AppTheme.textSecondary,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
if (onTap != null)
|
|
const Icon(
|
|
Icons.arrow_forward_ios,
|
|
size: 16,
|
|
color: AppTheme.textHint,
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 20),
|
|
SizedBox(
|
|
height: 200,
|
|
child: chart,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class MembershipChart extends StatelessWidget {
|
|
const MembershipChart({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return LineChart(
|
|
LineChartData(
|
|
gridData: FlGridData(
|
|
show: true,
|
|
drawVerticalLine: false,
|
|
horizontalInterval: 200,
|
|
getDrawingHorizontalLine: (value) {
|
|
return FlLine(
|
|
color: AppTheme.borderColor.withOpacity(0.5),
|
|
strokeWidth: 1,
|
|
);
|
|
},
|
|
),
|
|
titlesData: FlTitlesData(
|
|
show: true,
|
|
rightTitles: const AxisTitles(
|
|
sideTitles: SideTitles(showTitles: false),
|
|
),
|
|
topTitles: const AxisTitles(
|
|
sideTitles: SideTitles(showTitles: false),
|
|
),
|
|
leftTitles: AxisTitles(
|
|
sideTitles: SideTitles(
|
|
showTitles: true,
|
|
interval: 200,
|
|
getTitlesWidget: (value, meta) {
|
|
return Text(
|
|
value.toInt().toString(),
|
|
style: const TextStyle(
|
|
color: AppTheme.textHint,
|
|
fontSize: 12,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
bottomTitles: AxisTitles(
|
|
sideTitles: SideTitles(
|
|
showTitles: true,
|
|
getTitlesWidget: (value, meta) {
|
|
const months = ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun'];
|
|
if (value.toInt() < months.length) {
|
|
return Text(
|
|
months[value.toInt()],
|
|
style: const TextStyle(
|
|
color: AppTheme.textHint,
|
|
fontSize: 12,
|
|
),
|
|
);
|
|
}
|
|
return const Text('');
|
|
},
|
|
),
|
|
),
|
|
),
|
|
borderData: FlBorderData(show: false),
|
|
minX: 0,
|
|
maxX: 5,
|
|
minY: 800,
|
|
maxY: 1400,
|
|
lineBarsData: [
|
|
LineChartBarData(
|
|
spots: const [
|
|
FlSpot(0, 1000),
|
|
FlSpot(1, 1050),
|
|
FlSpot(2, 1100),
|
|
FlSpot(3, 1180),
|
|
FlSpot(4, 1220),
|
|
FlSpot(5, 1247),
|
|
],
|
|
color: AppTheme.primaryColor,
|
|
barWidth: 3,
|
|
isStrokeCapRound: true,
|
|
dotData: FlDotData(
|
|
show: true,
|
|
getDotPainter: (spot, percent, barData, index) {
|
|
return FlDotCirclePainter(
|
|
radius: 4,
|
|
color: AppTheme.primaryColor,
|
|
strokeWidth: 2,
|
|
strokeColor: Colors.white,
|
|
);
|
|
},
|
|
),
|
|
belowBarData: BarAreaData(
|
|
show: true,
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
AppTheme.primaryColor.withOpacity(0.3),
|
|
AppTheme.primaryColor.withOpacity(0.1),
|
|
AppTheme.primaryColor.withOpacity(0.0),
|
|
],
|
|
begin: Alignment.topCenter,
|
|
end: Alignment.bottomCenter,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class CategoryChart extends StatelessWidget {
|
|
const CategoryChart({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return PieChart(
|
|
PieChartData(
|
|
sectionsSpace: 4,
|
|
centerSpaceRadius: 50,
|
|
sections: [
|
|
PieChartSectionData(
|
|
color: AppTheme.primaryColor,
|
|
value: 45,
|
|
title: 'Actifs\n45%',
|
|
radius: 60,
|
|
titleStyle: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
PieChartSectionData(
|
|
color: AppTheme.secondaryColor,
|
|
value: 30,
|
|
title: 'Retraités\n30%',
|
|
radius: 60,
|
|
titleStyle: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
PieChartSectionData(
|
|
color: AppTheme.accentColor,
|
|
value: 25,
|
|
title: 'Étudiants\n25%',
|
|
radius: 60,
|
|
titleStyle: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class RevenueChart extends StatelessWidget {
|
|
const RevenueChart({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BarChart(
|
|
BarChartData(
|
|
alignment: BarChartAlignment.spaceAround,
|
|
maxY: 15000,
|
|
barTouchData: BarTouchData(enabled: false),
|
|
titlesData: FlTitlesData(
|
|
show: true,
|
|
rightTitles: const AxisTitles(
|
|
sideTitles: SideTitles(showTitles: false),
|
|
),
|
|
topTitles: const AxisTitles(
|
|
sideTitles: SideTitles(showTitles: false),
|
|
),
|
|
leftTitles: AxisTitles(
|
|
sideTitles: SideTitles(
|
|
showTitles: true,
|
|
interval: 5000,
|
|
getTitlesWidget: (value, meta) {
|
|
return Text(
|
|
'${(value / 1000).toInt()}k€',
|
|
style: const TextStyle(
|
|
color: AppTheme.textHint,
|
|
fontSize: 12,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
bottomTitles: AxisTitles(
|
|
sideTitles: SideTitles(
|
|
showTitles: true,
|
|
getTitlesWidget: (value, meta) {
|
|
const months = ['J', 'F', 'M', 'A', 'M', 'J'];
|
|
if (value.toInt() < months.length) {
|
|
return Text(
|
|
months[value.toInt()],
|
|
style: const TextStyle(
|
|
color: AppTheme.textHint,
|
|
fontSize: 12,
|
|
),
|
|
);
|
|
}
|
|
return const Text('');
|
|
},
|
|
),
|
|
),
|
|
),
|
|
borderData: FlBorderData(show: false),
|
|
gridData: FlGridData(
|
|
show: true,
|
|
drawVerticalLine: false,
|
|
horizontalInterval: 5000,
|
|
getDrawingHorizontalLine: (value) {
|
|
return FlLine(
|
|
color: AppTheme.borderColor.withOpacity(0.5),
|
|
strokeWidth: 1,
|
|
);
|
|
},
|
|
),
|
|
barGroups: [
|
|
_buildBarGroup(0, 8000, AppTheme.primaryColor),
|
|
_buildBarGroup(1, 9500, AppTheme.primaryColor),
|
|
_buildBarGroup(2, 7800, AppTheme.primaryColor),
|
|
_buildBarGroup(3, 11200, AppTheme.primaryColor),
|
|
_buildBarGroup(4, 13500, AppTheme.primaryColor),
|
|
_buildBarGroup(5, 12800, AppTheme.primaryColor),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
BarChartGroupData _buildBarGroup(int x, double y, Color color) {
|
|
return BarChartGroupData(
|
|
x: x,
|
|
barRods: [
|
|
BarChartRodData(
|
|
toY: y,
|
|
color: color,
|
|
width: 16,
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(4),
|
|
topRight: Radius.circular(4),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
} |