287 lines
8.4 KiB
Dart
287 lines
8.4 KiB
Dart
import 'package:fl_chart_app/presentation/resources/app_resources.dart';
|
|
import 'package:fl_chart_app/util/extensions/color_extensions.dart';
|
|
import 'package:fl_chart/fl_chart.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
// ignore: must_be_immutable
|
|
class LineChartSample6 extends StatelessWidget {
|
|
LineChartSample6({
|
|
super.key,
|
|
Color? line1Color1,
|
|
Color? line1Color2,
|
|
Color? line2Color1,
|
|
Color? line2Color2,
|
|
}) : line1Color1 = line1Color1 ?? AppColors.contentColorOrange,
|
|
line1Color2 = line1Color2 ?? AppColors.contentColorOrange.darken(60),
|
|
line2Color1 = line2Color1 ?? AppColors.contentColorBlue.darken(60),
|
|
line2Color2 = line2Color2 ?? AppColors.contentColorBlue {
|
|
minSpotX = spots.first.x;
|
|
maxSpotX = spots.first.x;
|
|
minSpotY = spots.first.y;
|
|
maxSpotY = spots.first.y;
|
|
|
|
for (final spot in spots) {
|
|
if (spot.x > maxSpotX) {
|
|
maxSpotX = spot.x;
|
|
}
|
|
|
|
if (spot.x < minSpotX) {
|
|
minSpotX = spot.x;
|
|
}
|
|
|
|
if (spot.y > maxSpotY) {
|
|
maxSpotY = spot.y;
|
|
}
|
|
|
|
if (spot.y < minSpotY) {
|
|
minSpotY = spot.y;
|
|
}
|
|
}
|
|
}
|
|
|
|
final Color line1Color1;
|
|
final Color line1Color2;
|
|
final Color line2Color1;
|
|
final Color line2Color2;
|
|
|
|
final spots = const [
|
|
FlSpot(0, 1),
|
|
FlSpot(2, 5),
|
|
FlSpot(4, 3),
|
|
FlSpot(6, 5),
|
|
];
|
|
|
|
final spots2 = const [
|
|
FlSpot(0, 3),
|
|
FlSpot(2, 1),
|
|
FlSpot(4, 2),
|
|
FlSpot(6, 1),
|
|
];
|
|
|
|
late double minSpotX;
|
|
late double maxSpotX;
|
|
late double minSpotY;
|
|
late double maxSpotY;
|
|
|
|
Widget leftTitleWidgets(double value, TitleMeta meta) {
|
|
final style = TextStyle(
|
|
color: line1Color1,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 18,
|
|
);
|
|
|
|
final intValue = reverseY(value, minSpotY, maxSpotY).toInt();
|
|
|
|
if (intValue == (maxSpotY + minSpotY)) {
|
|
return Text('', style: style);
|
|
}
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 6),
|
|
child: Text(
|
|
intValue.toString(),
|
|
style: style,
|
|
textAlign: TextAlign.center,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget rightTitleWidgets(double value, TitleMeta meta) {
|
|
final style = TextStyle(
|
|
color: line2Color2,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 18,
|
|
);
|
|
final intValue = reverseY(value, minSpotY, maxSpotY).toInt();
|
|
|
|
if (intValue == (maxSpotY + minSpotY)) {
|
|
return Text('', style: style);
|
|
}
|
|
|
|
return Text(intValue.toString(), style: style, textAlign: TextAlign.right);
|
|
}
|
|
|
|
Widget topTitleWidgets(double value, TitleMeta meta) {
|
|
if (value % 1 != 0) {
|
|
return Container();
|
|
}
|
|
const style = TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.mainTextColor2,
|
|
);
|
|
return SideTitleWidget(
|
|
meta: meta,
|
|
child: Text(value.toInt().toString(), style: style),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 22, bottom: 20),
|
|
child: AspectRatio(
|
|
aspectRatio: 2,
|
|
child: LineChart(
|
|
LineChartData(
|
|
lineTouchData: LineTouchData(
|
|
touchTooltipData: LineTouchTooltipData(
|
|
tooltipBorderRadius: BorderRadius.zero,
|
|
getTooltipColor: (spot) => Colors.white,
|
|
getTooltipItems: (List<LineBarSpot> touchedSpots) {
|
|
return touchedSpots.map((LineBarSpot touchedSpot) {
|
|
return LineTooltipItem(
|
|
touchedSpot.y.toString(),
|
|
TextStyle(
|
|
color: touchedSpot.bar.gradient!.colors.first,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 20,
|
|
),
|
|
);
|
|
}).toList();
|
|
},
|
|
),
|
|
getTouchedSpotIndicator: (
|
|
_,
|
|
indicators,
|
|
) {
|
|
return indicators
|
|
.map((int index) => const TouchedSpotIndicatorData(
|
|
FlLine(color: Colors.transparent),
|
|
FlDotData(show: false),
|
|
))
|
|
.toList();
|
|
},
|
|
touchSpotThreshold: 12,
|
|
distanceCalculator:
|
|
(Offset touchPoint, Offset spotPixelCoordinates) =>
|
|
(touchPoint - spotPixelCoordinates).distance,
|
|
),
|
|
lineBarsData: [
|
|
LineChartBarData(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
line1Color1,
|
|
line1Color2,
|
|
],
|
|
),
|
|
spots: reverseSpots(spots, minSpotY, maxSpotY),
|
|
isCurved: true,
|
|
isStrokeCapRound: true,
|
|
barWidth: 10,
|
|
belowBarData: BarAreaData(
|
|
show: false,
|
|
),
|
|
dotData: FlDotData(
|
|
show: true,
|
|
getDotPainter: (spot, percent, barData, index) {
|
|
return FlDotCirclePainter(
|
|
radius: 12,
|
|
color: Color.lerp(
|
|
line1Color1,
|
|
line1Color2,
|
|
percent / 100,
|
|
)!,
|
|
strokeColor: Colors.white,
|
|
strokeWidth: 1,
|
|
);
|
|
},
|
|
),
|
|
),
|
|
LineChartBarData(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
line2Color1,
|
|
line2Color2,
|
|
],
|
|
),
|
|
spots: reverseSpots(spots2, minSpotY, maxSpotY),
|
|
isCurved: true,
|
|
isStrokeCapRound: true,
|
|
barWidth: 10,
|
|
belowBarData: BarAreaData(
|
|
show: false,
|
|
),
|
|
dotData: FlDotData(
|
|
show: true,
|
|
getDotPainter: (spot, percent, barData, index) {
|
|
return FlDotCirclePainter(
|
|
radius: 12,
|
|
color: Color.lerp(
|
|
line2Color1,
|
|
line2Color2,
|
|
percent / 100,
|
|
)!,
|
|
strokeColor: Colors.white,
|
|
strokeWidth: 1,
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
minY: 0,
|
|
maxY: maxSpotY + minSpotY,
|
|
titlesData: FlTitlesData(
|
|
leftTitles: AxisTitles(
|
|
sideTitles: SideTitles(
|
|
showTitles: true,
|
|
getTitlesWidget: leftTitleWidgets,
|
|
reservedSize: 38,
|
|
),
|
|
),
|
|
rightTitles: AxisTitles(
|
|
sideTitles: SideTitles(
|
|
showTitles: true,
|
|
getTitlesWidget: rightTitleWidgets,
|
|
reservedSize: 30,
|
|
),
|
|
),
|
|
bottomTitles: const AxisTitles(
|
|
sideTitles: SideTitles(showTitles: false),
|
|
),
|
|
topTitles: AxisTitles(
|
|
sideTitles: SideTitles(
|
|
showTitles: true,
|
|
reservedSize: 32,
|
|
getTitlesWidget: topTitleWidgets,
|
|
),
|
|
),
|
|
),
|
|
gridData: FlGridData(
|
|
show: true,
|
|
drawVerticalLine: true,
|
|
checkToShowHorizontalLine: (value) {
|
|
final intValue = reverseY(value, minSpotY, maxSpotY).toInt();
|
|
|
|
if (intValue == (maxSpotY + minSpotY).toInt()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
),
|
|
borderData: FlBorderData(
|
|
show: true,
|
|
border: const Border(
|
|
left: BorderSide(color: AppColors.borderColor),
|
|
top: BorderSide(color: AppColors.borderColor),
|
|
bottom: BorderSide(color: Colors.transparent),
|
|
right: BorderSide(color: Colors.transparent),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
double reverseY(double y, double minX, double maxX) {
|
|
return (maxX + minX) - y;
|
|
}
|
|
|
|
List<FlSpot> reverseSpots(List<FlSpot> inputSpots, double minY, double maxY) {
|
|
return inputSpots.map((spot) {
|
|
return spot.copyWith(y: (maxY + minY) - spot.y);
|
|
}).toList();
|
|
}
|
|
}
|