Gathering data and displaying it via charts is increasingly common in mobile apps. A popular example is a stock investment app that displays lots of charts, like prices for a stock and portfolio distribution pie charts. And today, we are going to learn how to build beautiful charts for these situations using the FL Chart package in Flutter.
This tutorial uses fabricated data to keep things simple and make it easier to understand the package implementation; you can easily replace it with data from your APIs.
- Prerequisites
- Setup
- Creating a line chart
- Creating a pie chart
- Other graph options
- Animations with FL Chart
Prerequisites
Let’s do a quick check of things we need before jumping right ahead:
- The Flutter SDK
- An editor; you can use either Visual Code or Android Studio
- At least beginner-level knowledge of Flutter
That’s pretty much it!
Setup
Add the dependency to your pubspec.yaml
file:
dependencies: fl_chart: ^0.45.0
Creating a line chart
Line charts are one of the most useful charts when it comes to data representation. Let’s create a line chart for the annual price data for a company’s stock. We will use the LineChart
widget to create a line chart — that’s how obvious it is.
The widget takes LineChartData
as the key parameter with swapAnimationDuration
and swapAnimationCurve
as optional parameters that can be used to control the implicit animation during a state change:
LineChart( LineChartData( // control how the chart looks like here ), swapAnimationDuration: Duration(milliseconds: 150), // Optional swapAnimationCurve: Curves.linear, // Optional );
A very simple implementation will look something like this:
Let’s take a look at the code:
class LineChartWidget extends StatelessWidget { final List<PricePoint> points; final bool isPositiveChange; const LineChartWidget(this.points, this.isPositiveChange, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { return AspectRatio( aspectRatio: 2, child: LineChart( LineChartData( lineBarsData: [ LineChartBarData( spots: points.map((point) => FlSpot(point.x, point.y)).toList(), isCurved: false, colors: [ isPositiveChange ? Colors.green : Colors.red, ], dotData: FlDotData( show: false, ), ), ], ), ), ); } }
LineChartData
contains all the information on how the line chart would look. We have used the lineBarsData
property that takes a list of LineChartBarData
to draw one or more lines on the graph.
LineChartBarData
is then used to define how the individual lines would look. It takes a list of spots
that are similar to the plot points for a line graph. By default, these points will be marked with circular dots, but we can control their appearance by using dotData
. It’s possible to hide them as well using the same.
We can also control if the line should be curved or not using isCurved
, and the color of the line is modified using the colors
property that takes a list of colors. We can use colorStops
to change the color of the line after the stop point.
N.B., it is necessary to wrap the LineChart
widget with either a SizedBox
or AspectRatio
for it to actually show up on screen. Prefer using AspectRatio
so that the graph is not skewed on different screen sizes.
Now let’s clear the unnecessary clutter from this graph and make it a bit cleaner.
The code for the sample looks like this:
LineChartData( … …. borderData: FlBorderData( border: const Border(bottom: BorderSide(), left: BorderSide())), gridData: FlGridData(show: false), titlesData: FlTitlesData( bottomTitles: _bottomTitles, leftTitles: SideTitles(showTitles: false), topTitles: SideTitles(showTitles: false), rightTitles: SideTitles(showTitles: false), ) ), … … SideTitles get _bottomTitles => SideTitles( showTitles: true, reservedSize: 22, margin: 10, interval: 1, getTextStyles: (context, value) => const TextStyle( color: Colors.blueGrey, fontWeight: FontWeight.bold, fontSize: 16, ), getTitles: (value) { switch (value.toInt()) { case 1: return 'Jan'; case 3: return 'Mar'; case 5: return 'May'; case 7: return 'Jul'; case 9: return 'Sep'; case 11: return 'Nov'; } return ''; }, );
We can also add touch events on the line chart and get a callback for the touch event to perform further operations. By default, LineChartData
displays a tooltip with the y
value touching a spot on the line chart, but we can modify this to display whatever text we want and can style it differently as well.
We are using LineTouchData
that provides a bunch of properties like touchCallback
, touchTooltipData
, and even getTouchedSpotIndicator
to modify how the touch indicator and tooltip looks.
Here’s a simple code snippet to demonstrate the same:
LineChartData( lineTouchData: LineTouchData( enabled: true, touchCallback: (FlTouchEvent event, LineTouchResponse? touchResponse) { // TODO : Utilize touch event here to perform any operation }, touchTooltipData: LineTouchTooltipData( tooltipBgColor: Colors.blue, tooltipRoundedRadius: 20.0, showOnTopOfTheChartBoxArea: true, fitInsideHorizontally: true, tooltipMargin: 0, getTooltipItems: (touchedSpots) { return touchedSpots.map( (LineBarSpot touchedSpot) { const textStyle = TextStyle( fontSize: 10, fontWeight: FontWeight.w700, color: Colors.white, ); return LineTooltipItem( points[touchedSpot.spotIndex].y.toInt().toString(), textStyle, ); }, ).toList(); }, ), getTouchedSpotIndicator: (LineChartBarData barData, List<int> indicators) { return indicators.map( (int index) { final line = FlLine( color: Colors.grey, strokeWidth: 1, dashArray: _dashArray); return TouchedSpotIndicatorData( line, FlDotData(show: false), ); }, ).toList(); }, getTouchLineEnd: (_, __) => double.infinity), …. …. )
The complete LineChartWidget
looks like this:
import 'dart:math'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'data/price_point.dart'; const _dashArray = [4, 2]; class LineChartWidget extends StatelessWidget { final List<PricePoint> points; final bool isPositiveChange; const LineChartWidget(this.points, this.isPositiveChange, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { final minY = points.map((point) => point.y).reduce(min); final maxY = points.map((point) => point.y).reduce(max); return AspectRatio( aspectRatio: 2, child: LineChart( LineChartData( lineTouchData: LineTouchData( enabled: true, touchCallback: (FlTouchEvent event, LineTouchResponse? touchResponse) { // TODO : Utilize touch event here to perform any operation }, touchTooltipData: LineTouchTooltipData( tooltipBgColor: Colors.blue, tooltipRoundedRadius: 20.0, showOnTopOfTheChartBoxArea: true, fitInsideHorizontally: true, tooltipMargin: 0, getTooltipItems: (touchedSpots) { return touchedSpots.map( (LineBarSpot touchedSpot) { const textStyle = TextStyle( fontSize: 10, fontWeight: FontWeight.w700, color: Colors.white, ); return LineTooltipItem( points[touchedSpot.spotIndex].y.toInt().toString(), textStyle, ); }, ).toList(); }, ), getTouchedSpotIndicator: (LineChartBarData barData, List<int> indicators) { return indicators.map( (int index) { final line = FlLine( color: Colors.grey, strokeWidth: 1, dashArray: _dashArray); return TouchedSpotIndicatorData( line, FlDotData(show: false), ); }, ).toList(); }, getTouchLineEnd: (_, __) => double.infinity), lineBarsData: [ LineChartBarData( spots: points.map((point) => FlSpot(point.x, point.y)).toList(), isCurved: false, colors: [ isPositiveChange ? Colors.green : Colors.red, ], dotData: FlDotData( show: false, ), ), ], minY: minY, minX: 0, maxY: maxY, borderData: FlBorderData( border: const Border(bottom: BorderSide(), left: BorderSide())), gridData: FlGridData(show: false), titlesData: FlTitlesData( bottomTitles: _bottomTitles, leftTitles: SideTitles(showTitles: false), topTitles: SideTitles(showTitles: false), rightTitles: SideTitles(showTitles: false), )), ), ); } } SideTitles get _bottomTitles => SideTitles( showTitles: true, reservedSize: 22, margin: 10, interval: 1, getTextStyles: (context, value) => const TextStyle( color: Colors.blueGrey, fontWeight: FontWeight.bold, fontSize: 16, ), getTitles: (value) { switch (value.toInt()) { case 1: return 'Jan'; case 3: return 'Mar'; case 5: return 'May'; case 7: return 'Jul'; case 9: return 'Sep'; case 11: return 'Nov'; } return ''; }, );
Wasn’t that simple? Now let’s move on to the next most commonly used chart — i.e., the pie chart.
Creating a pie chart
Now that we are a bit familiar with the classes and properties used for a line chart, it would be fairly simple to understand the pie chart; the properties and class name suffixes are very similar.
Let’s create a pie chart to display sector distribution for a user’s portfolio, where each sector is represented using a different color code.
Let’s take a look at the code:
class PieChartWidget extends StatelessWidget { final List<Sector> sectors; const PieChartWidget(this.sectors, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { return AspectRatio( aspectRatio: 1.0, child: PieChart(PieChartData( sectionsSpace: 2.0, sections: _chartSections(sectors), centerSpaceRadius: 48.0, ))); } List<PieChartSectionData> _chartSections(List<Sector> sectors) { final List<PieChartSectionData> list = []; for (var sector in sectors) { const double radius = 40.0; final data = PieChartSectionData( color: sector.color, value: sector.value, radius: radius, title: '', ); list.add(data); } return list; } }
We have used a PieChart
widget to create the pie chart; this widget takes PieChartData
as an argument to define how the pie chart would look. We have used sectionsSpace
to modify the gap between adjacent filled sections in the pie chart.
To make the pie chart hollow from the center, we have set centerSpaceRadius
to 48.0
, which is 0.0
by default. sections
property takes a list of PieChartSectionData
to define how each section in the pie chart will look. PieChartSectionData
provides control over the values and representation of individual sections of the pie.
If no value for title
is provided for PieChartSectionData
, then it displays the section value by default, so don’t forget to add an empty string for title
if you do not wish to display anything over the pie chart.
Other graph options
Apart from the most commonly used graph options that we discussed above, this powerful library also provides you with some really interesting graph types that are worth exploring. Let’s take a quick look at them as well.
Scatter chart
This allows you to plot a number of points anywhere on the graph by specifying the x and y coordinates along with a radius and color. The most amazing part about this chart is the animations that you can play with while transitioning from one state to another.
Radar chart
This allows you to create a two-dimensional graphical representation from a set of three or more data points. You can use RadarDataSet
that takes a list of RawDataSet
as dataEntries
to draw multiple radar charts in the same graphical space.
Animations with FL Chart
One thing that makes this package stand apart from other chart libraries are the beautiful animations and the control that you can have over animations for each of the charts. When you change the chart’s state, it animates to the new state internally (using implicit animations). You can control the animation duration and curve using optional swapAnimationDuration
and swapAnimationCurve
properties, respectively. You can also change the chart state based on user interactions by leveraging the <FooChart>TouchData
class that is available for all the chart options and can be really helpful in creating beautiful user interactions like the ones displayed below.
Conclusion
We learned how to draw the most widely used charts but this package is way more powerful than this and supports more complex charts like a bar chart, radar chart and even a scatter chart. If you wish to explore it further, do check out the Flutter package here.
Thanks for sticking around, happy coding fella! You can access the sample app used in the article here on GitHub.
The post Build beautiful charts in Flutter with FL Chart appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/h0yujpB
via Read more