<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Fution]]></title><description><![CDATA[Fution]]></description><link>https://blog.fution.co</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1707090749507/MipOBNloq.png</url><title>Fution</title><link>https://blog.fution.co</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 15:44:18 GMT</lastBuildDate><atom:link href="https://blog.fution.co/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Writing Implicitly Animated Widgets in Flutter]]></title><description><![CDATA[There are many ways to create animations in Flutter. Most of these involve creating an Animation and/or AnimationController and explicitly calling forward/reverse. However, a lot of the time what you ]]></description><link>https://blog.fution.co/writing-implicitly-animated-widgets-in-flutter</link><guid isPermaLink="true">https://blog.fution.co/writing-implicitly-animated-widgets-in-flutter</guid><category><![CDATA[animation]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Flutter Widgets]]></category><category><![CDATA[Flutter Examples]]></category><category><![CDATA[Dart]]></category><dc:creator><![CDATA[Morgan McKenzie]]></dc:creator><pubDate>Sun, 18 Feb 2024 19:38:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708284893059/628bfdc6-9671-4712-91d7-f87f959293a2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are many ways to create animations in Flutter. Most of these involve creating an Animation and/or AnimationController and <em>explicitly</em> calling forward/reverse. However, a lot of the time what you want is to be able to provide a state and have the widget figure out how to animate to that state by itself. This is termed an <em>implicit</em> animation in the flutter framework, although that term is misused a fair amount even within their own documentation.</p>
<p>A great example is AnimatedOpacity. This widget animates based on the value passed for <code>opacity</code>, and then fades its child in or out.</p>
<iframe style="aspect-ratio:250/270;width:100%;max-width:250px;margin:auto">
  Your browser does not support the video tag.
</iframe>

(Video showing flutter logo animating in and out)

<p>The code for using this is as follows:</p>
<pre><code class="language-dart">AnimatedOpacity(
  opacity: opacityLevel,
  duration: const Duration(seconds: 3),
  child: const FlutterLogo(),
)
</code></pre>
<p>All that needs doing to change the opacity is to pass in a different <code>opacityLevel</code> - no need to know how animations, tweens, or any other advanced concepts work.</p>
<p>This is extremely useful if you want to do something provided by flutter - examples include <code>AnimatedOpacity</code>, <code>AnimatedPositioned</code>, <code>AnimatedSize</code> . There are even more multipurpose widgets such as <code>AnimatedCrossFade</code> and <code>AnimatedWidget</code> that will transition between two widgets or any widget passed in respectively. But what if you want to do something else?</p>
<hr />
<p>This post is going to assume some knowledge of how the flutter animation framework works, or at least animations in general. Without that knowledge you should be able to get things to work anyways, but it's worth knowing what a <a href="https://api.flutter.dev/flutter/animation/Tween-class.html">Tween</a> and <a href="https://api.flutter.dev/flutter/animation/Animation-class.html">Animation</a> are and the difference between them. I'm not going to explain here as that's already covered in their documentation, but give that a read before continuing if you're unsure.</p>
<h1>Writing your ImplicitlyAnimatedWidget subclass</h1>
<p>The first thing you'll want to do is create new classes subclassing <code>ImplicitlyAnimatedWidget</code>and <code>ImplicitlyAnimatedWidgetState</code>. For this example, we're going to create a widget that flips its child vertically.</p>
<pre><code class="language-dart">class AnimatedFlipY extends ImplicitlyAnimatedWidget {
  const AnimatedFlipY({
    super.key,
    required this.child,
    required this.flipped,
    required super.duration,
  });

  final Widget child;
  final bool flipped;

  @override
  _AnimatedFlipYState createState() =&gt; _AnimatedFlipYState();
}

class _AnimatedFlipYState extends ImplicitlyAnimatedWidgetState&lt;AnimatedFlipY&gt; {
  @override
  void forEachTween(TweenVisitor&lt;dynamic&gt; visitor) {}

  @override
  void didUpdateTweens() {}

