How to show graph y-value on chart_flutter hover? - flutter

I rendered a time series line graph with charts_flutter. Is there a way to display the chart's y-value when the user hovers over the chart (I think this is a common graph feature). Here's my current code for simply displaying the chart:
Widget _graph() {
//
List<TimeSeriesSales> graphData = ...;
//
return Container(
child: charts.TimeSeriesChart(
[
charts.Series<TimeSeriesSales, DateTime>(
id: 'Sales',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (TimeSeriesSales sales, _) => sales.time,
measureFn: (TimeSeriesSales sales, _) => sales.sales,
data: graphData,
)
],
animate: false,
dateTimeFactory: const charts.LocalDateTimeFactory(),
),
);
}
class TimeSeriesSales {
final DateTime time;
final int sales;
TimeSeriesSales(this.time, this.sales);
}

Related

Line chart representation of List containing double values

I have Lists of values containing double type numbers and each of this lists contains about 5000 values. Each of this lists has been collected from a microcontroller within 60 seconds.
I want to create a line chart to show the variance of this values. I tried to use the Spline Chart in Flutter but I cannot manage to make it read values from a list as its throwing this error:
The return type 'List?' isn't a 'num?', as required by the closure's context.
and here is the code:
Widget build(BuildContext context) {
final List<ChartData> chartData = [
ChartData(10, [5.5,0.6]),
ChartData(20, [2.5,0.6]),
ChartData(30, [1.5,0.6]),
ChartData(40, [0.5,0.6]),
ChartData(50, [0.5,3.6]),
ChartData(60, [0.5,2.6]),
ChartData(70, [0.5,1.6]),
ChartData(80, [0.5,0.63]),
ChartData(90, [0.5,0.126]),
];
return Scaffold(
body: Center(
child: Container(
child: SfCartesianChart(
series: <ChartSeries>[
// Renders spline chart
SplineSeries<ChartData, int>(
dataSource: chartData,
xValueMapper: (ChartData data, _) => data.x,
yValueMapper: (ChartData data, _) => data.y
)
]
)
)
)
);
}
}
class ChartData {
ChartData(this.x, this.y);
final int x;
final List? y;
}
I tried to create a list that has few values and called some elements from each list to the chart. It looked like this:
List x =[1.0,1.2,1.3,1.5,1.9,1.8,1.9,2.9,3.9,1.9,1.8,1.4];
#override
Widget build(BuildContext context) {
final List<ChartData> chartData = [
ChartData(10, x[1]),
ChartData(20, x[2]),
ChartData(30, x[0]),
ChartData(40, x[4]),
ChartData(50, x[5]),
ChartData(60, x[6]),
ChartData(70, x[7]),
ChartData(80, x[8]),
ChartData(90, x[9]),
];
And this worked for me but Can I make it so I can call all the elemnts from the list to the chart.
Do you have any suggestion on how to make this thing happens.
thank you in advance
Your y in ChartData is List. And you try to assign it to num?
yValueMapper: (ChartData data, _) => data.y
If the length of y is not fixed, you can use this function to generate SplineSeries
List<SplineSeries> generateSplineSeries(List<ChartData> chartData){
List<SplineSeries> splines = [];
for(int i=0; i<chartData.first.y!.length; i++){
splines.add( SplineSeries<ChartData, int>(
dataSource: chartData,
xValueMapper: (ChartData data, _) => data.x,
yValueMapper: (ChartData data, _) => data.y![i],
));
}
return splines;
}
Widget build(BuildContext context) {
final List<ChartData> chartData = [
ChartData(10, [5.5,0.6]),
ChartData(20, [2.5,0.6]),
ChartData(30, [1.5,0.6]),
ChartData(40, [0.5,0.6]),
ChartData(50, [0.5,3.6]),
ChartData(60, [0.5,2.6]),
ChartData(70, [0.5,1.6]),
ChartData(80, [0.5,0.63]),
ChartData(90, [0.5,0.126]),
];
return Scaffold(
body: Center(
child: Container(
child: SfCartesianChart(
series: generateSplineSeries(chartData),
)
)
)
);
}
#override
Widget build(BuildContext context) {
List x =[10.0,1.2,1.3,1.5,1.9,1.8,1.9,2.9,3.9,1.9,1.8,1.4];
final List<ChartData> chartData = [
ChartData(60,x),
];
List<SplineSeries> generateSplineSeries(List<ChartData> chartData){
List<SplineSeries> splines = [];
for(int i=0; i<chartData.first.y!.length; i++){
splines.add( SplineSeries<ChartData, int>(
dataSource: chartData,
xValueMapper: (ChartData data, _) => data.x,
yValueMapper: (ChartData data, _) => data.y![i],
));
}
return splines;
}
return Scaffold(
body: Center(
child: Container(
child: SfCartesianChart(
series: generateSplineSeries(chartData),
)
)
)
);
}
}
class ChartData {
ChartData(this.x, this.y);
final int x;
final List? y;
}

