Hello guys! Welcome to my new blog! Today we will be talking about charts in Flutter.
We will be discussing simple graphs in Flutter, how to customize them according to your needs, and discuss a use-case step by step.
The entire code discussed can be found in the repository given below.
Here is a little preview of what we will be building today

Given below is the outline of the article.
Dependencies
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
fl_chart: ^0.62.0
charts_flutter: ^0.12.0
1. Line Graph
The fl_charts library provides us with an easier way to build a line graph. Let us look at the code below.
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
class LineChartWidget extends StatefulWidget {
const LineChartWidget({Key? key}) : super(key: key);
@override
State<LineChartWidget> createState() => _LineChartWidgetState();
}
class _LineChartWidgetState extends State<LineChartWidget> {
@override
Widget build(BuildContext context) { }
}
In the code block above, we have created a stateful widget named LineChartWidget(). Here, the widget does not return anything yet, which would result in an error, thus let’s return something and slowly start building our widget.
@override
Widget build(BuildContext context) {
return SizedBox(width: 370, height: 300, child: )
}
Here we return a SizedBox( ) with the given height and width. You can change the dimensions according to your needs.
This SizedBox( ) still requires a child, here we access the LineChart( ) from fl_charts to build our line graph.
A. Line chart
The LineChart( ) widget has the following parameters
LineChartData data, {
Duration swapAnimationDuration = const Duration(milliseconds: 150),
Curve swapAnimationCurve = Curves.linear,
}
The LineChart( ) widget accepts a LineChartData argument which further contains arguments to customize your line chart.
Every time the state of your line chart changes, or the data fed to it changes, the chart is rebuilt using the animation curve specified in the Curve argument within the Duration specified. We will discuss these properties in Animating Graphs.
B. Line chart data
Further, let’s look at LineChartData. We use lineBarsData to customize our line chart further.
lineBarsData: [ LineChartBarData(spots: points, color: Colors.red), ],
In the lineBarsData, we further define LineChartBarData( ). spots: accepts a list of FlPoints<>, which is the list of points to be plotted on the chart. color specifies the color of the line plot on the chart.
points list is a hardcoded list of points to be plotted on the chart. It is defined below.
List<FlSpot> points = const [
FlSpot(0, 1), FlSpot(1, 3), FlSpot(2, 10), FlSpot(3, 7), FlSpot(4, 12),
FlSpot(5, 13), FlSpot(6, 17), FlSpot(7, 15), FlSpot(8, 20),
];
Further, LineChartBarData( ) has the following arguments.
show | determines to show or hide the bar line |
gradient | You can use any Gradient here. such as LinearGradient or RadialGradient |
barWidth | gets the stroke width of the line bar |
isCurved | curves the corners of the line on the spot’s positions |
curveSmoothness | smoothness radius of the curve corners (works when isCurved is true) |
preventCurveOverShooting | prevent overshooting when draw curve line on linear sequence spots. |
preventCurveOvershootingThreshold | threshold for applying prevent overshooting algorithm |
isStrokeCapRound | determines whether start and end of the bar line is Qubic or Round |
isStrokeJoinRound | determines whether stroke joins have a round shape or a sharp edge |
showingIndicators | show indicators based on provided indexes |
dashArray | A circular array of dash offsets and lengths. |
shadow | It drops a shadow behind your bar |
isStepLineChart | If sets true, it draws the chart in Step Line Chart style, using lineChartStepData |
lineChartStepData | Holds data for representing a Step Line Chart, and works only if [isStepChart] is true. |
Now, lets apply borders to our chart using the borderData property.
C. Border data
borderData: FlBorderData(
border: const Border(bottom: BorderSide(), left: BorderSide()),
),
While applying border, you can determine the sides you want to apply border to. Further, you can access top and right arguments to apply order to top and right sides of the chart.
D. Titles data
It draws some titles on left, top, right, bottom sides per each axis number, you can modify titlesData to have your custom titles.
titlesData: FlTitlesData(
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
rightTitles and topTitles are hidden using showTitles property. Thus only bottom and top sides have titles.
E. Grid data
gridData is usde to customize each grid in your chart. For our use case, we have not implemented grids, but you can customize grids as per required.
gridData: FlGridData(show: false)
Thus our code looks like
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
class LineChartWidget extends StatefulWidget {
const LineChartWidget({Key? key}) : super(key: key);
@override
State<LineChartWidget> createState() => _LineChartWidgetState();
}
class _LineChartWidgetState extends State<LineChartWidget> {
List<FlSpot> points = const [
FlSpot(0, 1), FlSpot(1, 3), FlSpot(2, 10), FlSpot(3, 7), FlSpot(4, 12),
FlSpot(5, 13), FlSpot(6, 17), FlSpot(7, 15), FlSpot(8, 20),
];
List<FlSpot> points2 = const [
FlSpot(1, 0), FlSpot(3, 1), FlSpot(10, 2), FlSpot(7, 3), FlSpot(12, 4),
FlSpot(13, 5), FlSpot(17, 6), FlSpot(15, 7), FlSpot(20, 8),
]
List<FlSpot> points3 = const [
FlSpot(2, 18), FlSpot(1, 17), FlSpot(7, 16), FlSpot(3, 15), FlSpot(2, 14),
FlSpot(2, 4), FlSpot(1, 4), FlSpot(1, 3), FlSpot(0, 1),
];
@override
Widget build(BuildContext context) {
return SizedBox(
width: 370,
height: 300,
child: LineChart(
LineChartData(
lineBarsData: [
LineChartBarData(spots: points, color: Colors.red),
],
borderData: FlBorderData(
border: const Border(bottom: BorderSide(), left: BorderSide()),
),
titlesData: FlTitlesData(
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
gridData: FlGridData( show: false)
),
),
);
}
}
Our line chart looks like:

2. Bar Graph
The fl_charts library provides us with an easier way to build a bar graph. Let us look at the code below.
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
class BarChartWidget extends StatefulWidget {
const BarChartWidget({Key? key}) : super(key: key);
@override
State<BarChartWidget> createState() => _BarChartWidgetState();
}
class _BarChartWidgetState extends State<BarChartWidget> {
@override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
width: 370,
child:
)
}
In the code block above, we have created a stateful widget named BarChartWidget( ). Here, the widget does not return anything yet, which would result in an error, thus let’s return something and slowly start building our widget.
Here we return a SizedBox( ) with the given height and width. You can change the dimensions according to your needs.
This SizedBox( ) still requires a child, here we access the BarChart( ) from fl_charts to build our line graph.
@override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
width: 370,
child: BarChart(
)
}
A. Bar chart
The BarChart( ) widget has the following parameters
BarChart BarChart(
BarChartData data,
{
Duration swapAnimationDuration = const
Duration(milliseconds: 150),
Curve swapAnimationCurve = Curves.linear,
})
The BarChart( ) widget accepts a BarChartData argument which further contains arguments to customize your line chart.
Every time the state of your line chart changes, or the data fed to it changes, the chart is rebuilt using the animation curve specified in the Curve argument within the Duration specified. We will discuss these properties in Animating Graphs.
B. Bar chart data
Further, let’s look at BarChartData to customize our bar chart further.
BarChartData(
alignment: BarChartAlignment.center,
groupsSpace: 32,
barGroups: groups
)
The alignment property aligns the bars in the bar chart on the plot.
groupSpaces space between groups applies only when alignment is center.
barGroups accepts the points to be plotted on the chart.
groups contain the list of points as shown below.
List<BarChartGroupData> groups = [
BarChartGroupData(
x: 0,
barRods: [
BarChartRodData(
toY: 1,
),
]
),
]
You can add multiple points in the similar way as required for your use case.
Each point is wrapped in a BarChartGroupData( ). x takes in the value of the x coordinate of the point and toY takes the y coordinate of the point.
BarChartRodData( ) further contains data to customize the bar in the bar chart. You can access these values to change the style of the bar chart.
fromY | Position that this bar starts from |
color | color of the rod bar |
gradient | You can use any Gradient here. such as LinearGradient or RadialGradient |
width | stroke width of the rod bar |
borderRadius | Determines the edge rounding of the bar corners, see BorderRadius. When null , it defaults to completely round bars. |
borderSide | Determines the border stroke around of the bar, see BorderSide. When null , it defaults to draw no stroke. |
backDrawRodData | if provided, draws a rod in the background of the line bar, check the BackgroundBarChartRodData |
rodStackItem | if you want to have stacked bar chart, provide a list of BarChartRodStackItem, it will draw over your rod. |
We can access the borderData( ), titlesData( ), and gridData( ) similar to the line chart as discussed.
Our bar chart looks like

