fl_chart
fl_chart copied to clipboard
x-Axis based on DateTime object and dots
Firstly i must to say that a worked with a lot of Flutter's chart library, and this is the best i found!
I have a problem with two lines on the same graph, and after a lot of checks i found that the x-Axis is based only on indexs, and can't be based on DateTime!
in my case it's really a big problem, because i have 2 lines to show on the graph, one based on quarter of an hour, and the second on minute, in this case there is no match between the 2 lines and the x-Axis.
As you can see on the orange text, the km/h and the date is right, but the location on the graph is not
Google charts is support DateTime graph, in Time Series Charts https://google.github.io/charts/flutter/gallery.html You can also see, for example, the Weekly Chart the x-Axis is based on DateTime object https://pub.dev/packages/bezier_chart
One more important thing is the barWidth, if is set to 0 the line is still appear, it could be good and right that 0 value make the line transparent, and stay only the Dots to appear.
In my case it's help to see a dangerous wind that more than 50km/h
(in the image attached i just drawed the red points in photoshop for the example, that why it's on 20kmh~)
Hope you will fix and support this because it's really critical, and add values to your awesome graph.
I'm not sure if my issue is the same or not, but definitely related. I currently have a lot of points that want plotted, but the bottom labels are getting on top of one another. It would be nice to get the labels to automatically adapt based on the values given.
This is my current chart:
Even though I want all the dots, I don't want all the labels at the bottom.
@xOldeVx I didn't understand the first problem, and I think you can explain it simpler, And about the second problem (lines with 0 barWidth), you are right but It is better to make a separate issue to follow it up separately.
And a reproducible code helps me a lot.
Thanks!
@jlubeck You can handle it using the interval
property of your SideTitle. Or alternatively you can override getTitles
and return empty string wherever you don't want to show a specific title.
Just as a note this issue relates to #444 and #462 and also +1 for this 👍
As you can see in the image, in the same point of time (7:30), on the red line is on 21:55 and on the blue is 7:30, if the x-axis was based according to DateTime object, (e.g. FlSpot(DateTime(2017, 9, 19), 25.0) || FlSpot(1612416600000, 25.0) instead of FlSpot(3, 25.0)) it was not happened.
As you can see in google charts, 2 kinds of line chart, one of time: https://google.github.io/charts/flutter/example/time_series_charts/simple) and one of index, like yours https://google.github.io/charts/flutter/example/line_charts/simple
@jlubeck You can handle it using the
interval
property of your SideTitle. Or alternatively you can overridegetTitles
and return empty string wherever you don't want to show a specific title.
Can you please detail how to use interval property. I have tried to set a value (15 by example) and the application is looping ans stop the UI My objective is to display a time serie with by example 3 points (9h32, 12:47, 18:29) but display bottom title every 30 minutes
As you can see in the image, in the same point of time (7:30), on the red line is on 21:55 and on the blue is 7:30, if the x-axis was based according to DateTime object, (e.g. FlSpot(DateTime(2017, 9, 19), 25.0) || FlSpot(1612416600000, 25.0) instead of FlSpot(3, 25.0)) it was not happened.
As you can see in google charts, 2 kinds of line chart, one of time: https://google.github.io/charts/flutter/example/time_series_charts/simple) and one of index, like yours https://google.github.io/charts/flutter/example/line_charts/simple
Any news? that very important
I'm looking to do timeseries charts as well and FL Charts seems to be by far and away the best charting library, but it's impossible to have x-axis and hover labels with timestamp data (although the data charts just fine). The Google chart does support timestamps, but it's pretty ugly and you can't change that. I would love to see more thoughts on timeseries data in fl_chart.
Cheers!
I converted my DateTime to miliseconds epoch and works fine.
//Convert to FLSpot
List<charts.FlSpot> toTimeSeriesFlSpots(TimeSeriesChartData chartData) {
if (chartData.serie?.isEmpty ?? true) return <charts.FlSpot>[];
final spots =
chartData.serie!.map((e) => charts.FlSpot(e.label.millisecondsSinceEpoch.toDouble(), e.value ?? 0)).toList();
return spots;
}
// Bulid titles
charts.FlTitlesData getDefaultTimeSeriesTitlesData(double? domainInterval,
{TextStyle? titlesTheme, double? codomainInterval}) {
return charts.FlTitlesData(
show: true,
bottomTitles: charts.SideTitles(
showTitles: true,
getTextStyles: (context, value) => titlesTheme,
rotateAngle: 270,
reservedSize: 50,
getTitles: getDomainTimeSeriesTitles,
interval: domainInterval));
@Monolite How did you calculate the domain intervals on your getDefaultTimeSeriesTitlesData
function?
At the bottomTitles interval, I set the domainInterval value as below:
domainInterval = DaysCount * Duration.millisecondsPerDay;
where DaysCount is the number of days between each title
Thank you @Monolite
I converted my DateTime to miliseconds epoch and works fine.
//Convert to FLSpot List<charts.FlSpot> toTimeSeriesFlSpots(TimeSeriesChartData chartData) { if (chartData.serie?.isEmpty ?? true) return <charts.FlSpot>[]; final spots = chartData.serie!.map((e) => charts.FlSpot(e.label.millisecondsSinceEpoch.toDouble(), e.value ?? 0)).toList(); return spots; } // Bulid titles charts.FlTitlesData getDefaultTimeSeriesTitlesData(double? domainInterval, {TextStyle? titlesTheme, double? codomainInterval}) { return charts.FlTitlesData( show: true, bottomTitles: charts.SideTitles( showTitles: true, getTextStyles: (context, value) => titlesTheme, rotateAngle: 270, reservedSize: 50, getTitles: getDomainTimeSeriesTitles, interval: domainInterval));
Could you show the full code to use dateTime as x coordinate? I have problems implementing it, especially to use the getTitles
like you did with getDomainTimeSeriesTitles
In my case, I converted DateTime to milliseconds epoch with a list of 64 spots, and it became Out Of Memory.
In my case, I converted DateTime to milliseconds epoch with a list of 64 spots, and it became Out Of Memory.
I am also facing the same issue. Getting out of memory whenover tried to pass date in miliseconds epoch
@shitake4 @abuzarbasit you getting OOM crash because the interval run on a lot of lines, you not really need all of these lines, you can use an accuracy of one minute, for that convert firstly the date time from milliseconds to seconds, here's a full example.
import 'dart:math';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart' as intl;
class NewGraphWidget extends StatefulWidget {
@override
State<NewGraphWidget> createState() => _NewGraphWidgetState();
}
class _NewGraphWidgetState extends State<NewGraphWidget> {
intl.DateFormat hmFormat = intl.DateFormat('Hm');
double _maxX = 0;
double _minX = 0;
double middle = 0;
// convert to second by / 1000 / 60
final data = [
FlSpot(1693811005000 / 1000 / 60, 7.59659),
FlSpot(1693810765000 / 1000 / 60, 8.74324),
FlSpot(1693810704000 / 1000 / 60, 10.8216),
FlSpot(1693810645000 / 1000 / 60, 40.8216),
FlSpot(1693810285000 / 1000 / 60, 6.87993),
FlSpot(1693810105000 / 1000 / 60, 5.94827),
FlSpot(1693809804000 / 1000 / 60, 7.02326),
FlSpot(1693809744000 / 1000 / 60, 7.09493),
FlSpot(1693809685000 / 1000 / 60, 6.0916),
FlSpot(1693809625000 / 1000 / 60, 0.15662),
];
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
_maxX = double.parse(data.map<double>((e) => (e.x)).reduce(max).toStringAsFixed(0));
_minX = double.parse(data.map<double>((e) => e.x).reduce(min).toStringAsFixed(0));
middle = double.parse(data[data.length ~/ 2].x.toStringAsFixed(0)) + 4;
return LineChart(
LineChartData(
maxY: 50,
minY: 0,
gridData: FlGridData(show: false),
borderData: FlBorderData(
show: true,
border: Border(
bottom: BorderSide(color: Colors.white.withOpacity(0.8), width: 2),
left: BorderSide(color: Colors.white.withOpacity(0.8), width: 2),
right: const BorderSide(color: Colors.transparent),
top: const BorderSide(color: Colors.transparent),
),
),
titlesData: FlTitlesData(
bottomTitles: getBottomTitles(),
topTitles: noTitlesWidget(),
leftTitles: getLeftTitles(),
rightTitles: noTitlesWidget(),
),
lineBarsData: [
LineChartBarData(
isCurved: true,
color: Colors.cyan,
barWidth: 2,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
spots: data,
),
],
),
);
}
AxisTitles getBottomTitles() {
return AxisTitles(
sideTitles: SideTitles(
interval: 1,
showTitles: true,
getTitlesWidget: (value, meta) {
String text = '';
if (value == _maxX) {
text = getDate(data.first.x);
} else if (double.parse(value.toStringAsFixed(0)) == _minX) {
text = getDate(data.last.x);
} else if (value == middle) {
text = getDate(middle);
}
return Text(text);
},
),
);
}
AxisTitles getLeftTitles() {
return AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 25,
getTitlesWidget: (value, meta) {
String text = '';
switch (value.toString()) {
case '50.0':
text = '50';
break;
case '25.0':
text = '25';
break;
case '0.0':
text = '0';
break;
}
return Text(text);
},
),
);
}
String getDate(double value) {
return hmFormat.format(DateTime.fromMillisecondsSinceEpoch((value * 1000 * 60).toInt()));
}
AxisTitles noTitlesWidget() {
return const AxisTitles(sideTitles: SideTitles());
}
}
any update or sample? i still don't know how to implement this