How to set interval on y-axis in flutter charts?

Here's my code. I have a graph ranging values from 110-130. I need to set my y-axis from 100-140 not from 0-140. Anyone out there , please help me. I have attached my code below. I'm new to flutter.I have used desired tickcount property, it doesn't helped me. Is there any way to set bounds not just for y-axis but both the axis.
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart';
class SimpleTimeSeriesChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
SimpleTimeSeriesChart(this.seriesList, {this.animate});
#override
Widget build(BuildContext context) {
return new charts.TimeSeriesChart(
seriesList,
animate: animate,
dateTimeFactory: const charts.LocalDateTimeFactory(),
behaviors: [
charts.SlidingViewport(
charts.SelectionModelType.action,
),
charts.PanBehavior(),
],
primaryMeasureAxis: new charts.NumericAxisSpec(
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
desiredTickCount: 10,
),
),
);
}
static List<charts.Series<TimeSeriesSales, DateTime>> _createSampleData() {
List<TimeSeriesSales> data1 = [];
List<TimeSeriesSales> data2 = [];
var index = 0;
for (var item in DataMarket) {
data1.insert(index++, TimeSeriesSales(item['date'], item['market']));
}
index = 0;
for (var item in DataMarket) {
data2.insert(index++, TimeSeriesSales(item['date'], item['NAV']));
}
print(data1);
return [
new charts.Series<TimeSeriesSales, DateTime>(
id: 'Sales1',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (TimeSeriesSales sales, _) => sales.time,
measureFn: (TimeSeriesSales sales, _) => sales.sales,
measureLowerBoundFn: (TimeSeriesSales sales, _) => sales.sales - 5,
measureUpperBoundFn: (TimeSeriesSales sales, _) => sales.sales + 5,
data: data1,
),
new charts.Series<TimeSeriesSales, DateTime>(
id: 'Sales2',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
domainFn: (TimeSeriesSales sales, _) => sales.time,
measureFn: (TimeSeriesSales sales, _) => sales.sales,
data: data2,
)
];
}
}
/// Sample time series data type.
class TimeSeriesSales {
final DateTime time;
final double sales;
TimeSeriesSales(this.time, this.sales);
}
Add this property to your chart:
primaryMeasureAxis: new charts.NumericAxisSpec(tickProviderSpec:
new charts.BasicNumericTickProviderSpec(zeroBound: false))
Full example:
charts.LineChart(seriesList,primaryMeasureAxis: charts.NumericAxisSpec(
tickProviderSpec:charts.BasicNumericTickProviderSpec(zeroBound: false)))

Flutter - Using API for charts with model

