Files
unionflow-client-quarkus-pr…/unionflow-mobile-apps/lib/features/dashboard/presentation/widgets/chart_card.dart
2025-08-20 21:00:35 +00:00

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),
),
),
],
);
}
}