  @override
  Widget build(BuildContext context) {}
}
</code></pre>
<p>In the above example, you'll notice that the <code>AnimatedFlipY</code> class overrides the <code>createState</code> method to create an instance of <code>_AnimatedFlipYState</code>, which implements <code>ImplicitlyAnimatedWidgetState&lt;AnimatedFlipY&gt;</code> . If you've written a <code>StatefulWidget</code> subclass, this will be very familiar, but <code>ImplicitlyAnimatedWidgetState</code> introduces <code>forEachTween</code> which needs to be implemented in addition to the <code>build</code> method. We'll also need to override <code>didUpdateTweens</code>.</p>
<p>In the <code>forEachTween</code> method, we need to use the visitor to create a tween object which we will use to perform the animation. This is the implementation:</p>
<pre><code class="language-dart">Tween&lt;double&gt;? _flipY;

@override
void forEachTween(TweenVisitor&lt;dynamic&gt; visitor) {
  _flipY = visitor(
    _flipY,
    widget.flipped ? 1.0 : 0.0,
    (dynamic value) =&gt; Tween&lt;double&gt;(begin: value as double),
  ) as Tween&lt;double&gt;;
}
</code></pre>
<p>What we're doing here is calling the <code>TweenVisitor</code> with the current tween, the value we want to animate towards, and a builder function which creates a new tween given the input value. The result of this is a tween with the current value as the beginning and the target value (1.0 or 0.0 depending on whether we want the object to be flipped or not) as the end. There's a couple peculiarities in this code that should be mentioned - <code>TweenVisitor</code>'s type is <code>dynamic</code> because the <code>Tween</code> we're creating can be of any type, not just double, and might even be used for multiple types; and we have to use an optional for <code>_flipY</code> because it is null the first time this method is called.</p>
<p>Next let's look at the <code>didUpdateTweens</code> method. This gets called after the object is first constructed and after a change to the parameters passed to the <code>AnimatedFlipY</code> widget.</p>
<pre><code class="language-dart">late Animation&lt;double&gt; _flipAnimation;

@override
void didUpdateTweens() {
  _flipAnimation = animation.drive(_flipY!);
}
</code></pre>
<p>In this case, we can use a <code>late</code> modifier rather than optional for <code>_flipAnimation</code> as it will never be accessed until after it is set. The <code>animation</code> object is something managed by the <code>ImplicitlyAnimatedWidgetState</code>, and by calling <code>drive</code> on it with <code>_flipY</code> we are creating a new animation that will animated to the correct values.</p>
<p>If we simply used the <code>animation</code> object, it would always animate from 0.0 to 1.0 as that is all the superclass knows about, which is not what we want - let's say that our animation is at 0.6 and then we set <code>flipped</code> to false - we then want our animation to run from 0.6 to 0.0. By creating the new <code>Animation</code> object and assigning it to _flipAnimation we are able to control the output of our animation.</p>
<p>Now take a look at the <code>build</code> method of our class:</p>
<pre><code class="language-dart">@override
Widget build(BuildContext context) {
  return AnimatedBuilder(
    animation: _flipAnimation,
    builder: (context, child) {
      final flipMatrix = Matrix4.rotationX(_flipAnimation.value * pi);
      return Transform(
        transform: flipMatrix,
        alignment: Alignment.center,
        child: child,
      );
    },
    child: widget.child,
  );
}
</code></pre>
<p>Whew, that's a big one. Let's take a look at what is going on part by part:</p>
<pre><code class="language-dart">AnimatedBuilder(
  animation: _flipAnimation,
  builder: (context, child) {
    ...
  },
  child: widget.child,
);
</code></pre>
<p>This or something like it <em>will</em> need to be done for any implementation of an implicitly animated widget so that the animation is actually listened to. Another way of doing it will be shown at the end of this post.</p>
<p>We are using an <code>AnimatedBuilder</code> and passing the <code>_flipAnimation</code> animation to it. The <code>AnimatedBuilder</code> will listen to the animation and call the <code>builder</code> function every time the animation is changed; this means that the amount of work done in the <code>builder</code> function should be minimized as it is performed many times. If there are any static elements to the animation you're creating, those should be passed into the <code>child</code> parameter - in our case, the widget to be flipped. This child will then be passed into the builder function to be used there.</p>
<p>Finally, here is the code for doing the actual flip:</p>
<pre><code class="language-dart">    final flipMatrix = Matrix4.rotationX(_flipAnimation.value * pi);
    return Transform(
    transform: flipMatrix,
      origin: Offset(0, flipAxis),
      alignment: Alignment.center,
      child: child,
    );