i've got stuck at this problem for nearly 3 days. Already post it in another questions but, got minus instead an answer.
Let me make it clear the questions.
I would like to put my api data using models into chart flutter, here is my code
First is my model here is my age model
ages.dart
Ages agesFromJson(String str) => Ages.fromJson(json.decode(str));
String agesToJson(Ages data) => json.encode(data.toJson());
class Ages {
Ages({
this.entity,
this.code,
this.year,
this.underAge15,
this.age1564,
this.age65Over,
});
String entity;
String code;
int year;
String underAge15;
String age1564;
String age65Over;
factory Ages.fromJson(Map<String, dynamic> json) => Ages(
entity: json["entity"],
code: json["code"],
year: json["year"],
underAge15: json["under_age_15"],
age1564: json["age_15_64"],
age65Over: json["age_65_over"],
);
Map<String, dynamic> toJson() => {
"entity": entity,
"code": code,
"year": year,
"under_age_15": underAge15,
"age_15_64": age1564,
"age_65_over": age65Over,
};
}
and here i make the ages chart that given me the url from api and put into future builder
ages_chart.dart
class AgeCharts extends StatefulWidget {
final String url;
const AgeCharts({this.url});
#override
_AgeChartsState createState() => _AgeChartsState();
}
class _AgeChartsState extends State<AgeCharts> {
var chart;
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
print(widget.url);
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Japanese Age Working Populations'),
),
body: Container(
height: 400,
child: FutureBuilder<List>(
future: getChartData(widget),
builder: (context, dataapi) {
if (dataapi.hasError) print(dataapi.error);
print(dataapi);
return dataapi.hasData
? ShowChart(
data: dataapi.data,
)
: Center(
child: CircularProgressIndicator(),
);
},
),
),
),
);
}
}
after ages dart, i make a function to get an api.
Future<List> getChartData(widget) async {
final response = await http.get(widget.url);
return json.decode(response.body)['data'];
}
and here is my show chart when i already get an url
class ShowChart extends StatelessWidget {
final List data;
ShowChart({this.data});
static List<charts.Series<Ages, dynamic>> _createSampleData(dataAPI) {
return [
new charts.Series<Ages, dynamic>(
id: 'Desktop',
// colorFn specifies that the line will be blue.
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
// areaColorFn specifies that the area skirt will be light blue.
areaColorFn: (_, __) =>
charts.MaterialPalette.blue.shadeDefault.lighter,
domainFn: (Ages ages, _) => ages.year,
measureFn: (Ages ages, _) => int.parse(ages.underAge15),
data: dataAPI,
),
new charts.Series<Ages, dynamic>(
id: 'Tablet',
// colorFn specifies that the line will be red.
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
// areaColorFn specifies that the area skirt will be light red.
areaColorFn: (_, __) => charts.MaterialPalette.red.shadeDefault.lighter,
domainFn: (Ages ages, _) => ages.year,
measureFn: (Ages ages, _) => int.parse(ages.age1564),
data: dataAPI,
),
new charts.Series<Ages, dynamic>(
id: 'Mobile',
// colorFn specifies that the line will be green.
colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault,
// areaColorFn specifies that the area skirt will be light green.
areaColorFn: (_, __) =>
charts.MaterialPalette.green.shadeDefault.lighter,
domainFn: (Ages ages, _) => ages.year,
measureFn: (Ages ages, _) => int.parse(ages.age65Over),
data: dataAPI,
),
];
}
#override
Widget build(BuildContext context) {
print('showchart');
print(data);
return Container(
child: charts.LineChart(
_createSampleData(data),
defaultRenderer:
new charts.LineRendererConfig(includeArea: true, stacked: true),
animate: true,
),
);
}
}
this is my JSON Structure. but i get the data when using json.decode(response.body)
{
"status": true,
"message": "Chart found",
"data": [
{
"entity": "Japan",
"code": "JPN",
"year": 1950,
"under_age_15": "29288.106",
"age_15_64": "49447.555",
"age_65_over": "4066.423"
},
{
"entity": "Japan",
"code": "JPN",
"year": 1951,
"under_age_15": "29652.515",
"age_15_64": "50461.828",
"age_65_over": "4201.922"
},
]
}
and the i got an error like this
type 'List<dynamic>' is not a subtype of type 'List<Ages>'
help me solve the problem, please
Problems within your code:
You are explicitly casting your series data as charts.Series<Ages, dynamic> while it should be charts.Series<Ages, num>
You are trying to parse double as int. If you want doubles, use double.parse(x) otherwise, double.parse(x).round() will give you a int. (or .floor() or .ceil())
You should probably also specify your domain axis as not being zeroBound:
charts.LineChart(
_createSampleData(data),
defaultRenderer: charts.LineRendererConfig(includeArea: true, stacked: true),
animate: true,
domainAxis: charts.NumericAxisSpec(
tickProviderSpec: charts.BasicNumericTickProviderSpec(zeroBound: false),
),
),
Working Solution
Full source code for easy copy-paste
import 'dart:math' as math;
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Charts Demo',
home:
AgeCharts(url: 'http://udacoding-task-api.herokuapp.com/api/charts'),
),
);
}
// MAIN WIDGET
class AgeCharts extends StatefulWidget {
final String url;
const AgeCharts({this.url});
#override
_AgeChartsState createState() => _AgeChartsState();
}
class _AgeChartsState extends State<AgeCharts> {
var chart;
Future<List<Ages>> getChartData(widget) async {
final response = await http.get(widget.url);
final List<dynamic> jsonData = jsonDecode(response.body)['data'];
return jsonData.map((data) => Ages.fromJson(data)).toList();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Japanese Age Working Populations'),
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: FutureBuilder<List>(
future: getChartData(widget),
builder: (context, dataapi) {
if (dataapi.hasError) print(dataapi.error);
return dataapi.hasData
? ShowChart(data: dataapi.data)
: Center(child: CircularProgressIndicator());
},
),
),
),
);
}
}
// CHART
class ShowChart extends StatelessWidget {
final List<Ages> data;
ShowChart({this.data});
static List<charts.Series<Ages, num>> _createSampleData(dataAPI) {
return [
new charts.Series<Ages, num>(
id: 'underAge15',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
areaColorFn: (_, __) =>
charts.MaterialPalette.blue.shadeDefault.lighter,
domainFn: (Ages ages, _) => ages.year,
measureFn: (Ages ages, _) => double.parse(ages.underAge15).round(),
data: dataAPI,
),
new charts.Series<Ages, num>(
id: 'age1564',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
areaColorFn: (_, __) => charts.MaterialPalette.red.shadeDefault.lighter,
domainFn: (Ages ages, _) => ages.year,
measureFn: (Ages ages, _) => double.parse(ages.age1564).round(),
data: dataAPI,
),
new charts.Series<Ages, num>(
id: 'age65Over',
colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault,
areaColorFn: (_, __) =>
charts.MaterialPalette.green.shadeDefault.lighter,
domainFn: (Ages ages, _) => ages.year,
measureFn: (Ages ages, _) => double.parse(ages.age65Over).round(),
data: dataAPI,
),
];
}
#override
Widget build(BuildContext context) {
return Container(
child: charts.LineChart(
_createSampleData(data),
defaultRenderer:
new charts.LineRendererConfig(includeArea: true, stacked: true),
animate: true,
domainAxis: charts.NumericAxisSpec(
tickProviderSpec:
charts.BasicNumericTickProviderSpec(zeroBound: false),
),
),
);
}
}
// DOMAIN
class Ages {
Ages({
this.entity,
this.code,
this.year,
this.underAge15,
this.age1564,
this.age65Over,
});
String entity;
String code;
int year;
String underAge15;
String age1564;
String age65Over;
factory Ages.fromJson(Map<String, dynamic> json) => Ages(
entity: json["entity"],
code: json["code"],
year: json["year"],
underAge15: json["under_age_15"],
age1564: json["age_15_64"],
age65Over: json["age_65_over"],
);
Map<String, dynamic> toJson() => {
"entity": entity,
"code": code,
"year": year,
"under_age_15": underAge15,
"age_15_64": age1564,
"age_65_over": age65Over,
};
}
// DATA
math.Random random = math.Random();
final Map<String, dynamic> data = {
"status": true,
"message": "Chart found",
"data": List.generate(
20,
(index) => {
"entity": "Japan",
"code": "JPN",
"year": 1950 + index,
"under_age_15": (25000 + random.nextInt(5000)).toString(),
"age_15_64": (40000 + random.nextInt(5000)).toString(),
"age_65_over": (3000 + random.nextInt(2000)).toString(),
}).toList(),
};
BEFORE ANSWER UPDATE:
Try this:
Future<List<Ages>> getChartData(widget) async {
final response = await http.get(widget.url);
return json.decode(response.body)['data'].map((jsonData) => Ages.fromJson(jsonData)).toList();
}
I'm not 100% sure, since I don't know the structure of your JSON Data.

