Flutter Line chart with a great graph tool Fl_Chart

eye-catchDart and Flutter
Sponsored links

I wanted to show a graph for my application, so I looked for some tools and found a great tool called fl_chart. I tried to use Line Chart functionality, so I will share what I did in this post.

Sponsored links

Generate random data for the chart

I want to show Google Search Console data in my app, so I created a class that has the date and the corresponding value. The data is random values in the range 0 to 10,000.

class _MyData {
  final DateTime date;
  final double value;

  _MyData({
    required this.date,
    required this.value,
  });
}

List<_MyData> _generateData(int max) {
  final random = new Random();

  return List.generate(
    31,
    (index) => _MyData(
      date: DateTime(2022, 1, index + 1),
      value: random.nextDouble() * max,
    ),
  );
}

class Graph extends StatefulWidget {
  @override
  _Graph createState() => _Graph();
}

class _Graph extends State<Graph> {
  late List<_MyData> _data;

  @override
  void initState() {
    _data = _generateData(10000);
    super.initState();
  }
}

If you want to generate random values in a certain range, the following post might be helpful.

404 NOT FOUND | Technical Feeder
Programming stuffs with full example code which you can download and try easily.
Sponsored links

Hold a place to show the chart

The next step is to hold the place to show the chart. Fl_chart shows axis titles by default.

@override
Widget build(BuildContext context) {
  return SafeArea(
    child: Scaffold(
      appBar: AppBar(
        title: Text("Graph sample"),
      ),
      body: Center(
        child: Container(
          decoration: BoxDecoration(
            border: Border.all(),
          ),
          child: _graph(),
          margin: EdgeInsets.all(3),
          padding: EdgeInsets.all(15),
          height: MediaQuery.of(context).size.height * 0.5,
        ),
      ),
    ),
  );
}

Without the padding, the axis title is rendered over the parent widget.

Minimal code for a chart

Let’s check the default behavior. This time, we will use a line chart. Random values are assigned to _data variable in initState shown the code above.

Widget _graph() {
  final spots = _data
      .asMap()
      .entries
      .map((element) => FlSpot(
            element.key.toDouble(),
            element.value.value,
          ))
      .toList();

  return LineChart(
    LineChartData(
      lineBarsData: [
        LineChartBarData(
          spots: spots,
        ),
      ],
    ),
    swapAnimationDuration: Duration(milliseconds: 150), // Optional
    swapAnimationCurve: Curves.linear, // Optional
  );
}

By the way, the values of swapAnimationDuration and swapAnimationCurve are default values. If we want to change the chart style, we can change it.

Wow. With this minimal code, a chart is shown! The detailed data is also shown when touching the area. What great this is.

But one thing we should be careful of is that the detailed information is shown on the data above. If the data is on top of the widget, the widget is rendered outside of the screen.

over

Not show the dot for the each data

If the screen is small like on a mobile, the dots might be annoying. In this case, we can disable it. Let’s change the line color as well.

return LineChart(
  LineChartData(
    lineBarsData: [
      LineChartBarData(
        spots: spots,
        dotData: FlDotData(show: false),
        colors: const [Colors.blue],
      ),
      ...
    ]
  ),
);
without-dot

Add the second line

It’ ‘s easy to add the second line. Just add an additional LineChartBarData.

final spots2 = _generateData(5000)
    .asMap()
    .entries
    .map(
      (element) => FlSpot(
        element.key.toDouble(),
        element.value.value,
      ),
    )
    .toList();

return LineChart(
  LineChartData(
    lineBarsData: [
      LineChartBarData(
        spots: spots,
        dotData: FlDotData(show: false),
        colors: const [Colors.blue],
      ),
      LineChartBarData(
        spots: spots2,
        dotData: FlDotData(show: false),
        colors: const [Colors.red],
      ),
    ],
    ...
  ),
);
second line

Define axis titles

If the two lines are for the same indicator, it’s not necessary to show the title both left and right. Let’s hide the unnecessary titles and try to change the bottom title to date.

return LineChart(
  LineChartData(
    lineBarsData: [...],
    titlesData: FlTitlesData(
      rightTitles: SideTitles(showTitles: false),
      topTitles: SideTitles(showTitles: false),
      bottomTitles: SideTitles(
        reservedSize: 6,
        getTextStyles: (context, xValue) => const TextStyle(
          color: Colors.pink,
          fontSize: 9,
        ),
        rotateAngle: 30,
        showTitles: true,
        getTitles: (xValue) {
          final date = _data[xValue.toInt()].date;
          return DateFormat.MMMd().format(date);
        },
      ),
    ),
  ),
  ...
);

When the date is shown without angle, the string is overlap. To show the date here, it should be short enough. The format is done by DateFormat. If you are not familiar with it, please check the following post as well.

title change

Chart is rendered over the parent widget

LineChartData offers minY and maxY properties. If we add those properties without thinking about the chart data, the chart is rendered over the parent widget.

return LineChart(
  LineChartData(
    ...
    clipData: FlClipData.all(),
    minY: 3000,
    maxY: _data.map((e) => e.value).reduce(max),
  ),
);
set minY

To avoid this problem, clipData property needs to be set.

return LineChart(
  LineChartData(
    ...
    clipData: FlClipData.all(), // add here
    minY: 3000,
    maxY: _data.map((e) => e.value).reduce(max),
  ),
);
min with clip

Make the chart scrollable

If the screen is too small to show the chart, it should be scrollable. To make it scrollable, we can use SingleChildScrollView.

Widget _scrollGraph() {
  return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: Container(
      padding: EdgeInsets.only(bottom: 15),
      width: MediaQuery.of(context).size.height * 2,
      child: _graph(),
    ),
  );
}

If you need to make it scrollable in bi directions, in other words, cross axis, the following post might be helpful.

Comments

Copied title and URL