</code></pre>
<p>This uses a matrix calculation to calculate the amount of rotation around X axis and sets the alignment of the transform to the center so that the axis of rotation is in the middle of the object.</p>
<p>All of this results in the following:</p>
<pre><code class="language-plaintext">class AnimatedFlipY extends ImplicitlyAnimatedWidget {
  const AnimatedFlipY({
    super.key,
    required this.child,
    required this.flipped,
    required super.duration,
  });

  final Widget child;
  final bool flipped;

  @override
  _AnimatedFlipYState createState() =&gt; _AnimatedFlipYState();
}

class _AnimatedFlipYState extends ImplicitlyAnimatedWidgetState&lt;AnimatedFlipY&gt; {
  Tween&lt;double&gt;? _flipY;

  @override
  void forEachTween(TweenVisitor&lt;dynamic&gt; visitor) {
    _flipY =
        visitor(
              _flipY,
              widget.flipped ? 1.0 : 0.0,
              (dynamic value) =&gt; Tween&lt;double&gt;(begin: value as double),
            )
            as Tween&lt;double&gt;;
  }

  late Animation&lt;double&gt; _flipAnimation;

  @override
  void didUpdateTweens() {
    _flipAnimation = animation.drive(_flipY!);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _flipAnimation,
      builder: (context, child) {
        final flipMatrix = Matrix4.rotationX(_flipAnimation.value * pi);
        return Transform(
          transform: flipMatrix,
          alignment: Alignment.center,
          child: child,
        );
      },
      child: widget.child,
    );
  }
}
</code></pre>
<p>With that, you should be able to write your own implicitly animated widgets!</p>
<p>See in dartpad <a href="https://dartpad.dev/id=34aa567f81d14ee94453504ca327dee0">here</a>.</p>
<hr />
<h3>Bonus:</h3>
<p>I mentioned that there is an alternative way to use the animation. If you're going to be using the vertical flip (or whatever you're writing) in other places, you might want to make this into its own widget. That way you can re-use the logic elsewhere, like in a page transition for example. Yay for reusability!</p>
<p>The convention within the flutter codebase seems to be to name these types of objects <code>...Transition</code>, so we're going to call our widget <code>FlipYTransition</code>. This would be used in the above example instead of the <code>AnimatedBuilder</code>, and does essentially the same thing.</p>
<pre><code class="language-dart">class FlipYTransition extends AnimatedWidget {
  const FlipYTransition({
    super.key,
    required Animation&lt;double&gt; flip,
    this.child,
  }) : super(listenable: flip);

  final Widget? child;

  Animation&lt;double&gt; get flip =&gt; listenable as Animation&lt;double&gt;;

  @override
  Widget build(BuildContext context) {
    final flipMatrix = Matrix4.rotationX(flip.value * pi);
    return Transform(
      transform: flipMatrix,
      alignment: Alignment.center,
      child: child,
    );
  }
}
</code></pre>
<p>Everything done here should be relatively easy to follow from the previous example with the exception of the <code>flip</code> getter - this is not really necessary but provides a cleaner way to access the flip animation which was passed in, as the <code>AnimatedWidget</code> widget only saves the animation as a <code>Listenable</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Drawing Dot Leaders in Flutter]]></title><description><![CDATA[This is part of a series of articles I'm writing chronicling the build of an app using Flutter. I'm working with a great designer who has made an outstanding app design, but with that design he's managed to throw a bunch of wrenches of various sizes ...]]></description><link>https://blog.fution.co/drawing-dot-leaders-in-flutter</link><guid isPermaLink="true">https://blog.fution.co/drawing-dot-leaders-in-flutter</guid><category><![CDATA[dot leaders]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[UI]]></category><dc:creator><![CDATA[Morgan McKenzie]]></dc:creator><pubDate>Sat, 03 Feb 2024 18:14:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706984001594/64bc82f0-922b-4a4e-bd4c-92788cb310e1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is part of a series of articles I'm writing chronicling the build of an app using Flutter. I'm working with a great designer who has made an outstanding app design, but with that design he's managed to throw a bunch of wrenches of various sizes at me, which I've been figuring out how to fix one by one.</p>
<h1 id="heading-the-problem">The problem...</h1>
<p>... is that I need to draw dots between the beginning and end of a row. This row can be of any length, but dots need to be spaced evenly and to fill the full space between the start and finish:</p>
<iframe style="aspect-ratio:400/54;width:100%;max-width:400px;margin:auto" src="https://i.fution.co/leader-dots-example.svg"></iframe>

<h1 id="heading-the-work">The work...</h1>
<p>The easiest way to do this is to use a custom painter. What we need to do is figure out where the dots should be drawn in relation to the width and height of the box given to the custom painter to paint. Because of the way that circles are drawn in Flutter, I need to calculate everything based on the centre of the points. For now, that looks like this:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> offset = radius + spacing + radius;
<span class="hljs-keyword">final</span> vPos = size.height / <span class="hljs-number">2</span>;
<span class="hljs-keyword">var</span> hPos = radius;
</code></pre>
<p>To do the actual drawing, I'll be subclassing <code>CustomPainter</code> and using a CustomPaint; if you haven't run across those yet I recommend taking a look at the <a target="_blank" href="https://api.flutter.dev/flutter/rendering/CustomPainter-class.html">documentation</a>. To do the drawing, I will be using the <code>canvas</code> and <code>size</code> parameters passed to the <code>paint</code> function of the CustomPainter subclass.</p>
<p>I need to draw each of the points, then move to the next, while ensuring that I don't go past the right of the box given to us to draw in. I also create a <code>Paint</code> object loaded up with whichever colour I want to draw the dots with. For this implementation I've drawn the points from the left of the available space towards the right until the end of the available space. This is what the code to do that looks like:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> paint = Paint()..color = color;
<span class="hljs-keyword">do</span> {
  canvas.drawCircle(Offset(hPos, vPos), radius, paint);
  hPos += offset;
} <span class="hljs-keyword">while</span> (hPos + radius &lt;= width);
</code></pre>
<p>This all needs to be set up in a CustomPainter, and then passed to a CustomPaint; here's the bare basic setup but made configurable:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> CustomPaint(painter: DotsPainter())
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DotsPainter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">CustomPainter</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> radius;
  <span class="hljs-keyword">final</span> Color color;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> spacing;

  <span class="hljs-keyword">const</span> DotsPainter({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.radius,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.color,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.spacing,
  });

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> paint(Canvas canvas, Size size) {
    <span class="hljs-keyword">final</span> paint = Paint()..color = color;
    <span class="hljs-keyword">final</span> width = size.width;
    <span class="hljs-keyword">final</span> vPos = size.height / <span class="hljs-number">2</span>;
    <span class="hljs-keyword">final</span> offset = radius + spacing + radius;

    <span class="hljs-keyword">var</span> hPos = radius;
    <span class="hljs-keyword">do</span> {
      canvas.drawCircle(Offset(hPos, vPos), radius, paint);
      hPos += offset;
    } <span class="hljs-keyword">while</span> (hPos + radius &lt;= width);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">bool</span> shouldRepaint(<span class="hljs-keyword">covariant</span> DotsPainter oldDelegate) =&gt;
      oldDelegate.spacing != spacing || oldDelegate.color != color || oldDelegate.radius != radius;
}
</code></pre>
<p>The <code>shouldRepaint</code> function has an implementation that will ensure that any changes to the radius/colour/spacing ensures the painter will redraw. This does everything needed to draw the example given at the start with the correct colouring &amp; sizing.</p>
<h1 id="heading-the-end-result">The end result...</h1>
<p>For a full example, tap the icon below. You can open the code used to generate this preview, which includes some extra set-up code and hardcodes the colours &amp; sizes used.</p>
<iframe src="https://zapp.run/edit/zt4006dzu410?theme=light&amp;lazy=true&amp;split=0" style="width:100%;height:500px;border:0;overflow:hidden"></iframe>

<p>This solves the problem to my satisfaction and recreates the example shown at the start. I've made use of the configurable colouring &amp; sizing to make the dots show up better for the sake of this example.</p>
<p>However, as I keep building out the app I've realized something - drawing the points from the left isn't always sufficient. For example, if you have a list of items each with a different length of text on the left, the dots will be drawn according to that size which will result in them not matching up vertically. <em>Sigh</em>. I'll write a post soon showing how I've fixed that!</p>
<aside>
Cover Photo by Alex Konokh on Unsplash
</aside>]]></content:encoded></item></channel></rss>