Animations in Flutter: First steps

- 👤 Andrés Cruz

🇪🇸 En español

Animations in Flutter: First steps

It is well known that Flutter offers a very easy way to create beautiful cross-platform applications (Android and iOS, and Desktop and Web currently in the beta channel). 

From an Android and iOS developer perspective (like mine), this can be true on many levels, as you can create your user interface with declarative and functional code.

Using simple concepts like Column or Row, instead of diving into XML files (which are not code as such), or even the famous storyboards which is at the Graphical Interface level; all of which combined is a small hell, increasing the complexity of developing applications for both platforms that follow the same line (for example, an online store application for a client who wants their app for Android, iOS, and of course a website).

If you have worked with the Android animation API, you know they are a headache on many levels: a complex interface, many APIs (some of them deprecated), and in the end, you'll finish without knowing how to implement your animations aside from the support libraries... and not counting the animation process in iOS which is a world apart from its closest competitor Android with iOS.

This is not the case for Flutter animations, as there is only one way to implement them! and it's common for Android and iOS, since remember that Flutter does not use iOS primitives for example, if you want to implement a button, it does not use the Button primitives in Android and UIButton in iOS, but rather draws EVERYTHING from scratch for each platform; which is excellent since Flutter has total control and gains speed.

Why Flutter makes animations so easy

In Android, animations involve navigating between APIs, strange classes, and inconsistent behaviors. In iOS, the paradigm is completely different. I remember that keeping both versions synchronized in a multi-client project was insane.

With Flutter, that disappears. The animations are identical for Android, iOS, Web, and Desktop. Everything is drawn from scratch with the Flutter engine, so it doesn't depend on native primitives (UIButton, View, etc.). Total control, zero surprises.

Real advantages when animating widgets in Flutter

  • Same behavior across all platforms
  • Smooth animations because the framework controls the render
  • Clean and consistent APIs
  • Great variety of ready-made widgets
  • Total control when you want to go further

Types of animations in Flutter

  • Implicit animations
    • They are the simplest. You change a value and Flutter handles the transition. Perfect for getting started.
  • Explicit animations
    • Here you control the animation in detail with AnimationController, Tween, Curves, and more.
  • Interpolated vs. physics-based
    • Interpolated: define start and end, and Flutter calculates the transition.
    • Physics-based: use simulations like springs and gravity.
  • When to choose each type according to your use case
    • Implicit: micro-interactions, simplicity, smooth changes.
    • Explicit: complex sequences, chained animations, 3D, coordinations.
    • Physics: realistic effects, bouncing, and natural behaviors.

Implicit animations through Widgets

Implicit animations are nothing more than widgets that manage all the difficult part of the animation for you. In reality, these widgets are very similar to the ones you might have already implemented in your code. I'm talking about, for example:

  • AnimatedPadding, the animated counterpart of the Padding widget.
  • AnimatedOpacity, the animated counterpart of the Opacity widget.
  • Among others.

These two examples are very specific, and their behavior corresponds closely to their name (THAT is a good class name!).

AnimatedOpacity(
 opacity: show ? 1 : 0,
 duration: Duration(milliseconds: 500),
 child: Image.asset('assets/logo.png'),
)

You change show and you have a smooth transition without doing anything else.

These widgets only need a few things to work: 

  1. A child like almost everything in Flutter, it can be anything (which can be any widget),
  2. A duration (expressed in hours, minutes, seconds, milliseconds, etc.) and a value that must be managed by a StatefulWidget. This last point is very important, since every time the value is changed inside the setState function (therefore, the widget is rebuilt), the animation will automatically launch through an interpolation of the old and the new value, which in a nutshell means having a smooth transition from one state to another.
import 'package:flutter/material.dart';

class Animation1 extends StatefulWidget {
  @override
  _Animation1State createState() => _Animation1State();
}

class _Animation1State extends State<Animation1> {
  double opacity = 0.0;
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        AnimatedOpacity(
          child: Image.asset(
            'assets/img.png',
            fit: BoxFit.fill,
          ),
          duration: Duration(seconds: 3),
          opacity: opacity,
        ),
        FlatButton(
          onPressed: _changeOpacity,
          child: Text('Animate'),
        )
      ],
    );
  }

  void _changeOpacity() {
    setState(() {
      opacity = 1.0;
    });
  }
} 
Vary animated opacity in Flutter

The code snippet above changed the value of the opacity variable inside a setState function, which translated into an easy and amazing fade-in transition for our image.

And, in the end, this widget handled all the animation stuff by itself. The only thing we had to do was change the state of our widget.

The power of AnimatedContainer to animate multiple properties

 AnimatedContainer allows animating the most common elements such as width, height, color, borders, padding, radii... all by just updating the state.

AnimatedContainer(
 height: size,
 width: size,
 duration: Duration(seconds: 1),
 decoration: BoxDecoration(
   color: color,
   borderRadius: BorderRadius.circular(radius),
 ),
);

Limits of implicit animations

When you start chaining widgets or need sequences, reverses, pauses, or interval control, it gets complicated. I lived it: a hell of indentations and nested widgets. That's where explicit animations come in.

AnimatedContainer Widget 

This is great, you can just use these widgets to start using animations in your app! But there's a problem: the animations seen so far only animate one property of our child. That's not very flexible and can cause many scalability problems if we just adjust the widgets without control, which obviously leads to poor and cryptic code (you know that Dart can be an indentation hell if not written correctly).

In short, we can use these animations for really simple things, without implementing a logic per se at the moment we want to start the animation, vary behaviors, durations, reverse or stop the animation, none of this and much more CANNOT be done with these containers.

