Creating Bar Charts Animations with CustomPainter

In this blog, we are going to show you how to animate the bars of a bar chart. First, we will show you how to draw some basic bar chart. After that we will show you how to animate the following bar chart with three animations:

Creating a basic bar chart

Since the main goal of this post to show the animation of the charts, we will keep the drawing of the chart simple. We are going to use a CustomPainter to draw the chart. For this blog, we will provide data about everyone’s favorite fruit. Of course, everyone loves the banana the most, but I added some other fruits to compare. The data is provided as a simple map:

Map<span class="token operator"><</span>String<span class="token punctuation">,</span> int<span class="token operator">></span> data <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token string">"Banana"</span><span class="token punctuation">:</span> <span class="token number">16</span><span class="token punctuation">,</span>
  <span class="token string">"Orange"</span><span class="token punctuation">:</span> <span class="token number">8</span><span class="token punctuation">,</span>
  <span class="token string">"Apple"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
  <span class="token string">"Kiwi"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
  <span class="token string">"Pear"</span><span class="token punctuation">:</span> <span class="token number">3</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

Let’s start by looking at the CustomPainter, to create a CustomPainter we need to override the shouldRepaint method and the paint method. We will focus on the paint(Canvas canvas, Size size) method first. When animating we are going to need to implement the shouldRepaint(CustomPainter oldDelegate) method.

