Ever wish there was an easy way to add rich animation to your Flutter UIs? I did, so when we started working on the Wonderous app, it seemed like the perfect excuse to build a best of breed open-source animation package.
I have an unhealthy obsession with writing animation libraries (see: gTween & TweenJS), so I had a pretty good idea of what I wanted:
- simple, concise, and clean API
- a wide variety of ready-made effects
- scales well from basic fades to complex sequenced effects
- easy to build totally custom animations
- performant, small, and dependency free
- fun! I want to enjoy using it and sharing the results.
Flutter has a powerful animation framework, but it’s also quite verbose. Let’s say I wanted a button to slide in — I’d have to convert my parent widget to a
StatefulWidget, initialize a persistent
AnimationController, ensure it’s properly updated and disposed of, use it to drive a
CurvedAnimation, and feed that into a
SlideTransition wrapping my button.
To make it worse, the necessary code tends to be scattered across 3 or 4 parts of your class, and gets more complex with each additional animated element or effect. This introduces friction and discourages playing with animation.
Flutter Animate abstracts all of this away:
Animate(effects: [SlideEffect()], child: myButton)
It also adds an
animate extension to
Widget, which makes it extra concise (but you can use either syntax interchangeably):
And just to make it super extra concise, there’s extensions for
num to declare a
myButton.animate().slide(delay: 300.ms, duration: 2.seconds)
Flutter Animate includes a wide variety of pre-built visual effects (fade, slide, scale, rotate, blur, shimmer, shake, saturation, tint, visibility, and more), which have attractive defaults, but are customizable via parameters:
myText.animate().tint(color: Colors.red, end: 0.5)
Layering multiple effects on a single target is simple:
myButton.animate().fadeIn(curve: Curves.easeOut).scale(begin: 0.5)
One other neat trick: subsequent effects inherit their
curve, so your effects synchronize by default. For example, these two effects (fade + slide) will run together:
myText.animate().fade(delay: 300.ms, duration: 700.ms).slide()
You’re not stuck with just the included effects though, it’s super easy to build your own, either as reusable
Effect classes, or via
Beyond all that, there’s: toggles, swaps, builders, events, delayed starts, sequenced/parallel effects, saved effects, external controllers, and adapters (which let you sync animation to scrolling or other inputs).
Flutter Animate has a very detailed README, and comprehensive documentation, so we won’t dig into everything here. Instead, let’s take a look at one effect from Wonderous that was a lot of fun to create — the teaser animation for collectable artifacts:
We’re going to jump into the deep end with this one! If it seems a bit tough to grasp, don’t worry, this is probably the most complicated chunk of code I’ve written with the library so far.
collectible.animate(onPlay: (controller) => controller.repeat()) .shimmer(delay: 4000.ms, duration: 1800.ms) // shimmer + .shake(hz: 4, curve: Curves.easeInOutCubic) // shake + .scale(begin: 1.0, end: 1.1, duration: 600.ms) // scale up .then(delay: 600.ms) // then wait and .scale(begin: 1.0, end: 1 / 1.1) // scale down
The first line creates the animation, and uses the
onPlay callback to tell its
AnimationController to loop continuously.
The next line adds a
shimmer effect, which will start after 4 seconds (
delay: 4000.ms), and run for 1.8 seconds (
These timing values are inherited by the subsequent
shake effect, so it will run in parallel. It also adds a
curve, so the shake eases in and out.
The remainder is a bit tricky — I wanted the collectible to scale up, hold for a bit, then scale back down, all in the same time as the shimmy-shake.
scale inherits the 4 second delay, but defines a shorter duration (1800/3 =
600.ms) so it scales up over the first third of the other effects.
This is followed by
then, which is a special effect that sets the inheritable delay to the end of the previous effect, plus an optional additional
delay. In this case, it makes it so the second
scale starts 600ms after the first one ends.
scale counter-scales (
end: 1 / 1.1) back down over the same 600ms.
Combine this all together, and we have our final effect in just 6 short lines of code. Nice!
Flutter Animate is easy to use, but that also means it’s easy to go overboard and overwhelm or irritate your users — use restraint, be aware of the implications on performance and battery life, and always ask yourself if an animation is making your UI a better experience overall.
If you’d like to learn more about Flutter Animate then check out the in-depth README, API docs or the code.
Better yet, take a look at the open-source Wonderous mobile app, which uses the package for all of its delightful animations, and is available on the iOS and Play stores.
We welcome community participation in the package: logging issues, requesting features, or contributing new effects. It’s super easy to build new effects for Flutter Animate — usually with just a few lines of code. In fact, while playing with ideas for this blog post I wrote a new 3D flip effect (coming soon) in about 20 minutes.
I hope you have fun playing with Flutter Animate, and that it helps bring a bit more life and personality to your apps!
Wonderful, thanks for the post. It’s super informative and helpful. I didn’t know using animation is that simple.
amazing. ehat about expand and collapse animation???
dude this is some seriously awesome stuff!!
@Hesam — you’re welcome!
@santosh — feel free to request it (or better yet, submit a pull request) on GitHub! In fact, this issue may already cover it? https://github.com/gskinner/flutter_animate/issues/10
@JT — thanks! It was a lot of fun to build.
holy crap that’s cool. nice API!
What a powerful package and nice article!