The panacea: AnimatedContainer.

Its behavior is exactly the same as a common container, but the best part is that any of its properties can be animated. The only thing we have to do is decide what changes will be made to our child.

Let's say we want to change both the height and width of our container, and also its color (yes, colors can be interpolated). The first thing we should do is define the fields of our StatefulWidget:

  var imgHeight = 100.0;
  var imgWidth = 100.0;
  var backgroundColor = Colors.white;

And now, our container:

AnimatedContainer(
  height: imgHeight,
  width: imgWidth,
  color: backgroundColor,
  child: Image.asset('assets/img.png', fit: BoxFit.fill),
  duration: Duration(seconds: 3),
)

And decide what the new values will be assigned in our setState function:

  void _changeValues() {
    setState(() {
      // new values to interpolate
      imgWidth = 300;
      imgHeight = 300;
      backgroundColor = Colors.deepPurpleAccent;
    });
  } 
Multiple properties on an image in Flutter.

As you can see, more than one property is animated at the same time using AnimatedContainer.

This is the source code:

I said that any property can be animated in this widget. Let's try and animate its decoration parameter following the previous steps:

import 'package:flutter/material.dart';

class Animation3 extends StatefulWidget {
  @override
  _Animation3State createState() => _Animation3State();
}

class _Animation3State extends State<Animation3> {
  var imgHeight = 100.0;
  var imgWidth = 100.0;
  var backgroundColor = Colors.white;
  var borderRadius = 0.0;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        AnimatedContainer(
          height: imgHeight,
          width: imgWidth,
          decoration: BoxDecoration(
            color: backgroundColor,
            borderRadius: BorderRadius.circular(borderRadius),
          ),
          child: Image.asset('assets/img.png', fit: BoxFit.fill),
          duration: Duration(seconds: 3),
        ),
        FlatButton(
          onPressed: _changeValues,
          child: Text('Animate'),
        )
      ],
    );
  }

  void _changeValues() {
    setState(() {
      // new values to interpolate
      imgWidth = 300;
      imgHeight = 300;
      borderRadius = 150.0;
      backgroundColor = Colors.deepPurple;
    });
  }
} 
Animate more properties

As I said, ANY property of the Container widget can be animated. :) Finally, we can even create a small sequential choreography for our little friend Ditto. But this would involve tracking the state of the class fields, adding many new variables, many conditionals to set the new values, and doing so would lead us to a lot of repetition.

Other examples:

Animated opacity 

AnimatedOpacity(
 opacity: visible ? 1 : 0,
 duration: Duration(milliseconds: 500),
)

Container with several animated properties:

AnimatedContainer(
 duration: Duration(seconds: 1),
 width: w,
 height: h,
 color: color,
)

Basic example with AnimationController

_controller.forward();

Simple 3D Flip with Transform:

Transform(
 transform: Matrix4.identity()..rotateY(pi * value),
 alignment: Alignment.center,
 child: child,
)

Explicit animations: total control step-by-step

Unlike the previous ones where we only defined a widget, some properties, and more, with explicit ones, we have TOTAL control over when we start, stop, reverse... (you get it, right) the animations... BUT they are more complex to implement and require a controller to do all this.

The role of the AnimationController

It is the "brain" of the animation. It defines duration, status, repetition, direction, pauses, and more.

late final AnimationController _controller;
_controller = AnimationController(
 vsync: this,
 duration: Duration(seconds: 2),
)..forward();

Tween, Curved Animation and accelerations

final curve = CurvedAnimation(
 parent: _controller,
 curve: Curves.easeOut,
);
final animation = Tween<double>(
 begin: 0,
 end: 300,
).animate(curve);

They allow customizing how the animation progresses.

AnimatedBuilder and AnimatedWidget

They are used to rebuild only the affected part in each frame.

AnimatedBuilder(
 animation: animation,
 builder: (_, child) {
   return Transform.scale(
     scale: animation.value,
     child: child,
   );
 },
 child: Icon(Icons.star),
)

Cases where you really need explicit animations

  • Complex sequences
  • Synchronized animations
  • 3D animations (like card flip)
  • Games and highly dynamic elements
  • Precise control over each frame

Common animation patterns in Flutter

  • Hero animations (transitions between screens)
    • The Hero widget allows moving an element between screens with visual continuity.
  • Animated lists and grids (AnimatedList)
    • Perfect for adding and removing items smoothly.
  • Staggered animations
    • Useful for animating elements in cascade.
  • Advanced transitions with the animations package

Frequently Asked Questions

  • Which widget to use to start animating?
    • AnimatedContainer or AnimatedOpacity.
  • How to make a 3D animation?
    • With Transform and an AnimationController.
  • Are implicit or explicit animations better?
    • It depends: implicit are fast; explicit give you total control.
  • How to prevent an animation from feeling slow?
    • Adjust curves, reduce duration, or smooth calculations in the build.

 

Conclusion

This is just to get started with animations. You just want your app to look great without making a huge effort. The interface is simple and easy to use. 

After trying implicit animations, explicit animations, animated containers, and patterns like Hero, you will see that Flutter lets you scale from the simplest to the most advanced without changing your mindset. That consistency was what I enjoyed the most when I switched from native Android/iOS to Flutter.

If you already master this, I recommend exploring:

  • Advanced physics animations (SpringSimulation)
  • TweenSequence for complex animations
  • Shared Axis Transitions (animations package)
  • Flutter Impeller for performance improvements

I agree to receive announcements of interest about this Blog.

We're going to explain Flutter animations in a simple and step-by-step way; we're going to write basic animations in a simple and exemplified way.

| 👤 Andrés Cruz

🇪🇸 En español