<span class="token keyword">class</span> <span class="token class-name">BarChartPainter</span> <span class="token keyword">extends</span> <span class="token class-name">CustomPainter</span> <span class="token punctuation">{</span>
  <span class="token keyword">final</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> int<span class="token operator">></span> data<span class="token punctuation">;</span>
  <span class="token function">BarChartPainter</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token metadata symbol">@override</span>
  <span class="token keyword">void</span> <span class="token function">paint</span><span class="token punctuation">(</span>Canvas canvas<span class="token punctuation">,</span> Size size<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Paint axis <span class="token operator">=</span> <span class="token function">Paint</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token punctuation">.</span>strokeWidth <span class="token operator">=</span> axisWidth
      <span class="token punctuation">.</span><span class="token punctuation">.</span>color <span class="token operator">=</span> Colors<span class="token punctuation">.</span>grey<span class="token punctuation">;</span>

    double number <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token function">drawAxes</span><span class="token punctuation">(</span>canvas<span class="token punctuation">,</span> size<span class="token punctuation">,</span> axis<span class="token punctuation">)</span><span class="token punctuation">;</span>
    data<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">drawBar</span><span class="token punctuation">(</span>canvas<span class="token punctuation">,</span> size<span class="token punctuation">,</span> number<span class="token punctuation">,</span> key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
      number<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

First, we add a constructor to the painter with the data for the chart. In the paint method, we are going to paint bars on the canvas for each category. The bar length will be based on the max value of the categories. To complete the chart we will draw the axes and the category names. To use the BarChartPainter, we can pass the data as a constructor argument. Then we provide BarChartPainter to the painter in a CustomPaint widget.

<span class="token keyword">class</span> <span class="token class-name">BarChart</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">final</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> int<span class="token operator">></span> data <span class="token operator">=</span> <span class="token punctuation">{</span>
    <span class="token string">"Banana"</span><span class="token punctuation">:</span> <span class="token number">16</span><span class="token punctuation">,</span>
    <span class="token string">"Orange"</span><span class="token punctuation">:</span> <span class="token number">8</span><span class="token punctuation">,</span>
    <span class="token string">"Apple"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token string">"Kiwi"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token string">"Pear"</span><span class="token punctuation">:</span> <span class="token number">3</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">Center</span><span class="token punctuation">(</span>
      child<span class="token punctuation">:</span> <span class="token function">CustomPaint</span><span class="token punctuation">(</span>
        painter<span class="token punctuation">:</span> <span class="token function">BarChartPainter</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">,</span>
        child<span class="token punctuation">:</span> <span class="token function">Container</span><span class="token punctuation">(</span>
          width<span class="token punctuation">:</span> <span class="token number">300</span><span class="token punctuation">,</span>
          height<span class="token punctuation">:</span> <span class="token number">200</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

This will display the following bar chart, if you want to see the full code of the chart or play around with the code, you can go to this Dartpad.

Animating the bar chart with an Animation Controller

Since we now have a basic bar chart, we can start with the animation. Flutter has animation controllers provided to help with animations. We are going to move the bars from left to right. Our bar chart will be changing, so we have to change the bar chart from a StatelessWidget into a StatefulWidget. We also have to extend the state with SingleTickerProviderStateMixin, since we are going to use the AnimationController. We have two add two different objects to our Bar chart class. The Animation and the AnimationController.
We initialize those in the initState method. Here we define the duration of the animation in the controller. For the animation, we are going to use a Tween. This simply means we go from one value to another linearly. We also have to add a listener to call the setState method. Without this step, Flutter will not check if it needs to redraw. We can now use the current value of the animation in the buildWidget method with: animation.value. For cleaning up the controller we also override the dispose method to clean up the AnimationController.

<span class="token keyword">class</span> <span class="token class-name">BarChart</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span>
  <span class="token metadata symbol">@override</span>
  _BarChartState <span class="token function">createState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_BarChartState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">_BarChartState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token operator"><</span>BarChart<span class="token operator">></span>
    <span class="token keyword">with</span> SingleTickerProviderStateMixin <span class="token punctuation">{</span>
  Map<span class="token operator"><</span>String<span class="token punctuation">,</span> int<span class="token operator">></span> data <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">//..//};</span>
  Animation<span class="token operator"><</span>double<span class="token operator">></span> animation<span class="token punctuation">;</span>
  AnimationController controller<span class="token punctuation">;</span>
  <span class="token metadata symbol">@override</span>
  <span class="token keyword">void</span> <span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    controller <span class="token operator">=</span>
        <span class="token function">AnimationController</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Duration</span><span class="token punctuation">(</span>seconds<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">,</span> vsync<span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    animation <span class="token operator">=</span> Tween<span class="token operator"><</span>double<span class="token operator">></span><span class="token punctuation">(</span>begin<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> end<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>controller<span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token function">addListener</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    controller<span class="token punctuation">.</span><span class="token function">forward</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">CustomPaint</span><span class="token punctuation">(</span>
      painter<span class="token punctuation">:</span> <span class="token function">BarChartPainter</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> <span class="token string">"Favorite Fruit"</span><span class="token punctuation">,</span> animation<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">,</span>
      child<span class="token punctuation">:</span> <span class="token function">Container</span><span class="token punctuation">(</span>
        width<span class="token punctuation">:</span> <span class="token number">350</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token metadata symbol">@override</span>
  <span class="token keyword">void</span> <span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    controller<span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

As you can see we now pass a value from the animation with animation.value to the BarChart. This is a number between zero and a hundred. This percentage we use to determine the length of the bars. We added this value as a percentage to the constructor:

<span class="token function">BarChartPainter</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>data<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>title<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>percentage<span class="token punctuation">)</span>

Which means we can update the computation of the width of the bars as follows:

<span class="token keyword">final</span> width <span class="token operator">=</span> <span class="token punctuation">(</span>size<span class="token punctuation">.</span>width <span class="token operator">-</span> marginTopX<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token punctuation">(</span><span class="token function">maxValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> value<span class="token punctuation">)</span> <span class="token operator">*</span> percentage <span class="token operator">/</span> <span class="token number">100</span><span class="token punctuation">;</span>

Another change we have to make is to update the shouldRepaint method. This should be set to true when the percentage has changed, otherwise, it will not repaint after each change. After those changes, we have our first animation. The code so far can be found on this Dartpad.

Animating the bar chart with an Animated Builder

We can simplify the animation a bit by using the AnimatedBuilder. Here we removed the animation variable. We can now initialize the animation in the builder by wrapping the Widget that uses the animation.value by an AnimationBuilder. The TweenAnimationBuilder is a default AnimationBuilder for a Tween animation. Here we provide the duration and the tween. Since we want to use percentages it again is from zero to hundred.

<span class="token keyword">class</span> <span class="token class-name">_BarChartState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span>
    <span class="token keyword">with</span> SingleTickerProviderStateMixin <span class="token punctuation">{</span>
  Map<span class="token operator"><</span>string<span class="token punctuation">,</span> int<span class="token operator">=</span><span class="token string">""</span><span class="token operator">></span> data <span class="token operator">=</span> <span class="token punctuation">{</span>
    <span class="token string">"Banana"</span><span class="token punctuation">:</span> <span class="token number">16</span><span class="token punctuation">,</span>
    <span class="token string">"Orange"</span><span class="token punctuation">:</span> <span class="token number">8</span><span class="token punctuation">,</span>
    <span class="token string">"Apple"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token string">"Kiwi"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token string">"Pear"</span><span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">TweenAnimationBuilder</span><span class="token punctuation">(</span>
        tween<span class="token punctuation">:</span> <span class="token function">Tween</span><span class="token punctuation">(</span>begin<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> end<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        duration<span class="token punctuation">:</span> <span class="token function">Duration</span><span class="token punctuation">(</span>seconds<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>BuildContext context<span class="token punctuation">,</span> double percentage<span class="token punctuation">,</span> Widget child<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">return</span> <span class="token function">CustomPaint</span><span class="token punctuation">(</span>
            painter<span class="token punctuation">:</span> <span class="token function">BarChartPainter</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> <span class="token string">"Favorite Fruit"</span><span class="token punctuation">,</span> percentage<span class="token punctuation">)</span><span class="token punctuation">,</span>
            child<span class="token punctuation">:</span> <span class="token function">Container</span><span class="token punctuation">(</span>
              width<span class="token punctuation">:</span> <span class="token number">250</span><span class="token punctuation">,</span>
              height<span class="token punctuation">:</span> <span class="token number">150</span><span class="token punctuation">,</span>
            <span class="token punctuation">)</span><span class="token punctuation">,</span>
          <span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

We also changed the animation to make sure all bars move equally fast. This means the shorter bars will be finished earlier in this animation. For this, we only have to make a small method in the computation of the width of the bar.

<span class="token keyword">final</span> barWidth <span class="token operator">=</span> <span class="token punctuation">(</span>size<span class="token punctuation">.</span>width <span class="token operator">-</span> marginTopX<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token punctuation">(</span><span class="token function">maxValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">final</span> currentMaxWidth <span class="token operator">=</span> <span class="token punctuation">(</span>size<span class="token punctuation">.</span>width <span class="token operator">-</span> marginTopX<span class="token punctuation">)</span> <span class="token operator">*</span> percentage <span class="token operator">/</span> <span class="token number">100</span><span class="token punctuation">;</span>
<span class="token keyword">final</span> width <span class="token operator">=</span> <span class="token function">min</span><span class="token punctuation">(</span>currentMaxWidth<span class="token punctuation">,</span> barWidth<span class="token punctuation">)</span><span class="token punctuation">;</span>

The Flutter documentation about the AnimatedBuilder is great, so if you want more information about the AnimatedBuilder we would recommend looking there. Again, the code so far can be found on a Dartpad here.

Animating the bar chart with an Animated Widget

When you have a Widget that you want to use with multiple animations, the AnimatedWidget is a good choice. In this example, we will change the BarChart widget. Now when you want to draw a bar chart with a different animation, the animation can be adjusted. For example, the duration can be changed.

<span class="token keyword">class</span> <span class="token class-name">_MyAppState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span> <span class="token keyword">with</span> SingleTickerProviderStateMixin <span class="token punctuation">{</span>
  Animation animation<span class="token punctuation">;</span>
  AnimationController controller<span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  <span class="token keyword">void</span> <span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    controller <span class="token operator">=</span>
        <span class="token function">AnimationController</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Duration</span><span class="token punctuation">(</span>seconds<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">,</span> vsync<span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    animation <span class="token operator">=</span> <span class="token function">Tween</span><span class="token punctuation">(</span>begin<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> end<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>controller<span class="token punctuation">)</span><span class="token punctuation">;</span>
    controller<span class="token punctuation">.</span><span class="token function">forward</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token metadata symbol">@override</span>
  <span class="token keyword">void</span> <span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    controller<span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">MaterialApp</span><span class="token punctuation">(</span>
      home<span class="token punctuation">:</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span>
        appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span>
          title<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">"Bar chart demo"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
        body<span class="token punctuation">:</span> <span class="token function">BarChart</span><span class="token punctuation">(</span>animation<span class="token punctuation">:</span> animation<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">BarChart</span> <span class="token keyword">extends</span> <span class="token class-name">AnimatedWidget</span> <span class="token punctuation">{</span>
  <span class="token function">BarChart</span><span class="token punctuation">(</span><span class="token punctuation">{</span>Key key<span class="token punctuation">,</span> Animation animation<span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">,</span> listenable<span class="token punctuation">:</span> animation<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">final</span> Map<span class="token operator"><</span>string<span class="token punctuation">,</span> int<span class="token operator">=</span><span class="token string">""</span><span class="token operator">></span> data <span class="token operator">=</span> <span class="token punctuation">{</span>
    <span class="token string">"Banana"</span><span class="token punctuation">:</span> <span class="token number">16</span><span class="token punctuation">,</span>
    <span class="token string">"Orange"</span><span class="token punctuation">:</span> <span class="token number">8</span><span class="token punctuation">,</span>
    <span class="token string">"Apple"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token string">"Kiwi"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token string">"Pear"</span><span class="token punctuation">:</span> <span class="token number">3</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">final</span> animation <span class="token operator">=</span> listenable <span class="token operator">as</span> Animation<span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token function">CustomPaint</span><span class="token punctuation">(</span>
      painter<span class="token punctuation">:</span> <span class="token function">BarChartPainter</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> <span class="token string">"Favorite Fruit"</span><span class="token punctuation">,</span> animation<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">,</span>
      child<span class="token punctuation">:</span> <span class="token function">Container</span><span class="token punctuation">(</span>
        width<span class="token punctuation">:</span> <span class="token number">250</span><span class="token punctuation">,</span>
        height<span class="token punctuation">:</span> <span class="token number">150</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>string<span class="token punctuation">,</span><span class="token operator">></span>

Another change we have made to the animation in this version is to draw the bars one by one. Again we only have to change the computation of the width. For the computation of the width, we need the total sum of the values so far. The drawBar function has another variable, that we pass there.

double sum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
data<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">drawBar</span><span class="token punctuation">(</span>canvas<span class="token punctuation">,</span> size<span class="token punctuation">,</span> number<span class="token punctuation">,</span> key<span class="token punctuation">,</span> value<span class="token punctuation">,</span> sum<span class="token punctuation">)</span><span class="token punctuation">;</span>
  number<span class="token operator">++</span><span class="token punctuation">;</span>
  sum <span class="token operator">+=</span> value<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Based on the sum so far and the percentage of the total sum, we can determine if the bar should be drawn and how long it should be.

int <span class="token function">maxValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> data<span class="token punctuation">.</span>values<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span>max<span class="token punctuation">)</span><span class="token punctuation">;</span>
double minValue <span class="token operator">=</span> <span class="token function">min</span><span class="token punctuation">(</span><span class="token function">totalValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> percentage <span class="token operator">/</span> <span class="token number">100</span> <span class="token operator">-</span> sum<span class="token punctuation">,</span> value <span class="token operator">*</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>minValue <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">final</span> width <span class="token operator">=</span> <span class="token punctuation">(</span>size<span class="token punctuation">.</span>width <span class="token operator">-</span> marginTopX<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token punctuation">(</span><span class="token function">maxValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> minValue<span class="token punctuation">)</span><span class="token punctuation">;</span>
  canvas<span class="token punctuation">.</span><span class="token function">drawLine</span><span class="token punctuation">(</span>
    <span class="token function">Offset</span><span class="token punctuation">(</span>marginTopX<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token function">Offset</span><span class="token punctuation">(</span>width <span class="token operator">+</span> marginTopX<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">,</span>
    paint<span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

The Flutter documentation about the AnimatedWidget is clear, so if you want more information about the AnimatedWidget we would recommend looking there. Again we put the full code in a Dartpad, so you can play around with the code here. Should you still have any questions, feel free to ask them!

Leave a Reply