2023-11-21 20:23:14 +01:00
|
|
|
import 'package:fl_chart/fl_chart.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
2023-11-22 15:09:05 +01:00
|
|
|
import 'package:prasule/main.dart';
|
2023-11-21 20:23:14 +01:00
|
|
|
|
2023-11-22 15:09:05 +01:00
|
|
|
/// Monthly/Yearly expense/income [LineChart]
|
2023-11-21 20:23:14 +01:00
|
|
|
class ExpensesChart extends StatelessWidget {
|
|
|
|
const ExpensesChart(
|
|
|
|
{super.key,
|
|
|
|
required this.date,
|
|
|
|
required this.locale,
|
2023-11-22 15:09:05 +01:00
|
|
|
this.expenseData = const [],
|
|
|
|
this.incomeData = const [],
|
2023-11-21 20:23:14 +01:00
|
|
|
this.yearly = false});
|
|
|
|
final bool yearly;
|
|
|
|
final DateTime date;
|
|
|
|
final String locale;
|
2023-11-22 15:09:05 +01:00
|
|
|
final List<double> expenseData;
|
|
|
|
List<double> get expenseDataSorted {
|
|
|
|
var list = List<double>.from(expenseData);
|
2023-11-21 20:23:14 +01:00
|
|
|
list.sort((a, b) => a.compareTo(b));
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2023-11-22 15:09:05 +01:00
|
|
|
final List<double> incomeData;
|
|
|
|
List<double> get incomeDataSorted {
|
|
|
|
var list = List<double>.from(incomeData);
|
|
|
|
list.sort((a, b) => a.compareTo(b));
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
double get maxY {
|
|
|
|
if (incomeData.isEmpty) return expenseDataSorted.last;
|
|
|
|
if (expenseData.isEmpty) return incomeDataSorted.last;
|
|
|
|
if (expenseDataSorted.last > incomeDataSorted.last) {
|
|
|
|
return expenseDataSorted.last;
|
|
|
|
} else {
|
|
|
|
return incomeDataSorted.last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-21 20:23:14 +01:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return LineChart(
|
|
|
|
LineChartData(
|
|
|
|
maxX: (yearly) ? 12 : DateTime(date.year, date.month, 0).day.toDouble(),
|
2023-11-22 15:09:05 +01:00
|
|
|
maxY: maxY,
|
2023-11-21 20:23:14 +01:00
|
|
|
minX: 1,
|
|
|
|
minY: 0,
|
|
|
|
backgroundColor: Theme.of(context).colorScheme.background,
|
|
|
|
lineBarsData: [
|
2023-11-22 15:09:05 +01:00
|
|
|
if (incomeData.isNotEmpty)
|
|
|
|
LineChartBarData(
|
|
|
|
isCurved: true,
|
|
|
|
barWidth: 8,
|
|
|
|
isStrokeCapRound: true,
|
|
|
|
dotData: const FlDotData(show: false),
|
|
|
|
belowBarData: BarAreaData(show: false),
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
spots: List.generate(
|
|
|
|
(yearly) ? 12 : DateTime(date.year, date.month, 0).day,
|
|
|
|
(index) => FlSpot(index.toDouble() + 1, incomeData[index]),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
if (expenseData.isNotEmpty)
|
|
|
|
LineChartBarData(
|
|
|
|
isCurved: true,
|
|
|
|
barWidth: 8,
|
|
|
|
isStrokeCapRound: true,
|
|
|
|
dotData: const FlDotData(show: false),
|
|
|
|
belowBarData: BarAreaData(show: false),
|
|
|
|
color: Theme.of(context).colorScheme.error,
|
|
|
|
spots: List.generate(
|
|
|
|
(yearly) ? 12 : DateTime(date.year, date.month, 0).day,
|
|
|
|
(index) => FlSpot(index.toDouble() + 1, expenseData[index]),
|
|
|
|
),
|
2023-11-21 20:23:14 +01:00
|
|
|
),
|
|
|
|
], // actual data
|
|
|
|
titlesData: FlTitlesData(
|
|
|
|
rightTitles: const AxisTitles(
|
|
|
|
sideTitles: SideTitles(showTitles: false),
|
|
|
|
),
|
|
|
|
topTitles: const AxisTitles(
|
|
|
|
sideTitles: SideTitles(showTitles: false),
|
|
|
|
),
|
|
|
|
bottomTitles: AxisTitles(
|
|
|
|
sideTitles: SideTitles(
|
|
|
|
showTitles: true,
|
|
|
|
getTitlesWidget: (value, meta) {
|
|
|
|
String text;
|
|
|
|
if (yearly) {
|
|
|
|
text = DateFormat.MMM(locale).format(
|
|
|
|
DateTime(date.year, value.toInt(), 1),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
text = value.toInt().toString();
|
|
|
|
}
|
|
|
|
return SideTitleWidget(
|
|
|
|
axisSide: meta.axisSide, child: Text(text));
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
), // axis descriptions
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|