3. Animating Graphs
To witness animation in a graph, it is important for the state of a graph to change. Thus in our home_screen.dart file, where we draw the entire layout, we add buttons to do so.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'1) Line Chart',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20
),
),
TextButton(
onPressed: change,
child: const Text(
'Animate',
style: TextStyle(color: Colors.blue),)
)
],
),
We have made a text button to change the state of the chart. The change function is specified below.
int animate = 1;
void change(){
setState((){
animate == 1 ? animate = 2 : animate = 1;
});
}
Now this animate value as fed to both the charts and the state changes as the data points change corresponding to the value toggled.
int a;
LineChartWidget({required this.a, Key? key}) : super(key: key);
Now changing the data points.
LineChartBarData(
spots: widget.a == 1? points : points2,
color: Colors.red
),
Specifying the animation below
swapAnimationCurve: Curves.easeIn,
swapAnimationDuration: const Duration(seconds: 1),
The same logic is applied in the bar chart widget. You can change the curves and duration values as per your requirements.
Our graphs when animating look like this:
4. Nested Graphs
We can further modify our graphs to cater to different use cases. Use cases that we will discuss are Multi-line graph and Bar-Line graph
A. Multi-line chart
We can build graphs with multiple lines to display different sets of points with different lines.
Analyze the code below
lineBarsData: [
LineChartBarData(
spots: widget.a == 1? points : points2,
color: Colors.red
),
LineChartBarData(
spots: points2,
color: Colors.green
),
LineChartBarData(
spots: points3,
color: Colors.blue
),
],
In the lineBarsData, we can add multiple lines, by creating multiple LineChartBarData( ) instances with different sets of points. You can change the color of the line plots to enhance visualization.
This is how our multiline graph looks like

