I am trying to design a chart in a flutter app that displays data graphed against time or day (depending on a button press), however, I am running into an issue where the graph label text runs into each other.
I was wondering if there is a way to rotate text in the fl_chart object LineChartData to show the date or time at an angle or vertically rotated?
My code looks like this so far and the output of the graph looks like this:
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:sembast/timestamp.dart';
import '../models/outcome.dart';
dynamic grapher() {
return Stack(
children: <Widget>[
AspectRatio(
aspectRatio: 1.70,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(18),
),
color: Colors.black),
child: Padding(
padding: const EdgeInsets.only(
right: 18.0, left: 12.0, top: 24, bottom: 12),
child: LineChart(
mainData(),
),
),
),
),
],
);
}
LineChartData mainData() {
return LineChartData(
...
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 22,
textStyle: const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16),
getTitles: (value) {
//return value.round().toString();
for (int i = 0; i <= outcomeList.length; i++) {
return outcomeList[i].recordedTime.toString();
}
},
margin: 8,
),
...
),
...
}
List<FlSpot> datapoints = [
FlSpot(0, outcomeList[0].value),
FlSpot(2.6, outcomeList[1].value),
FlSpot(4.9, outcomeList[2].value),
FlSpot(6.8, outcomeList[3].value),
FlSpot(8, outcomeList[4].value),
FlSpot(10, outcomeList[5].value),
];
List<Outcome> outcomeList = [
Outcome(name: 'mood', recordedTime: Timestamp.now(), value: 5.6),
Outcome(
name: 'mood',
recordedTime:
Timestamp.fromDateTime(DateTime.now().add(new Duration(hours: 1))),
value: 6.7),
Outcome(
name: 'mood',
recordedTime:
Timestamp.fromDateTime(DateTime.now().add(new Duration(hours: 2))),
value: 5.5),
Outcome(
name: 'mood',
recordedTime:
Timestamp.fromDateTime(DateTime.now().add(new Duration(hours: 3))),
value: 6.2),
Outcome(
name: 'mood',
recordedTime:
Timestamp.fromDateTime(DateTime.now().add(new Duration(hours: 4))),
value: 7.7),
Outcome(
name: 'mood',
recordedTime: Timestamp.fromDateTime(
DateTime.now().add(new Duration(hours: 5, minutes: 26))),
value: 6.4),
];
You can use SideTitles's attribute rotateAngle
full code is official demo LineChartSample7 use rotateAngle
You can see red rectangle of working demo below
code snippet
SideTitles(
rotateAngle: 90,
showTitles: true,
working demo
full code
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
class LineChartSample7 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SizedBox(
width: 300,
height: 140,
child: LineChart(
LineChartData(
lineTouchData: LineTouchData(enabled: false),
lineBarsData: [
LineChartBarData(
spots: [
FlSpot(0, 4),
FlSpot(1, 3.5),
FlSpot(2, 4.5),
FlSpot(3, 1),
FlSpot(4, 4),
FlSpot(5, 6),
FlSpot(6, 6.5),
FlSpot(7, 6),
FlSpot(8, 4),
FlSpot(9, 6),
FlSpot(10, 6),
FlSpot(11, 7),
],
isCurved: true,
barWidth: 2,
colors: [
Colors.green,
],
dotData: FlDotData(
show: false,
),
),
LineChartBarData(
spots: [
FlSpot(0, 0),
FlSpot(1, 3),
FlSpot(2, 4),
FlSpot(3, 5),
FlSpot(4, 8),
FlSpot(5, 3),
FlSpot(6, 5),
FlSpot(7, 8),
FlSpot(8, 4),
FlSpot(9, 7),
FlSpot(10, 7),
FlSpot(11, 8),
],
isCurved: true,
barWidth: 2,
colors: [
Colors.black,
],
dotData: FlDotData(
show: false,
),
),
LineChartBarData(
spots: [
FlSpot(0, 7),
FlSpot(1, 3),
FlSpot(2, 4),
FlSpot(3, 0),
FlSpot(4, 3),
FlSpot(5, 4),
FlSpot(6, 5),
FlSpot(7, 3),
FlSpot(8, 2),
FlSpot(9, 4),
FlSpot(10, 1),
FlSpot(11, 3),
],
isCurved: false,
barWidth: 2,
colors: [
Colors.red,
],
dotData: FlDotData(
show: false,
),
),
],
betweenBarsData: [
BetweenBarsData(
fromIndex: 0,
toIndex: 2,
colors: [Colors.red.withOpacity(0.3)],
)
],
minY: 0,
titlesData: FlTitlesData(
bottomTitles: SideTitles(
rotateAngle: 90,
showTitles: true,
textStyle:
TextStyle(fontSize: 10, color: Colors.purple, fontWeight: FontWeight.bold),
getTitles: (value) {
switch (value.toInt()) {
case 0:
return 'Jan';
case 1:
return 'Feb';
case 2:
return 'Mar';
case 3:
return 'Apr';
case 4:
return 'May';
case 5:
return 'Jun';
case 6:
return 'Jul';
case 7:
return 'Aug';
case 8:
return 'Sep';
case 9:
return 'Oct';
case 10:
return 'Nov';
case 11:
return 'Dec';
default:
return '';
}
}),
leftTitles: SideTitles(
showTitles: true,
getTitles: (value) {
return '\$ ${value + 0.5}';
},
),
),
gridData: FlGridData(
show: true,
checkToShowHorizontalLine: (double value) {
return value == 1 || value == 6 || value == 4 || value == 5;
},
),
),
),
);
}
}
Related
How can i avoid overlapping of the x-axis data and the overlapping tooltip values, i tried reducing the font but that didnt work.For tooltip i wanted to show only maximum value,and other values only when the user clicks on it.
BarChart(
BarChartData(
barTouchData: barTouchData,
titlesData: titlesData,
borderData: borderData,
barGroups: barGroups,
gridData: FlGridData(show: false),
alignment: BarChartAlignment.spaceAround,
minY: 0,
maxY: double.parse((y! + 1000).toString()),
//double.parse(widget.total.toString()),
),
);
}
BarTouchData get barTouchData => BarTouchData(
enabled: true,
touchTooltipData: BarTouchTooltipData(
tooltipBgColor: Colors.transparent,
tooltipPadding: EdgeInsets.zero,
tooltipMargin: 5,
getTooltipItem: (
BarChartGroupData group,
int groupIndex,
BarChartRodData rod,
int rodIndex,
) {
return BarTooltipItem(
rod.toY.round().toString(),
const TextStyle(
fontSize: 10,
color: Colors.white,
// fontWeight: FontWeight.bold,
),
);
},
),
);
FlTitlesData get titlesData => FlTitlesData(
show: true,
bottomTitles: AxisTitles(
sideTitles: SideTitles(
// rotateAngle: 90,
showTitles: true,
reservedSize: 30,
// getTitlesWidget: getTitles,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false, reservedSize: 30),
),
topTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: false,
),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
);
FlBorderData get borderData => FlBorderData(
show: true,
border: Border.all(color: Color.fromARGB(255, 207, 182, 168), width: 1),
);
LinearGradient get _barsGradient => const LinearGradient(
colors: [
Colors.lightBlueAccent,
Colors.greenAccent,
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
);
List<BarChartGroupData> get barGroups =>
List.generate(widget.data.length, (index) {
//return Text(text[index].toString());
// for ( var i in widget.data ) Text(i.toString())
return BarChartGroupData(
x: index + 1,
barRods: [
BarChartRodData(
toY: widget.data[index].toDouble(),
gradient: _barsGradient,
)
],
showingTooltipIndicators: [0],
);
});
}
I'm trying to create a line chart off the last 12 hours. I'd like the x-axis to read the labels as "12pm, 5am" respectively. The x-axis labels should only show star/end. When my code gets to this point it freezes the simulator with no errors.
sensorData has 12 objects in it and are stored as a DateTime. The times are hourly based.
final Color lineColor;
final List<SpatialGrowSensor> sensorData;
const SensorLineChart({
Key? key,
required this.sensorData,
required this.lineColor,
}) : super(key: key);
LineChartData sampleData1(List<SpatialGrowSensor> sensorData) {
return LineChartData(
lineTouchData: lineTouchData1(),
gridData: gridData(),
titlesData: titlesData1(),
borderData: borderData(),
lineBarsData: lineBarsData1(),
minY: 0,
maxY: 10,
);
}
LineTouchData lineTouchData1() => LineTouchData(
handleBuiltInTouches: true,
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.blueGrey.withOpacity(0.8),
),
);
FlTitlesData titlesData1() => FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: bottomTitles(),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: leftTitles(),
),
);
List<LineChartBarData> lineBarsData1() => [
lineChartBarData1_1(),
];
Widget leftTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
);
return Text(value.toString(), style: style, textAlign: TextAlign.center);
}
SideTitles leftTitles() => SideTitles(
getTitlesWidget: leftTitleWidgets,
showTitles: true,
reservedSize: 32,
interval: 2,
);
Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
);
// format datetime as '12pm'
String dateFormated = DateFormat('ha').format(DateTime.fromMillisecondsSinceEpoch(value.toInt(), isUtc: true));
Widget text = Text(dateFormated, style: style);
return SideTitleWidget(
axisSide: meta.axisSide,
space: 10,
child: text,
);
}
SideTitles bottomTitles() => SideTitles(
showTitles: true,
reservedSize: 32,
interval: 6,
getTitlesWidget: bottomTitleWidgets,
);
FlGridData gridData() => FlGridData(show: false);
FlBorderData borderData() => FlBorderData(
show: true,
border: const Border(
bottom: BorderSide(color: Color(0xff4e4965), width: 4),
left: BorderSide(color: Colors.transparent),
right: BorderSide(color: Colors.transparent),
top: BorderSide(color: Colors.transparent),
),
);
LineChartBarData lineChartBarData1_1() {
final flSpotData = sensorData.map((sensor) {
return FlSpot(sensor.dateTime.millisecondsSinceEpoch.toDouble(), sensor.sensorValue);
}).toList();
return LineChartBarData(
isCurved: true,
color: lineColor,
barWidth: 4,
dotData: FlDotData(show: false),
belowBarData: BarAreaData(show: false),
preventCurveOverShooting: true,
spots: flSpotData,
);
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: HexColor('#0c110a'),
),
child: SizedBox(
height: 200,
child: Padding(
padding: const EdgeInsets.all(32.0),
child: LineChart(
sampleData1(sensorData),
),
),
),
),
);
}
}```
How can I do this with fl_chart?
current view
what i want to do
my codes..
How should I revise my codes?
I am using fl_chart for the first time.
Value - Total Production - 100000 unit
time taken for total production.
return LineChartData(
lineTouchData: LineTouchData(enabled: false),
gridData: FlGridData(show: false),
borderData: FlBorderData(show: false),
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 0,
getTextStyles: (context, value) => const TextStyle(color: Color(0xff68737d), fontWeight: FontWeight.bold, fontSize: 16),
getTitles: (value) {
switch (value.toInt()) {
case 1:
return 'ÜRETİM BANDI';
case 2:
return 'GEÇEN SÜRE';
}
return '';
},
margin: 10,
interval: 1,
),
leftTitles: SideTitles(showTitles: false),
topTitles: SideTitles(showTitles: false),
rightTitles: SideTitles(showTitles: false),
),
minX: 0,
maxX: 2,
minY: 0,
maxY: 0,
lineBarsData: [
LineChartBarData(
spots: const [
FlSpot(0, 10),
FlSpot(10, 20),
],
isCurved: true,
colors: [
ColorTween(begin: gradientColors[0], end: gradientColors[1]).lerp(0.2)!,
ColorTween(begin: gradientColors[0], end: gradientColors[1]).lerp(0.2)!,
],
barWidth: 10,
isStrokeCapRound: true,
isStepLineChart: true,
dotData: FlDotData(
show: false,
),
belowBarData: BarAreaData(show: false),
),
],
);
thank you,
return LineChart(
LineChartData(
gridData: FlGridData(
show: false,
),
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 14,
getTitles: (value) {
return _months[value.toInt()];
},
),
leftTitles: SideTitles(
showTitles: true,
getTitles: (value) {
return (value.toInt()).toString();
},
interval: 180/10,
reservedSize: 28,
margin: 12,
),
),
borderData: FlBorderData(show: false),
minX: 0,
minY: 0,
lineBarsData: [
LineChartBarData(spots: [
FlSpot(1, 10),
FlSpot(2, 50),
FlSpot(3, 99),
FlSpot(4, 45),
FlSpot(5, 130),
FlSpot(6, 180),
], isCurved: true,
barWidth: 5,
isStrokeCapRound: true,
belowBarData: BarAreaData(
show: true,
colors: [Colors.yellow, Colors.red],
gradientColorStops: [0, 0.1, 0.2],
gradientFrom: const Offset(1, 1),
// gradientTo: const Offset(0, 1),
),
)
]),
);
Can anyone help me to design this type of line chart with the shadows as on the image
I have a fl_chart (pub), which is displayed below in the gifs. When transitioning data the painter paints outside of the chart's bounds. How could I add a clipper (or some other fix) to the chart below, so this bug does not occur? There is some code at the bottom & some images. fl_chart uses a CustomPainter to draw the charts, so maybe I could override something in the source code?
My quick fix (I removed the transition animation, but I'd like to use the animation):
Ignore the label bug on the y axis (on the left)
(If you don't see the bug look close on the right side)
I would like to use a transition like this, but without the chart going outside the boundaries:
Here is the code:
LineChartData mainData() {
return LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
fitInsideHorizontally: true,
tooltipBgColor: Colors.white,
getTooltipItems: (List<LineBarSpot> touchedBarSpots) {
return touchedBarSpots.map((barSpot) {
return LineTooltipItem(
'${barSpot.y.toInt()}',
TextStyle(
fontFamily: 'Jost*',
fontSize: 15,
color: Colors.black,
),
);
}).toList();
}
),
getTouchedSpotIndicator: (LineChartBarData barData, List<int> spotIndexes) {
return spotIndexes.map((spotIndex) {
return TouchedSpotIndicatorData(
FlLine(
color: const Color.fromARGB(255, 77, 77, 77),
strokeWidth: 1,
dashArray: [4,4],
),
FlDotData(
getDotPainter: (spot, percent, barData, index) {
return FlDotCirclePainter(
radius: 5.5,
color: gradientColors[0],
strokeWidth: 2,
strokeColor: Colors.white,
);
},
),
);
}).toList();
}
),
gridData: FlGridData(
show: true,
getDrawingHorizontalLine: (value) {
return FlLine(
color: const Color.fromARGB(255, 98, 95, 161),
strokeWidth: 1,
dashArray: [4,4]
);
},
),
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 14,
textStyle:
const TextStyle(
color: Color.fromARGB(255, 181, 181, 181),
fontWeight: FontWeight.w300,
fontFamily: 'Jost*',
fontSize: 13,
),
getTitles: (value) {
return _labels[widget.timeType][value.toInt()] ?? '';
},
),
leftTitles: SideTitles(
showTitles: true,
textStyle: const TextStyle(
color: Color.fromARGB(255, 181, 181, 181),
fontWeight: FontWeight.w300,
fontFamily: 'Jost*',
fontSize: 16,
),
getTitles: (value) {
return (value.toInt()).toString();
},
reservedSize: 28,
margin: 12,
),
),
borderData:
FlBorderData(
show: true,
border: Border.symmetric(
horizontal: BorderSide(
color: const Color.fromARGB(255, 170, 170, 170),
width: 1.2
),
),
),
minX: 0,
maxX: _data[widget.timeType].length.toDouble()-1, //length of data set
minY: _data[widget.timeType].reduce(min).toDouble() - 1, //set to lowest v
maxY: _data[widget.timeType].reduce(max).toDouble() + 1, //set to highest v
lineBarsData: [
LineChartBarData(
spots: [
for (int i = 0; i < _data[widget.timeType].length; i++)
FlSpot(i.toDouble(), _data[widget.timeType][i].toDouble())
],
//FlSpot(2.6, 4),
isCurved: true,
colors: [
gradientColors[0],
],
barWidth: 2,
isStrokeCapRound: true,
dotData: FlDotData(
show: false,
),
belowBarData: BarAreaData(
show: true,
colors: gradientColors,
gradientColorStops: [0, 0.5, 1.0],
gradientFrom: const Offset(0, 0),
gradientTo: const Offset(0, 1),
),
),
],
);
}
there is a clipData property in the LinechartData
try to set it as FlClipData.all().