Working with Flutter graphs

Naveen Renati
4 min readJul 12, 2021

--

All the flutter developers out there at some point while working with graphs would have searched how to show selected points on the graph. I am one of you. Many responses in StackOverflow and GitHub and many trial and error methods took me to this.

This blog will implement that graph we all are trying to implement using some sample JSON data.

Getting Started

Let's create a flutter project and make a stateful class start with.

Implementation:

We will create a new dart file and name it Home and make it a Stateful class. Change your existing main.dart file as shown below

Let’s add a package called charts_flutter in pubspec and run flutter pub get.

Add the dependency

dependencies:
flutter:
sdk: flutter
charts_flutter: ^0.10.0

Run flutter packages get in the root directory of your app.

Import in Home class

import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;

Now let’s take a look at our sample JSON data to plot the graph

var response = {
"count": 30,
"next": null,
"previous": null,
"results": [
{"date": "2017-06-01T00:00:00+05:30", "price": "2848.55"},
{"date": "2017-07-01T00:00:00+05:30", "price": "2887.06"},
{"date": "2017-08-01T00:00:00+05:30", "price": "2984.83"},
]
};

Let’s create a model class for the above JSON response.

We need a simple class that helps in plotting the graph which contains two variables. I’ll be adding this in Home right after closing the homeclass.

class _GraphData {
final String date;
final String price;

_GraphData(this.date, this.price);
}

Now that we have the data and model class ready let’s start with the UI in the Home class. Before starting with UI lets create some variables which we need later in the process

List<_GraphData> _stockGraph = [];
List<GraphTimeData> graphData;

var rate, date;

NumberFormat simpleCurrencyFormat =
NumberFormat.simpleCurrency(locale: 'en_IN', decimalDigits: 2);

var stockImage =
'https://gumlet.assettype.com/swarajya%2F2019-05%2F4bbec1ee-80cf-460b-a1f2-ff388ed7eeea%2F576px_ITC_Limited_Logo_svg.png?auto=format&q=35&w=1200&rect=0,276,576,324';

The simpleCurrencyFormat is for the rupee symbol and limit to 2 decimal points.

Add a scaffold as a parent widget and give background color and add an appbar.

backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.black,
title: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 50,
height: 50,
color: Colors.white,
child: Image.network('$stockImage')),
SizedBox(
width: 10,
),
Text('Stock Price Growth'),
],
),
centerTitle: true,
),

Let’s add sample data required to plot the graph. We will create a method where we will be setting up the data to the graph.

We need to add sample JSON data. Create a file called JsonData and place the below code

In initState create a function called getGraphData();

void getGraphData() async {
GraphTimeDataList graphList = GraphTimeDataList.fromJson(response);
setState(() {
graphData = graphList.results;
this._stockGraph = _getStockGraphData(graphData);
});
}
}

To the body add a SingleChildScrollView and as a child, we will add a container to give height to the graph. Let’s take a look at the code below