B. Bar-line graph
To plot bar and line in a same graph, we will discuss 2 approaches.
1. Using Stack
We can separately create 2 widgets, a bar graph and a line graph, and can superimpose them using a stack widget.
Analyse the code below
Stack( children: [ BarChartWidget(a: animate), LineChartWidget(a: animate,), ], ),
It returns the following output.

The above approach results in a desired output, but it requires a lot of size and configuration changes. The numbers on the axis might not superimpose correctly, thus distorting the chart as seen above.
2. Using OrdinalComboBarLineChart
Using the chatrs_flutter package, we can use OrdinalComboBarLineChart( ) widget, to plot a bar and a line in the same chart.
The charts_flutter package has been deprecated. Thus, check before its usage.
Firstly, we create a sample Ordinal Class.
class OrdinalSales {
final String year;
final int sales;
OrdinalSales(this.year, this.sales);
}
Further, we create OrdinalSales objects to create our data.
final desktopSalesData = [
OrdinalSales('2014', 5),
OrdinalSales('2015', 25),
OrdinalSales('2016', 100),
OrdinalSales('2017', 75),
];
final tableSalesData = [
OrdinalSales('2014', 5),
OrdinalSales('2015', 25),
OrdinalSales('2016', 100),
OrdinalSales('2017', 75),
];
final mobileSalesData = [
OrdinalSales('2014', 10),
OrdinalSales('2015', 50),
OrdinalSales('2016', 200),
OrdinalSales('2017', 150),
];
Now, we create our plots for the corresponding data values.
return [
charts.Series<OrdinalSales, String>(
id: 'Desktop',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (OrdinalSales sales, _) => sales.year,
measureFn: (OrdinalSales sales, _) => sales.sales,
data: desktopSalesData),
charts.Series<OrdinalSales, String>(
id: 'Tablet',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
domainFn: (OrdinalSales sales, _) => sales.year,
measureFn: (OrdinalSales sales, _) => sales.sales,
data: tableSalesData),
charts.Series<OrdinalSales, String>(
id: 'Mobile ',
colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault,
domainFn: (OrdinalSales sales, _) => sales.year,
measureFn: (OrdinalSales sales, _) => sales.sales,
data: mobileSalesData)
// Configure our custom line renderer for this series.
..setAttribute(charts.rendererIdKey, 'customLine'),
];
We wrap all of this code in our OrdinalComboBarLineChart class. Examine the code below.
import 'package:charts_flutter/flutter.dart';
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
class OrdinalComboBarLineChart extends StatelessWidget {
final List<Series<dynamic, String>> seriesList;
final bool animate;
const OrdinalComboBarLineChart(this.seriesList, {super.key, required this.animate});
factory OrdinalComboBarLineChart.withSampleData() {
return OrdinalComboBarLineChart(
_createSampleData(),
// Disable animations for image tests.
animate: false,
);
}
@override
Widget build(BuildContext context) {
return charts.OrdinalComboChart(
seriesList,
animate: animate,
// Configure the default renderer as a bar renderer.
defaultRenderer: charts.BarRendererConfig(
groupingType: charts.BarGroupingType.stacked),
// Custom renderer configuration for the line series. This will be used for
// any series that does not define a rendererIdKey.
customSeriesRenderers: [
charts.LineRendererConfig(
// ID used to link series to this renderer.
customRendererId: 'customLine')
]);
}
/// Create series list with multiple series
static List<charts.Series<OrdinalSales, String>> _createSampleData() {
final desktopSalesData = [
OrdinalSales('2014', 5),
OrdinalSales('2015', 25),
OrdinalSales('2016', 100),
OrdinalSales('2017', 75),
];
final tableSalesData = [
OrdinalSales('2014', 5),
OrdinalSales('2015', 25),
OrdinalSales('2016', 100),
OrdinalSales('2017', 75),
];
final mobileSalesData = [
OrdinalSales('2014', 10),
OrdinalSales('2015', 50),
OrdinalSales('2016', 200),
OrdinalSales('2017', 150),
];
return [
charts.Series<OrdinalSales, String>(
id: 'Desktop',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (OrdinalSales sales, _) => sales.year,
measureFn: (OrdinalSales sales, _) => sales.sales,
data: desktopSalesData),
charts.Series<OrdinalSales, String>(
id: 'Tablet',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
domainFn: (OrdinalSales sales, _) => sales.year,
measureFn: (OrdinalSales sales, _) => sales.sales,
data: tableSalesData),
charts.Series<OrdinalSales, String>(
id: 'Mobile ',
colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault,
domainFn: (OrdinalSales sales, _) => sales.year,
measureFn: (OrdinalSales sales, _) => sales.sales,
data: mobileSalesData)
// Configure our custom line renderer for this series.
..setAttribute(charts.rendererIdKey, 'customLine'),
];
}
}
/// Sample ordinal data type.
class OrdinalSales {
final String year;
final int sales;
OrdinalSales(this.year, this.sales);
}
This code gives the following output

5. Project code and resources

Prithviraj Kapil
Hi! I’m Prithviraj Kapil, a passionate coder, app developer, and technical creative writer. I work with android and flutter. I like to develop mobile apps and create art. I am currently an undergraduate student and I have started contributing to Developers Breach with a motive to share my knowledge with others.
Here We Go Again : (
if (article == helpful) {
println("Like and subscribe to blog newsletter.")
} else {
println("Let me know what i should blog on.")
}
You must be logged in to post a comment.