Written by Krzysztof Len
JS Fullstack Gdańsk
Published October 30, 2020

Flutter Halloween Chart

How to customise chart using charts_flutter package – tutorial

In this tutorial, I’ll show you how you can customise a bar chart using charts_flutter package. Halloween is around the corner, so let’s create a chart which designs will allude to these event and the end of this tutorial will look like this:

To generate a chart, we are going to use a charts_flutter package which the documentation available in the link below:

https://pub.dev/packages/charts_flutter

Let’s begin.

Step. 1 – config

First of all, we have to init our flutter project. Run the following command in your terminal:

flutter create <name_of_your_project>

After our project is initialized, open it in your IDE and then:

  • Create an assets folder in the root directory – it will be necessary to use the halloween font. Here is the one I’ve been using, but feel free to choose whichever you like:

https://www.1001fonts.com/feast-of-flesh-bb-font.html

  • Download the fonts package and paste .otf and .ttf files in the assets folder
  • Go to pubspec.yaml file and under the dependencies paste the version of package which Flutter has to install. Currently it’s 0.9.0:
dependencies:
  flutter:
    sdk: flutter
  charts_flutter: ^0.9.0
  • Add assets folder to make it available in the project:
# The following section is specific to Flutter.
flutter:
  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/

With that setup, we’re good to go to the next step and start to create the Halloween chart.

Step. 2 – main.dart

Let’s add some code to the root file. Replace defaulted code in main.dart with the following one:

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Color(0xFFEB6123),
        fontFamily: 'fofbb_reg',
      ),
      home: Scaffold(
          appBar: AppBar(
            title: Text(
              'Flutter Halloween chart',
            ),
          ),
          body: Text('Chart')),
    );
  }
}

For now, we’ve returned a simple text in the scaffold body to avoid an error, but later on, we’ll replace it with our Halloween chart. Remember to import material package.

Step. 3 – create scary model

Halloween is all about collecting as many candies as possible, so let’s create a model with our candy stats.

  • In the /lib folder create a subfolder called models with scary_statistics.dart file and paste the code below:
class ScaryStatics {
  final int house;
  final int candy;

  ScaryStatics(this.house, this.candy);
}

Step. 4 – create basis bar chart

Now, we can create our first simple bar chart, using the installed package.

  • In the /lib directory, create a subfolder called widgets and create halloween_bar_chart.dart file inside it.

Below is the code snippet which will generate the basic bar chart:

class HalloweenBarChart extends StatelessWidget {
  final List<Series> seriesList;
  final bool animate;

  HalloweenBarChart(this.seriesList, {this.animate});

  factory HalloweenBarChart.withSampleData() {
    return HalloweenBarChart(_createSampleData(), animate: false);
  }

  static List<Series<ScaryStatics, String>> _createSampleData() {
    return [];
  }

  @override
  Widget build(BuildContext context) {
    return BarChart(
      seriesList,
      animate: animate,
    );
  }
}

The only thing left is to import that class.

  • In the main.dart file replace the scaffold body Text we previously put in it with the following lines:
body: Container(
   height: 200,
   padding: const EdgeInsets.all(15),
   child: HalloweenBarChart.withSampleData(),
),

Now our chart needs to be fed with some Halloween data.

Inside _createSampleData method we’ll create some dummy data. Create a variable that holds some amount of instances of ScaryStatistics in mine version it looks like this:

final data = [
   ScaryStatics(1, 11),
   ScaryStatics(2, 14),
   ScaryStatics(3, 8),
   ScaryStatics(4, 10),
   ScaryStatics(5, 15),
   ScaryStatics(6, 1),
   ScaryStatics(7, 9),
   ScaryStatics(8, 3),
   ScaryStatics(9, 9),
   ScaryStatics(10, 12),
   ScaryStatics(11, 18),
   ScaryStatics(12, 8),
   ScaryStatics(13, 3),
   ScaryStatics(14, 4),
   ScaryStatics(15, 10),
   ScaryStatics(16, 12),
   ScaryStatics(17, 10),
   ScaryStatics(18, 8),
   ScaryStatics(19, 15),
   ScaryStatics(20, 12),
];
  • Return this data in List of Series – which is the model delivered from charts_flutter package:
return [
   Series(
     id: 'Data',
     data: data,
     domainFn: (ScaryStatics data, _) => data.house.toString(),
     measureFn: (ScaryStatics data, _) => data.candy,
   ),
];

Important notice: toString() called on data.house is necessary, because we provide a number of houses as integers, but it’s required to provide it as a String value.

Step. 5 – change basic styles of labels

Now our chart has a default look and the only difference is that our custom fonts are already loaded and applied on the x and y-axis labels. But we can go further and customize it independently.

So let’s start with the x-axis.

  • Under animate property in BarChart you can define domainAxis and provide some custom options:
@override
  Widget build(BuildContext context) {
    return BarChart(
      seriesList,
      animate: animate,
      domainAxis: OrdinalAxisSpec(
        renderSpec: SmallTickRendererSpec(
          labelStyle: TextStyleSpec(
            fontSize: 11,
            color: Color.fromHex(code: '#5E32BA'),
          ),
          lineStyle: LineStyleSpec(
            color: Color.transparent,
          ),
        ),
      ),
    );
  }

This will change the color and font-size of the label and hide the bottom line.

For the y-axis you can define primaryMeasureAxis property:

@override
  Widget build(BuildContext context) {
    return BarChart(
      seriesList,
      animate: animate,
      domainAxis: OrdinalAxisSpec(
        renderSpec: SmallTickRendererSpec(
          labelStyle: TextStyleSpec(
            fontSize: 11,
            color: Color.fromHex(code: '#5E32BA'),
          ),
          lineStyle: LineStyleSpec(
            color: Color.transparent,
          ),
        ),
      ),
      primaryMeasureAxis: NumericAxisSpec(
        tickFormatterSpec:
            BasicNumericTickFormatterSpec((num value) => "$value 🍬"),
        renderSpec: GridlineRendererSpec(
          labelStyle: TextStyleSpec(
            fontSize: 11,
            color: Color.fromHex(code: '#96C457'),
          ),
          labelAnchor: TickLabelAnchor.after,
        ),
      ),
    );
  }

To display text-like labels we need BasicNumericTickFormatterSpec function and with little string interpolation and Halloween candy Unicode, we can display a pleasant-looking label.

We can customize also in what order our label and grid line are displayed. We can use the TickLabelAnchor enum for that.

Step. 6 – static chart labels

Currently, our chart starts from zero and on the x-axis, every house we visited is displayed in a row. We can also change that behavior and display more static labels.

  • Create a final variable with our label’s:
@override
  Widget build(BuildContext context) {
    final staticLabels = <TickSpec<int>>[
      // Possible to define style of labels directly inside
      TickSpec(5, style: TextStyleSpec(fontFamily: 'fofbb_reg')),
      TickSpec(10),
      TickSpec(15),
      TickSpec(20),
    ];

    return BarChart(
    // Rest of the code
  • In primaryMeasureAxis inside NumericAxisSpec add the following property:
primaryMeasureAxis: NumericAxisSpec(
   tickProviderSpec: StaticNumericTickProviderSpec(staticLabels),
   tickFormatterSpec: BasicNumericTickFormatterSpec((num value) => "$value 🍬"),
   renderSpec: GridlineRendererSpec(
     labelStyle: TextStyleSpec(
        fontSize: 11,
        color: Color.fromHex(code: '#96C457'),
     ),
     labelAnchor: TickLabelAnchor.after,
     ),
   ),

Now our y-axis labels start from 5 and display only provided labels.

We can do similar with the x-axis:

  • Create a final variable with our label’s under staticLabels variable:
 @override
  Widget build(BuildContext context) {
    final staticLabels = <TickSpec<int>>[
      // Possible to define style of labels directly inside
      TickSpec(5, style: TextStyleSpec(fontFamily: 'fofbb_reg')),
      TickSpec(10),
      TickSpec(15),
      TickSpec(20),
    ];

    final staticTicks = <TickSpec<String>>[
      // Possible to define style of labels directly inside
      TickSpec("1",
          label: "1 house", style: TextStyleSpec(fontFamily: 'fofbb_reg')),
      TickSpec("10", label: "10 house"),
      TickSpec("20", label: "20 house"),
    ];
  • Then inside in domainAxis in OrdinalAxisSpec add the following line:
domainAxis: OrdinalAxisSpec(
   tickProviderSpec: StaticOrdinalTickProviderSpec(staticTicks),
   renderSpec: SmallTickRendererSpec(
      labelStyle: TextStyleSpec(
         lineHeight: 3,
         fontSize: 11,
         color: Color.fromHex(code: '#5E32BA'),
       ),
       lineStyle: LineStyleSpec(
         color: Color.transparent,
       ),
     ),
   ),

With that, we have customized x and y-axis labels.

Step. 7 – default selected (min/max)

Now let’s make a default selected bars with the minimum and maximum values in different colors.

  • Inside _createSampleData method paste before return statement below code:
final highestValue = data.reduce((curr, next) => curr.candy > next.candy ? curr : next);    
final smallestValue = data.reduce((curr, next) => curr.candy < next.candy ? curr : next);

return [
  Series(
    id: 'Data',
    data: data,
    domainFn: (ScaryStatics data, _) => data.house.toString(),
    measureFn: (ScaryStatics data, _) => data.candy,
  ),
];
  • Inside of the return, we can add colorFn property and with a little conditioning we can customize our bars colors depend on values:
return [
   Series(
     id: 'Data',
     data: data,
     domainFn: (ScaryStatics data, _) => data.house.toString(),
     measureFn: (ScaryStatics data, _) => data.candy,
     colorFn: (data, _) {
        if (data.candy == highestValue.candy) {
          return Color.fromHex(code: '#18181A');
        }
        if (data.candy == smallestValue.candy) {
          return Color.fromHex(code: '#5E32BA');
        }
        return Color.fromHex(code: '#EB6123');
     },
   ),
];
  • Finally, as a little polishing let’s add some line-height between x-axis labels and columns. Inside OrdinalAxisSpec add the following line:
return BarChart(
      seriesList,
      animate: animate,
      domainAxis: OrdinalAxisSpec(
        tickProviderSpec: StaticOrdinalTickProviderSpec(staticTicks),
        renderSpec: SmallTickRendererSpec(
          labelStyle: TextStyleSpec(
            lineHeight: 3,
            fontSize: 11,
            color: Color.fromHex(code: '#5E32BA'),
          ),
          lineStyle: LineStyleSpec(
            color: Color.transparent,
          ),
        ),
      ),
  • To make bars a little rounded add the following property:
return BarChart(
      seriesList,
      animate: animate,
      defaultRenderer: BarRendererConfig(
        cornerStrategy: const ConstCornerStrategy(7),
      ),

And that’s all. Our halloween bar chart is ready, we now know how many candies we collect in which house and we can now eat our sweets. Enjoy Halloween 🙂

Written by Krzysztof Len
JS Fullstack Gdańsk
Published October 30, 2020