Container(
height: MediaQuery.of(context).size.height * 0.3,
child: charts.TimeSeriesChart(
[
charts.Series<_GraphData, DateTime>(
id: 'Stock Price',
colorFn: (_, __) =>
charts.ColorUtil.fromDartColor(Colors.green),
domainFn: (_GraphData row, _) =>
DateTime.tryParse(row.date),
measureFn: (_GraphData row, _) => double.parse(row.price),
data: _stockGraph,
),
],
dateTimeFactory: const charts.LocalDateTimeFactory(),
animate: true,
defaultInteractions: true,
defaultRenderer: charts.LineRendererConfig(
strokeWidthPx: 3, radiusPx: 5, roundEndCaps: true),
selectionModels: [
charts.SelectionModelConfig(
updatedListener: (charts.SelectionModel model) {
if (model.hasDatumSelection) {
rate = model.selectedSeries.first
.measureFn(model.selectedDatum.first.index)
.toString();
date = formatDateAndTime(
model.selectedDatum.first.datum.date.toString());
ToolTipMgr.setTitle({
'title': '${simpleCurrencyFormat.format(double.parse(rate))} /gm',
'subTitle': '$date'
});
}
},
),
],
behaviors: [
charts.SelectNearest(
eventTrigger: charts.SelectionTrigger.tapAndDrag,
),
charts.LinePointHighlighter(
radiusPaddingPx: 3.0,
showVerticalFollowLine:
charts.LinePointHighlighterFollowLineType.nearest,
symbolRenderer: CustomRectangleSymbolRenderer(),
),
],
domainAxis: charts.DateTimeAxisSpec(
tickProviderSpec: charts.StaticDateTimeTickProviderSpec([
charts.TickSpec(toDateTimeGraph(graphData[0].date),
style: charts.TextStyleSpec(
fontSize: 11,
color: charts.ColorUtil.fromDartColor(Colors.white),
)),
charts.TickSpec(toDateTimeGraph(graphData[15].date),
style: charts.TextStyleSpec(
fontSize: 11,
color: charts.ColorUtil.fromDartColor(Colors.white),
)),
charts.TickSpec(toDateTimeGraph(graphData[30].date),
style: charts.TextStyleSpec(
fontSize: 11,
color: charts.ColorUtil.fromDartColor(Colors.white),
)),
charts.TickSpec(toDateTimeGraph(graphData[45].date),
style: charts.TextStyleSpec(
fontSize: 11,
color: charts.ColorUtil.fromDartColor(Colors.white),
)),
]),
renderSpec: charts.GridlineRendererSpec(
labelAnchor: charts.TickLabelAnchor.inside,
lineStyle: charts.LineStyleSpec(
color: charts.ColorUtil.fromDartColor(
Colors.transparent))),
showAxisLine: true),
animationDuration: Duration(milliseconds: 700),
primaryMeasureAxis: charts.NumericAxisSpec(
tickProviderSpec: charts.BasicNumericTickProviderSpec(
zeroBound: false, desiredTickCount: 4),
showAxisLine: true,
renderSpec: charts.GridlineRendererSpec(
labelAnchor: charts.TickLabelAnchor.centered,
labelStyle: charts.TextStyleSpec(
fontSize: 10,
color: charts.ColorUtil.fromDartColor(Colors.white)),
lineStyle: charts.LineStyleSpec(
color: charts.ColorUtil.fromDartColor(
Colors.transparent))),
),
),
)

You can play around with the animation on and off, animation duration, the color of the plotline, radius of it, and so on.

Now that plotting is done let’s figure out how to show data on the tap of the graph. This is where behaviors and selectionModels come into the picture. Behaviors are where we define the action such as tap or tap and drag.

behaviors: [
charts.SelectNearest(
eventTrigger: charts.SelectionTrigger.tapAndDrag,
),
charts.LinePointHighlighter(
radiusPaddingPx: 3.0,
showVerticalFollowLine:
charts.LinePointHighlighterFollowLineType.nearest,
symbolRenderer: CustomRectangleSymbolRenderer(),
),
],

CustomRectangleSymbolRenderer is used to draw the rectangle and the text which is the selected data from the graph. Let’s take a look at the code of CustomRectangleSymbolRenderer

The position and the dimensions of the shape and the text can be adjusted using the canvas-bound values.

selectionModels gives us the data of the selected values through updatedListener

selectionModels: [
charts.SelectionModelConfig(
updatedListener: (charts.SelectionModel model) {
if (model.hasDatumSelection) {
rate = model.selectedSeries.first
.measureFn(model.selectedDatum.first.index)
.toString();
date = formatDateAndTime(
model.selectedDatum.first.datum.date.toString());
ToolTipMgr.setTitle({
'title': '${simpleCurrencyFormat.format(double.parse(rate))} /gm',
'subTitle': '$date'
});
}
},
),
],

That’s it now you have the graph with sample data and the interaction with feedback. Let me know in the comments if this worked out for you.

In this article, I shared with you my approach of working with graphs and customizing them according to your requirement. Hope you liked this article. Please leave a clap and a comment below your thoughts about the article.

--

--

Naveen Renati
Naveen Renati

Written by Naveen Renati

Accomplished Senior Flutter Developer in a fast-paced startup. Versatile in UI/UX, product management, research, and multiple tech stacks.

Responses (1)