Why I cant make a class string as domainFn for pie chart in charts_flutter

Hi there I am trying to make Piechart base on its example
But I change Just one thing, I changed
final int year
to
final String year
but for domainFn Im getting this error:
{
"resource": "/home/saeb/AndroidStudioProjects/charts/lib/main.dart",
"owner": "dart",
"code": "return_of_invalid_type_from_closure",
"severity": 8,
"message": "The return type 'String' isn't a 'int', as defined by anonymous closure.",
"source": "dart",
"startLineNumber": 36,
"startColumn": 45,
"endLineNumber": 36,
"endColumn": 55,
"tags": []
}
my label should be string not int, how I can do that?
my code:
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart';
class PieOutsideLabelChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
PieOutsideLabelChart(this.seriesList, {this.animate});
factory PieOutsideLabelChart.withSampleData() {
return new PieOutsideLabelChart(
_createSampleData(),
animate: false,
);
}
#override
Widget build(BuildContext context) {
return new charts.PieChart(seriesList,
animate: animate,
defaultRenderer: new charts.ArcRendererConfig(arcRendererDecorators: [
new charts.ArcLabelDecorator(
labelPosition: charts.ArcLabelPosition.outside)
]));
}
static List<charts.Series<LinearSales, int>> _createSampleData() {
final data = [
new LinearSales('a long text', 100),
new LinearSales('even longer text', 75),
new LinearSales('i am long text', 25),
new LinearSales('the longest text ever', 5),
];
return [
new charts.Series<LinearSales, int>(
id: 'Sales',
domainFn: (LinearSales sales, _) => sales.year,
measureFn: (LinearSales sales, _) => sales.sales,
data: data,
labelAccessorFn: (LinearSales row, _) => '${row.year}: ${row.sales}',
)
];
}
}
class LinearSales {
final String year;
final int sales;
LinearSales(this.year, this.sales);
}
main(List<String> args) {
runApp(MaterialApp(
title: 'charts',
home: new Scaffold(
appBar: new AppBar(title: new Text("نمودار فلان"),),
body:new ListView(children: <Widget>[
new Card
(child: PieOutsideLabelChart(PieOutsideLabelChart._createSampleData()),)
// ]
// ),
]
)
)
)
);
}
It was syntax problem. my bad :D
I should change my charts.Series to charts.Series<LinearSales, **String**>> from charts.Series<LinearSales, **int**>>
this code works:
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart';
class PieOutsideLabelChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
PieOutsideLabelChart(this.seriesList, {this.animate});
factory PieOutsideLabelChart.withSampleData() {
return new PieOutsideLabelChart(
_createSampleData(),
animate: false,
);
}
#override
Widget build(BuildContext context) {
return new charts.PieChart(seriesList,
animate: animate,
defaultRenderer: new charts.ArcRendererConfig(arcRendererDecorators: [
new charts.ArcLabelDecorator(
labelPosition: charts.ArcLabelPosition.outside)
]));
}
static List<charts.Series<LinearSales, String>> _createSampleData() {
final data = [
new LinearSales('a long text', 100),
new LinearSales('even longer text', 75),
new LinearSales('i am long text', 25),
new LinearSales('the longest text ever', 5),
];
return [
new charts.Series<LinearSales, String>(
id: 'Sales',
domainFn: (LinearSales sales, _) => sales.year,
measureFn: (LinearSales sales, _) => sales.sales,
data: data,
labelAccessorFn: (LinearSales row, _) => '${row.year}',
)
];
}
}
class LinearSales {
final String year;
final int sales;
LinearSales(this.year, this.sales);
}
main(List<String> args) {
runApp(MaterialApp(
title: 'charts',
home: new Scaffold(
appBar: new AppBar(title: new Text("نمودار فلان"),),
body: PieOutsideLabelChart(PieOutsideLabelChart._createSampleData()),)
)
);
}
you can make a class string as domainFn for pie chart in charts_flutter.
it will take some adjustments
Step 1: Add this to your chart so it gonna show label on outside of pie chart
defaultRenderer: new charts.ArcRendererConfig(
arcWidth: 60,
arcRendererDecorators: [new charts.ArcLabelDecorator(
labelPosition: charts.ArcLabelPosition.outside)]
),
Step 2 : Now for customization of those label with string there on property.
which is labelAccessorFn
String datas(int id,int value){
if(id==0){
return value.toString()+":RT";
}else if(id==1){
return value.toString()+":OT";
}
else if(id==2){
return value.toString()+":IT";
}
return [
new charts.Series<VfmProductivityPie, int>(
id: 'Sales',
domainFn: (VfmProductivityPie sales, _) => sales.id,
measureFn: (VfmProductivityPie sales, _) => sales.value,
data: data,
labelAccessorFn: (VfmProductivityPie row, _) =>
// print('object'),
//'${row.id}r: ${row.value}',
datas(row.id,row.value)
)
];
Note: write data function under _createSampleDataworking screenshot of piechart
Its very simple:
set year variable to int again:
final int year
and here use toString() method:
labelAccessorFn: (LinearSales row, _) => '${row.year.toString()}: ${row.sales}',

Pie chart through API call in Flutter

I am trying to display some data in a pie chart that is coming from an API but the problem is that how to apply the JSON data into a pie chart.
I've created a custom json for this.
Async function to get data from API
_getData() async{
final response = await http.get('https://api.myjson.com/bins/16e68w');
Map<String,dynamic> map = json.decode(response.body);
return map;
}
Returns json response as a Map<String, dynamic>. I didn't parse json.
Scaffold(
body: FutureBuilder(
future: _getData(),
builder: (BuildContext context,AsyncSnapshot snapshot){
if(snapshot.connectionState == ConnectionState.done)
return new charts.PieChart(
dataList(snapshot.data),
defaultRenderer: new charts.ArcRendererConfig(
arcRendererDecorators: [new charts.ArcLabelDecorator()])
);
else
return Center(child: CircularProgressIndicator());
}
),
);
Format your data in charts.Series list.
We'll generate a list of LinearSales object for data.
static List<charts.Series<LinearSales, int>> dataList(Map<String, dynamic> apiData) {
List<LinearSales> list = new List();
for(int i=0; i<apiData['data'].length; i++)
list.add(new LinearSales(i, apiData['data'][i]));
return [
new charts.Series<LinearSales, int>(
id: 'Sales',
domainFn: (LinearSales sales, _) => sales.year,
measureFn: (LinearSales sales, _) => sales.sales,
data: list,
labelAccessorFn: (LinearSales row, _) => '${row.year}: ${row.sales}',
)
];
}
}
class LinearSales {
final int year;
final int sales;
LinearSales(this.year, this.sales);
}
Final Result