React to changes in CustomPaint Widgets

After describing in the last post how to detect which CustomPaint widgets are clicked, the next step is me was to change the widgets. First, I’m going to show how to do this with a simple example that contains three different CustomPainter widgets. After that, I will show you how to do the same for the List of CustomPaint Widgets.

Changing the state of CustomPaint widget

So let’s get back to the example with three circles.

Create a StatefulWidget

Since we want to change the state of the app, let’s start by adding a StatefulWidget. (hint: typing stful in Android Studio is a great shortcut). Since we want to pass multiple attributes of the Circle to the CirclePainter, we are also going to add a CircleModel. For now, it contains the center, radius, color, and the key to the CustomPainter for checking if we clicked on the circle. We are also going to pass a key to the state to the Widget so that we can update the state later on.

<span class="token keyword">class</span> <span class="token class-name">Circle</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span>
  <span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>key<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> CircleModel model<span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey<span class="token operator"><</span>_CircleState<span class="token operator">></span> key<span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  _CircleState <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">_CircleState</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">_CircleState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token operator"><</span>Circle<span class="token operator">></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>
      key<span class="token punctuation">:</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>key<span class="token punctuation">,</span>
      painter<span class="token punctuation">:</span> <span class="token function">CirclePainter</span><span class="token punctuation">(</span>
          widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>center<span class="token punctuation">,</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>radius<span class="token punctuation">,</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>color<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><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">CircleModel</span> <span class="token punctuation">{</span>
  <span class="token function">CircleModel</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>radius<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>color<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> Offset center<span class="token punctuation">;</span>
  double radius<span class="token punctuation">;</span>
  <span class="token keyword">final</span> Color color<span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey key<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Add a method to increase the size of the circles

Inside the newly created CircleState, we are going to add a method to change something of the state. For now, I’m going to increase the radius of the circles, but the same applies to the color and the location of the circle.

<span class="token keyword">class</span> <span class="token class-name">_CircleState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token operator"><</span>Circle<span class="token operator">></span> <span class="token punctuation">{</span>
  <span class="token keyword">void</span> <span class="token function">increaseSize</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>
    widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>radius <span class="token operator">=</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>radius <span class="token operator">+</span> <span class="token number">5</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>
  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>
      key<span class="token punctuation">:</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>key<span class="token punctuation">,</span>
      painter<span class="token punctuation">:</span> <span class="token function">CirclePainter</span><span class="token punctuation">(</span>
          widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>center<span class="token punctuation">,</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>radius<span class="token punctuation">,</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>color<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><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>

Update the shouldRepaint method

We also have to adjust the CustomPainter, since now the shouldRepaint always returns false. Since we are only updating the radius in this example I’m going to change the repaint method such that the method returns true when there is difference between the old and the new radius.

<span class="token keyword">class</span> <span class="token class-name">CirclePainter</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> double radius<span class="token punctuation">;</span>
  <span class="token keyword">final</span> Offset center<span class="token punctuation">;</span>
  <span class="token keyword">final</span> Color color<span class="token punctuation">;</span>

  <span class="token function">CirclePainter</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>radius<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>color<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 paint <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>color <span class="token operator">=</span> color<span class="token punctuation">;</span>
    canvas<span class="token punctuation">.</span><span class="token function">drawCircle</span><span class="token punctuation">(</span>center<span class="token punctuation">,</span> radius<span class="token punctuation">,</span> paint<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token metadata symbol">@override</span>
  bool <span class="token function">shouldRepaint</span><span class="token punctuation">(</span>CirclePainter oldDelegate<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> oldDelegate<span class="token punctuation">.</span>radius <span class="token operator">!=</span> radius<span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  bool <span class="token function">hitTest</span><span class="token punctuation">(</span>Offset position<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">final</span> Path path <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Path</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    path<span class="token punctuation">.</span><span class="token function">addRect</span><span class="token punctuation">(</span>Rect<span class="token punctuation">.</span><span class="token function">fromCircle</span><span class="token punctuation">(</span>center<span class="token punctuation">:</span> center<span class="token punctuation">,</span> radius<span class="token punctuation">:</span> radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> path<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>position<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Handle the clicks on the circles

The last thing to do is to call the increaseSize method on the CircleState for each of the circles that we clicked on. For this, we already create the GlobalKeys for both pointing to the painter and the _CircleState. We need the first keys to determine whether or not there was a click on that Widget. We need the second key to update the state of the Circle.

<span class="token keyword">class</span> <span class="token class-name">HexagonGridDemo</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> GlobalKey blueCirclePainter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GlobalKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey<span class="token operator"><</span>_CircleState<span class="token operator">></span> blueCircle <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GlobalKey</span><span class="token operator"><</span>_CircleState<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey redCirclePainter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GlobalKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey<span class="token operator"><</span>_CircleState<span class="token operator">></span> redCircle <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GlobalKey</span><span class="token operator"><</span>_CircleState<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey yellowCirclePainter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GlobalKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey<span class="token operator"><</span>_CircleState<span class="token operator">></span> yellowCircle <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GlobalKey</span><span class="token operator"><</span>_CircleState<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> result <span class="token operator">=</span> <span class="token function">BoxHitTestResult</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">'Hexagon Grid 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">Listener</span><span class="token punctuation">(</span>
          onPointerDown<span class="token punctuation">:</span> <span class="token punctuation">(</span>PointerEvent details<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token function">handleClick</span><span class="token punctuation">(</span>details<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span><span class="token punctuation">,</span>
          child<span class="token punctuation">:</span> <span class="token function">Stack</span><span class="token punctuation">(</span>
            children<span class="token punctuation">:</span> <span class="token operator"><</span>Widget<span class="token operator">></span><span class="token punctuation">[</span>
              <span class="token function">Circle</span><span class="token punctuation">(</span>
                  model<span class="token punctuation">:</span> <span class="token function">CircleModel</span><span class="token punctuation">(</span>
                    <span class="token function">Offset</span><span class="token punctuation">(</span><span class="token number">90</span><span class="token punctuation">,</span> <span class="token number">120</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    <span class="token number">70</span><span class="token punctuation">,</span>
                    Colors<span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
                    blueCirclePainter<span class="token punctuation">,</span>
                  <span class="token punctuation">)</span><span class="token punctuation">,</span>
                  key<span class="token punctuation">:</span> blueCircle<span class="token punctuation">)</span><span class="token punctuation">,</span>
              <span class="token function">Circle</span><span class="token punctuation">(</span>
                  model<span class="token punctuation">:</span> <span class="token function">CircleModel</span><span class="token punctuation">(</span>
                    <span class="token function">Offset</span><span class="token punctuation">(</span><span class="token number">60</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    <span class="token number">50</span><span class="token punctuation">,</span>
                    Colors<span class="token punctuation">.</span>red<span class="token punctuation">,</span>
                    redCirclePainter<span class="token punctuation">,</span>
                  <span class="token punctuation">)</span><span class="token punctuation">,</span>
                  key<span class="token punctuation">:</span> redCircle<span class="token punctuation">)</span><span class="token punctuation">,</span>
              <span class="token function">Circle</span><span class="token punctuation">(</span>
                  model<span class="token punctuation">:</span> <span class="token function">CircleModel</span><span class="token punctuation">(</span>
                    <span class="token function">Offset</span><span class="token punctuation">(</span><span class="token number">140</span><span class="token punctuation">,</span> <span class="token number">70</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    <span class="token number">40</span><span class="token punctuation">,</span>
                    Colors<span class="token punctuation">.</span>yellow<span class="token punctuation">,</span>
                    yellowCirclePainter<span class="token punctuation">,</span>
                  <span class="token punctuation">)</span><span class="token punctuation">,</span>
                  key<span class="token punctuation">:</span> yellowCircle<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>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">handleClick</span><span class="token punctuation">(</span>PointerEvent details<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isClicked</span><span class="token punctuation">(</span>details<span class="token punctuation">,</span> redCirclePainter<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      redCircle<span class="token punctuation">.</span>currentState<span class="token punctuation">.</span><span class="token function">increaseSize</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">if</span> <span class="token punctuation">(</span><span class="token function">isClicked</span><span class="token punctuation">(</span>details<span class="token punctuation">,</span> blueCirclePainter<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      blueCircle<span class="token punctuation">.</span>currentState<span class="token punctuation">.</span><span class="token function">increaseSize</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">if</span> <span class="token punctuation">(</span><span class="token function">isClicked</span><span class="token punctuation">(</span>details<span class="token punctuation">,</span> yellowCirclePainter<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      yellowCircle<span class="token punctuation">.</span>currentState<span class="token punctuation">.</span><span class="token function">increaseSize</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>

  bool <span class="token function">isClicked</span><span class="token punctuation">(</span><span class="token keyword">final</span> PointerEvent details<span class="token punctuation">,</span> <span class="token keyword">final</span> GlobalKey key<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">final</span> RenderBox circleBox <span class="token operator">=</span> key<span class="token punctuation">.</span>currentContext<span class="token punctuation">.</span><span class="token function">findRenderObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    Offset localClick <span class="token operator">=</span> circleBox<span class="token punctuation">.</span><span class="token function">globalToLocal</span><span class="token punctuation">(</span>details<span class="token punctuation">.</span>position<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>circleBox<span class="token punctuation">.</span><span class="token function">hitTest</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> position<span class="token punctuation">:</span> localClick<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Now the circles are increasing in size whenever we click on them. If you want to try it out or change something else of the circles (for example, the color) you can play around with the code in this Dartpad.

Changing the state on a list of CustomPaint widgets

The previous solution is not really scalable if you want to add a lot of CustomPaint Widgets or if you want to add and remove them on the fly. So now we are going to update our Hexagon Grid example from the previous blog with updates on the Hexagons.

Change the widget in a StatefulWidget

The first step is to change the HexagonPaint to a StatefulWidget so that we can update the state of the Widget. When changing the HexagonPaint to a StatefulWidget, we keep the constructor in the HexagonPaint and add a key to the _HexagonPaintState, so that the Listener can update the state. We also added a boolean to the model for whether the hexagon is clicked or not.

<span class="token keyword">class</span> <span class="token class-name">HexagonPaint</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">final</span> HexagonModel model<span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey<span class="token operator"><</span>_HexagonPaintState<span class="token operator">></span> key <span class="token operator">=</span> GlobalKey<span class="token operator"><</span>_HexagonPaintState<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">HexagonPaint</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  _HexagonPaintState <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">_HexagonPaintState</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">_HexagonPaintState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token operator"><</span>HexagonPaint<span class="token operator">></span> <span class="token punctuation">{</span>
  <span class="token function">updateColor</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>
      widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>clicked <span class="token operator">=</span> <span class="token boolean">true</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>
  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>
      key<span class="token punctuation">:</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>key<span class="token punctuation">,</span>
      painter<span class="token punctuation">:</span> <span class="token function">HexagonPainter</span><span class="token punctuation">(</span>
          widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>center<span class="token punctuation">,</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>radius<span class="token punctuation">,</span> widget<span class="token punctuation">.</span>model<span class="token punctuation">.</span>clicked<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><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">HexagonModel</span> <span class="token punctuation">{</span>
  <span class="token keyword">final</span> Offset center<span class="token punctuation">;</span>
  <span class="token keyword">final</span> double radius<span class="token punctuation">;</span>
  <span class="token keyword">final</span> GlobalKey key <span class="token operator">=</span> <span class="token function">GlobalKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  bool clicked <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
  <span class="token function">HexagonModel</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>radius<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Update the HexagonPainter

We now have to update our HexagonPainter to deal with the new boolean. If the Hexagon is clicked we will fill the Hexagon with a red color otherwise it remains blue. Another thing we need to do before our HexagonPainter is finished is to update the shouldRepaint method. This determines whether it needs to be drawn again or not. We can compare the clicked value with the clicked value of the oldDelegate.

<span class="token keyword">class</span> <span class="token class-name">HexagonPainter</span> <span class="token keyword">extends</span> <span class="token class-name">CustomPainter</span> <span class="token punctuation">{</span>
  <span class="token keyword">static</span> <span class="token keyword">const</span> int SIDES_OF_HEXAGON <span class="token operator">=</span> <span class="token number">6</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> double radius<span class="token punctuation">;</span>
  <span class="token keyword">final</span> Offset center<span class="token punctuation">;</span>
  <span class="token keyword">final</span> bool clicked<span class="token punctuation">;</span>

  <span class="token function">HexagonPainter</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>radius<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>clicked<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 paint <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>color <span class="token operator">=</span> clicked <span class="token operator">?</span> Colors<span class="token punctuation">.</span>red <span class="token punctuation">:</span> Colors<span class="token punctuation">.</span>blue<span class="token punctuation">;</span>
    Path path <span class="token operator">=</span> <span class="token function">createHexagonPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    canvas<span class="token punctuation">.</span><span class="token function">drawPath</span><span class="token punctuation">(</span>path<span class="token punctuation">,</span> paint<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  Path <span class="token function">createHexagonPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">final</span> path <span class="token operator">=</span> <span class="token function">Path</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> angle <span class="token operator">=</span> <span class="token punctuation">(</span>math<span class="token punctuation">.</span>pi <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">/</span> SIDES_OF_HEXAGON<span class="token punctuation">;</span>
    Offset firstPoint <span class="token operator">=</span> <span class="token function">Offset</span><span class="token punctuation">(</span>radius <span class="token operator">*</span> math<span class="token punctuation">.</span><span class="token function">cos</span><span class="token punctuation">(</span><span class="token number">0.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> radius <span class="token operator">*</span> math<span class="token punctuation">.</span><span class="token function">sin</span><span class="token punctuation">(</span><span class="token number">0.0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    path<span class="token punctuation">.</span><span class="token function">moveTo</span><span class="token punctuation">(</span>firstPoint<span class="token punctuation">.</span>dx <span class="token operator">+</span> center<span class="token punctuation">.</span>dx<span class="token punctuation">,</span> firstPoint<span class="token punctuation">.</span>dy <span class="token operator">+</span> center<span class="token punctuation">.</span>dy<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>int i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> SIDES_OF_HEXAGON<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      double x <span class="token operator">=</span> radius <span class="token operator">*</span> math<span class="token punctuation">.</span><span class="token function">cos</span><span class="token punctuation">(</span>angle <span class="token operator">*</span> i<span class="token punctuation">)</span> <span class="token operator">+</span> center<span class="token punctuation">.</span>dx<span class="token punctuation">;</span>
      double y <span class="token operator">=</span> radius <span class="token operator">*</span> math<span class="token punctuation">.</span><span class="token function">sin</span><span class="token punctuation">(</span>angle <span class="token operator">*</span> i<span class="token punctuation">)</span> <span class="token operator">+</span> center<span class="token punctuation">.</span>dy<span class="token punctuation">;</span>
      path<span class="token punctuation">.</span><span class="token function">lineTo</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    path<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> path<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token metadata symbol">@override</span>
  bool <span class="token function">shouldRepaint</span><span class="token punctuation">(</span>HexagonPainter oldDelegate<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span>
      oldDelegate<span class="token punctuation">.</span>clicked <span class="token operator">!=</span> clicked<span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  bool <span class="token function">hitTest</span><span class="token punctuation">(</span>Offset position<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">final</span> Path path <span class="token operator">=</span> <span class="token function">createHexagonPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> path<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>position<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Call the update method when clicked on the Hexagon

The last thing to do is to call the updateColor() on the currentState of the Hexagon. In the last blog, we showed how to determine which hexagon was clicked on. We can now access to the key we added on the HexagonPaint. The key references to the _HexagonPaintState which contains our method to update the hexagon.

<span class="token function">handleClick</span><span class="token punctuation">(</span>PointerEvent details<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">var</span> hexagon <span class="token operator">=</span>
      grid<span class="token punctuation">.</span>hexagons<span class="token punctuation">.</span><span class="token function">firstWhere</span><span class="token punctuation">(</span><span class="token punctuation">(</span>hexagon<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">determineClick</span><span class="token punctuation">(</span>hexagon<span class="token punctuation">,</span> details<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  hexagon<span class="token punctuation">.</span>key<span class="token punctuation">.</span>currentState<span class="token punctuation">.</span><span class="token function">updateColor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

bool <span class="token function">determineClick</span><span class="token punctuation">(</span>HexagonPaint hexagon<span class="token punctuation">,</span> PointerEvent details<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">final</span> RenderBox hexagonBox <span class="token operator">=</span>
      hexagon<span class="token punctuation">.</span>model<span class="token punctuation">.</span>key<span class="token punctuation">.</span>currentContext<span class="token punctuation">.</span><span class="token function">findRenderObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">final</span> result <span class="token operator">=</span> <span class="token function">BoxHitTestResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  Offset localClick <span class="token operator">=</span> hexagonBox<span class="token punctuation">.</span><span class="token function">globalToLocal</span><span class="token punctuation">(</span>details<span class="token punctuation">.</span>position<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>hexagonBox<span class="token punctuation">.</span><span class="token function">hitTest</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> position<span class="token punctuation">:</span> localClick<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Again we put the full code here in a Dartpad, so you can play around with the code without having a full setup. Should you still have any questions, feel free to ask them!

Leave a Reply