# Writing Implicitly Animated Widgets in Flutter

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 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 *implicit* animation in the flutter framework, although that term is misused a fair amount even within their own documentation.

A great example is AnimatedOpacity. This widget animates based on the value passed for `opacity`, and then fades its child in or out.

<iframe style="aspect-ratio:250/270;width:100%;max-width:250px;margin:auto" src="https://i.fution.co/animated-opacity.html">
  Your browser does not support the video tag.
</iframe>

<center>(Video showing flutter logo animating in and out)</center>

The code for using this is as follows:

```dart
AnimatedOpacity(
  opacity: opacityLevel,
  duration: const Duration(seconds: 3),
  child: const FlutterLogo(),
)
```

All that needs doing to change the opacity is to pass in a different `opacityLevel` - no need to know how animations, tweens, or any other advanced concepts work.

This is extremely useful if you want to do something provided by flutter - examples include `AnimatedOpacity`, `AnimatedPositioned`, `AnimatedSize` . There are even more multipurpose widgets such as `AnimatedCrossFade` and `AnimatedWidget` that will transition between two widgets or any widget passed in respectively. But what if you want to do something else?

* * *

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 [Tween](https://api.flutter.dev/flutter/animation/Tween-class.html) and [Animation](https://api.flutter.dev/flutter/animation/Animation-class.html) 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.

# Writing your ImplicitlyAnimatedWidget subclass

The first thing you'll want to do is create new classes subclassing `ImplicitlyAnimatedWidget`and `ImplicitlyAnimatedWidgetState`. For this example, we're going to create a widget that flips its child vertically.

```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() => _AnimatedFlipYState();
}

class _AnimatedFlipYState extends ImplicitlyAnimatedWidgetState<AnimatedFlipY> {
  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {}

  @override
  void didUpdateTweens() {}

  @override
  Widget build(BuildContext context) {}
}
```

In the above example, you'll notice that the `AnimatedFlipY` class overrides the `createState` method to create an instance of `_AnimatedFlipYState`, which implements `ImplicitlyAnimatedWidgetState<AnimatedFlipY>` . If you've written a `StatefulWidget` subclass, this will be very familiar, but `ImplicitlyAnimatedWidgetState` introduces `forEachTween` which needs to be implemented in addition to the `build` method. We'll also need to override `didUpdateTweens`.

In the `forEachTween` method, we need to use the visitor to create a tween object which we will use to perform the animation. This is the implementation:

```dart
Tween<double>? _flipY;

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

What we're doing here is calling the `TweenVisitor` 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 - `TweenVisitor`'s type is `dynamic` because the `Tween` 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 `_flipY` because it is null the first time this method is called.

Next let's look at the `didUpdateTweens` method. This gets called after the object is first constructed and after a change to the parameters passed to the `AnimatedFlipY` widget.

```dart
late Animation<double> _flipAnimation;

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

In this case, we can use a `late` modifier rather than optional for `_flipAnimation` as it will never be accessed until after it is set. The `animation` object is something managed by the `ImplicitlyAnimatedWidgetState`, and by calling `drive` on it with `_flipY` we are creating a new animation that will animated to the correct values.

If we simply used the `animation` 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 `flipped` to false - we then want our animation to run from 0.6 to 0.0. By creating the new `Animation` object and assigning it to \_flipAnimation we are able to control the output of our animation.

Now take a look at the `build` method of our class:

```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,
  );
}
```

Whew, that's a big one. Let's take a look at what is going on part by part:

```dart
AnimatedBuilder(
  animation: _flipAnimation,
  builder: (context, child) {
    ...
  },
  child: widget.child,
);
```

This or something like it *will* 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.

We are using an `AnimatedBuilder` and passing the `_flipAnimation` animation to it. The `AnimatedBuilder` will listen to the animation and call the `builder` function every time the animation is changed; this means that the amount of work done in the `builder` 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 `child` parameter - in our case, the widget to be flipped. This child will then be passed into the builder function to be used there.

Finally, here is the code for doing the actual flip:

```dart
    final flipMatrix = Matrix4.rotationX(_flipAnimation.value * pi);
    return Transform(
    transform: flipMatrix,
      origin: Offset(0, flipAxis),
      alignment: Alignment.center,
      child: child,
    );
```

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.

All of this results in the following:

```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() => _AnimatedFlipYState();
}

class _AnimatedFlipYState extends ImplicitlyAnimatedWidgetState<AnimatedFlipY> {
  Tween<double>? _flipY;

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

  late Animation<double> _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,
    );
  }
}
```

With that, you should be able to write your own implicitly animated widgets!

See in dartpad [here](https://dartpad.dev/id=34aa567f81d14ee94453504ca327dee0).

* * *

### Bonus:

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!

The convention within the flutter codebase seems to be to name these types of objects `...Transition`, so we're going to call our widget `FlipYTransition`. This would be used in the above example instead of the `AnimatedBuilder`, and does essentially the same thing.

```dart
class FlipYTransition extends AnimatedWidget {
  const FlipYTransition({
    super.key,
    required Animation<double> flip,
    this.child,
  }) : super(listenable: flip);

  final Widget? child;

  Animation<double> get flip => listenable as Animation<double>;

  @override
  Widget build(BuildContext context) {
    final flipMatrix = Matrix4.rotationX(flip.value * pi);
    return Transform(
      transform: flipMatrix,
      alignment: Alignment.center,
      child: child,
    );
  }
}
```

Everything done here should be relatively easy to follow from the previous example with the exception of the `flip` getter - this is not really necessary but provides a cleaner way to access the flip animation which was passed in, as the `AnimatedWidget` widget only saves the animation as a `Listenable`.
