Add beautiful animated effects & builders in Flutter, via an easy, highly customizable unified API.

Last update: Jun 26, 2022

Flutter Animate

A performant library that makes it simple to add almost any kind of animated effect in Flutter.

  1. Pre-built effects, like fade, scale, slide, blur, shake, shimmer, and color effects (saturation and tint).
  2. Easy custom effects
  3. Simplified animated builders
  4. Synchronized events

All via a simple, unified API without fussing with AnimationController and StatefulWidget.

NOTE: This library is currently in prerelease. Some aspects of the API will change as it is refined. Your feedback is welcome via Github issues.

Example Image

Above: An example showing some features of Flutter Animate in ~15 simple lines of code.

Duration extensions

This package includes extension methods for num, to make specifying durations easier. For example: 2.seconds, 0.1.minutes, or 300.ms.

Basics

Syntax

To apply effects, wrap the target widget in Animate, and specify a list of effects:

Animate(
  effects: [FadeEffect(), ScaleEffect()],
  child: Text("Hello World!"),
)

It also adds an .animate() extension method to all widgets, which wraps the widget in Animate(). Each effect also adds a chainable extension method to Animate to enable a shorthand syntax:

Text("Hello World!").animate().fade().scale()

NOTE: The shortform style is used in this README, but all functionality is available in either format.

Delay, duration, curve

Effects have optional delay, duration, and curve parameters. Effects run in parallel, but you can use a delay to run them sequentially:

Text("Hello").animate()
  .fade(duration: 500.ms)
  .scale(delay: 500.ms) // runs after fade.

Note that effects are "active" for the duration of the full animation, so for example, two fade effects on the same target can have unexpected results (SwapEffect detailed below, can help address this).

If not specified (or null), these values are inherited from the previous effect, or from Animate.defaultDuration and Animate.defaultCurve if it is the first effect:

Text("Hello World!").animate()
  .fadeIn() // uses `Animate.defaultDuration`
  .scale() // inherits duration from fadeIn
  .move(delay: 300.ms, duration: 600.ms) // runs after the above w/new duration
  .blur(end: 8.0) // inherits the delay & duration from move

Animate also has its own delay parameter, which happens before the animation runs. Unlike the delay on an Effect, it is only applied once if the animation repeats.

Text("Hello").animate(
    delay: 1000.ms, // this delay only happens once at the very start
    onInit: (controller) => controller.repeat(), // loop
  ).fadeIn(delay: 500.ms) // this delay happens at the start of each loop

Sequencing with ThenEffect

ThenEffect is a special "convenience" effect that simply sets its own inheritable delay to the sum of the delay and duration of the previous effect, and its own (optional) delay. This makes it easier to sequence effects.

In the following example, the slide would run immediately after the fade ended, then the blur would run 200ms after the slide ended.

Text("Hello").animate()
  .fadeIn(delay: 300.ms, duration: 500.ms)
  .then() // sets own delay to 800ms (300+500)
  .slide(duration: 400.ms) // inherits the 800ms delay
  .then(delay: 200.ms) // sets delay to 1400ms (800+400+200)
  .blur() // inherits the 1400ms delay
  // Explicitly setting delay overrides the inherited value.
  // This move effect will run BEFORE the initial fade:
  .move(delay: 0.ms)

Animating lists

The AnimateList class offers similar functionality for lists of widgets, with the option to offset each child's animation by a specified interval:

Column(children: AnimateList(
  interval: 400.ms,
  effects: [FadeEffect(duration: 300.ms)],
  children: [Text("Hello"), Text("World"),  Text("Goodbye")],
))

// or shorthand:
Column(
  children: [Text("Hello"), Text("World"),  Text("Goodbye")]
    .animate(interval: 400.ms).fade(duration: 300.ms),
)

Shared effects

Because Effect instances are immutable, they can be reused. This makes it easy to create a global collection of effects that are used throughout your app and updated in one place. This is also useful for design systems.

MyGlobalEffects.transitionIn = <Effect>[
  FadeEffect(duration: 100.ms, curve: Curves.easeOut),
  ScaleEffect(begin: 0.8, curve: Curves.easeIn)
]

// then:
Text('Hello').animate(effects: MyGlobalEffects.transitionIn)

Custom effects & builders

It is easy to write new resuable effects by extending Effect, but you can also easily create one-off custom effects by using CustomEffect, ToggleEffect, and SwapEffect.

CustomEffect

CustomEffect lets you build custom animated effects. Simply specify a builder function that accepts a context, value, and child. The child is the target of the animation (which may already have been wrapped in other effects).

For example, this would add a background behind the text and fade it from red to blue:

Text("Hello World").animate().custom(
  duration: 300.ms,
  builder: (context, value, child) => Container(
    color: Color.lerp(Colors.red, Colors.blue, value),
    padding: EdgeInsets.all(8),
    child: child, // child is the Text widget being animated
  )
)

By default it provides a value from 0-1 (though some curves can generate values outside this range), based on the current time, duration, and curve. You can also specify begin and end values as demonstrated in the example below.

Animate can be created without a child, so you use CustomEffect as a simplified builder. For example, this would build text counting down from 10, and fading out:

Animate().custom(
  duration: 10.seconds,
  begin: 10,
  end: 0,
  builder: (_, value, __) => Text(value.round()),
).fadeOut()

ToggleEffect

ToggleEffect also provides builder functionality, but instead of a double, it provides a boolean value equal to true before the end of the effect and false after (ie. after its duration).

Animate().toggle(
  duration: 2.seconds,
  builder: (_, value, __) => Text(value ? "Before" : "After"),
)

This can also be used to activate "Animated" widgets, like AnimatedContainer, by toggling their values with a minimal delay:

Animate().toggle(
  duration: 1.ms,
  builder: (_, value, __) => AnimatedContainer(
    duration: 1.second,
    color: value ? Colors.red : Colors.green,
  ),
)

SwapEffect

SwapEffect lets you swap out the whole target widget at a specified time:

Text("Before").animate().swap(duration: 900.ms, builder: (_) => Text("After"))

This can also be useful for creating sequential effects, by swapping the target widget back in, effectively wiping all previous effects:

Widget text = Text("Hello World!");

// then:
text.animate().fadeOut(300.ms) // fade out & then...
  .swap(builder: (_) => text.fadeIn()) // swap in original widget & fade back in

Events & callbacks

There are onInit and onComplete callbacks on Animate that trigger when the whole animation starts or ends. Use the provided AnimationController to manipulate the animation (ex. repeat, reverse, etc).

Text("Horrible Pulsing Text")
  .animate(onInit: (controller) => controller.repeat(reverse: true))
  .fadeOut(curve: Curves.easeInOut)

For more nuanced callbacks, use CallbackEffect or ListenEffect.

CallbackEffect

CallbackEffect lets you add a callback to an arbitrary postion in your animations. For example, adding a callback halfway through a fade:

Text("Hello").animate().fadeIn(duration: 600.ms)
  .callback(duration: 300.ms, callback: () => print('halfway'))

As with other effects, it will inherit the delay and duration of prior effects:

Text("Hello").animate().scale(delay: 200.ms, duration: 400.ms)
  .callback(callback: () => print('scale is done'))

ListenEffect

ListenEffect lets you register a callback to receive the animation value (as a double) for a given delay, duration, curve, begin, and end.

Text("Hello").animate().fadeIn(curve: Curves.easeOutExpo)
  .listen(callback: (value) => print('current opacity: $value'))

The above example works, because the listen effect inherits duration and curve from the fade, and both use begin=0, end=1 by default.

Installation

Grab it from pub.dev.

GitHub

https://github.com/gskinner/flutter_animate
You might also like...

A Flutter slidable widget that provides an easy to use configuration. Highly customisable. Just as you want it!

A Flutter slidable widget that provides an easy to use configuration. Highly customisable. Just as you want it!

A Flutter slidable widget that provides an easy to use configuration. Highly customisable. Just as you want it!

Feb 14, 2022

Flutter-Animated-Library-of-Books - Flutter App - Animated Book Library

bookshelf A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if thi

Jan 2, 2022

The easiest way to create your animated splash screen in a fully customizable way.

The easiest way to create your animated splash screen in a fully customizable way.

Animated Splash Screen Check it out at Pub.Dev Do it your way Assets image Custom Widget Url image IconData Or just change PageTransition and/or Splas

Jun 16, 2022

Animated dialog box - A pure dart package for showing animated alert box.

Animated dialog box - A pure dart package for showing animated alert box.

animated_dialog_box A pure dart package for showing animated alert box. Getting Started https://github.com/Shubham-Narkhede/animated_dialog_box/blob/m

Jan 14, 2022

Animated shimmer - A simple & lightweight widget to display an animated shimmer effect

Animated shimmer - A simple & lightweight widget to display an animated shimmer effect

Animated Shimmer Supports Null Safety A simple & lightweight widget to display a

Apr 27, 2022

Flutter plugin for building pull to refresh effects with PullToRefreshNotification and PullToRefreshContainer quickly.

pull_to_refresh_notification Language: English | 中文简体 widget to build pull to refresh effects. Web demo for PullToRefreshNotification Chinese blog pul

Jun 20, 2022

An all-in-one Fllutter package for state management, reactive objects, animations, effects, timed widgets etc.

An all-in-one Fllutter package for state management, reactive objects, animations, effects, timed widgets etc.

Frideos An all-in-one package for state management, streams and BLoC pattern, animations and timed widgets, effects. Contents 1. State management Gett

Jun 14, 2022

A beautiful and customizable Star Rating Dialog package for Flutter

A beautiful and customizable Star Rating Dialog package for Flutter

rating_dialog A beautiful and customizable Rating Dialog package for Flutter! Supports all platforms that flutter supports! Import the rating_dialog p

Jun 13, 2022

Add beautiful and trending tab indicators directly to your default Flutter TabBar

Add beautiful and trending tab indicators directly to your default Flutter TabBar

Add beautiful and trending tab indicators directly to your default Flutter TabBar. Features 💚 Supports Android, iOS, Web Can be directly added to the

Jun 28, 2022
Comments
  • 1. Something wrong with `buildSubAnimation`

    It's hard to produce minimal. You can run my project to get this error.

    If I try to decrease the grid cell when the grid cell increase animation has not been completed, it generates this error.

    ═══════ Exception caught by widgets library ═══════════════════════════════════
    The following assertion was thrown building Animate-[<'point : 0 | depth : 0 | false'>](state: _AnimateState#0613f(ticker active)):
    'package:flutter/src/animation/curves.dart': Failed assertion: line 183 pos 12: 'end <= 1.0': is not true.
    package:flutter/…/animation/curves.dart:183
    2
    
    Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
    In either case, please report this assertion by filing a bug on GitHub:
      https://github.com/flutter/flutter/issues/new?template=2_bug.md
    
    The relevant error-causing widget was
    Animate-[<'point : 0 | depth : 0 | false'>]
    lib/grid_zoom.dart:554
    When the exception was thrown, this was the stack
    #2      Interval.transformInternal
    package:flutter/…/animation/curves.dart:183
    #3      ParametricCurve.transform
    package:flutter/…/animation/curves.dart:38
    #4      Curve.transform
    package:flutter/…/animation/curves.dart:92
    #5      CurvedAnimation.value
    package:flutter/…/animation/animations.dart:462
    #6
    
    Reviewed by definev at 2022-06-14 10:40
  • 2. [Question] - How to restart animation?

    I love your package so much it hides almost the boilerplate part. So declarative animation. I make this effect but I got an issue about restarting the animation.

    An animation I'm trying to replicate this animation.

    Currently, my implementation is pretty close but I don't know how to restart animation so It's a bit weird.

    My reproduce: https://gist.github.com/definev/d1aacf50122f62e79a956eb7245e495c

    https://user-images.githubusercontent.com/62325868/173096991-1be7683c-0e27-446c-b0c8-1eaeace5e113.mov

    The UI is separated into two-part:

    • Center: white block.
    • Border: yellow block.

    https://user-images.githubusercontent.com/62325868/173099633-d6ddbd90-2db3-400f-8249-f4528b6cde4c.mp4

    My border is regenerating every user increasing depth so it auto animates. I want to trigger the center to re-animate when changing depth.

    Reviewed by definev at 2022-06-10 15:31
  • 3. Support passing in controller

    Could be interesting to support passing in a ValueNotifier, and only instantiate an AnimationController if it is not provided. This would facilitate two things:

    1. Letting the parent widget own creating and destroying the controller at appropriate times.
    2. Support other notifiers (ex. animation on scroll).
    foo.animate(controller: myController).etc()
    

    Right now, we update the duration of the controller as new effects are added, so we'd need to decide if this still happens if the controller that's passed in is an AnimationController.

    Reviewed by gskinner at 2022-06-10 19:49
  • 4. Add a way to clear or reset the animation.

    It would be useful to have an effect similar to swap, but which returns the original child:

    Currently to create a complex sequence you can do something like this (from the README):

    Widget text = Text("Hello World!");
    
    // then:
    text.animate().fadeOut(300.ms) // fade out & then...
      .swap(builder: (_) => text.animate().fadeIn()) // swap in original widget & fade back in
    

    But the need to save off the original child is a pain point. We could do something like this:

    Text("Hello World!").animate().fadeOut(300.ms) // fade out & then...
      .clear(builder: (child) => child.animate().fadeIn()) // swap in original widget & fade back in
    
    Reviewed by gskinner at 2022-06-24 17:04

Related

Custom dropdown widget allows to add highly customizable widget in your projects with proper open and close animations and also comes with form required validation.
Custom dropdown widget allows to add highly customizable widget in your projects with proper open and close animations and also comes with form required validation.

Custom Dropdown Custom Dropdown package lets you add customizable animated dropdown widget. Features Lots of properties to use and customize dropdown

Jun 19, 2022
⚡️A highly customizable, powerful and easy-to-use alerting library for Flutter.
⚡️A highly customizable, powerful and easy-to-use alerting library for Flutter.

Flash ⚡️ A highly customizable, powerful and easy-to-use alerting library for Flutter. Website: https://sososdk.github.io/flash Specs This library all

Jun 29, 2022
Powerful, helpfull, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server with boilerplate code free.
Powerful, helpfull, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server with boilerplate code free.

flutter_axelor_sdk Powerful, helpful, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server w

Nov 12, 2021
Create highly customizable, simple, and controllable autocomplete fields for Flutter.
Create highly customizable, simple, and controllable autocomplete fields for Flutter.

Field Suggestion Installing Depend on it Add this to your package's pubspec.yaml file: dependencies: field_suggestion: <latest_version> Install it Y

Jun 17, 2022
A highly customizable Flutter color picker.
A highly customizable Flutter color picker.

FlexColorPicker FlexColorPicker is a customizable color picker for Flutter. The ColorPicker can show six different types of color pickers, three of wh

Jun 16, 2022
A highly customizable Flutter color picker.
A highly customizable Flutter color picker.

FlexColorPicker FlexColorPicker is a customizable color picker for Flutter. The ColorPicker can show six different types of color pickers, three of wh

Jun 16, 2022
A highly customizable Flutter widget to render and interact with JSON objects.
A highly customizable Flutter widget to render and interact with JSON objects.

The spreadsheet with superpowers ✨ ! JSON Data Explorer A highly customizable widget to render and interact with JSON objects. Features Expand and col

May 31, 2022
A package that provides a highly customizable sheet widget that snaps to different vertical & horizontal positions
A package that provides a highly customizable sheet widget that snaps to different vertical & horizontal positions

Snapping Sheet A package that provides a highly customizable sheet widget that snaps to different vertical & horizontal positions Can adapt to scrolla

Jun 21, 2022
The flutter_calendar_widget is highly customizable calendar widget.
The flutter_calendar_widget is highly customizable calendar widget.

flutter_calendar_widget The flutter_calendar_widget is highly customizable calendar widget. Not only can you change the style, but you can also change

Jun 24, 2022
Beautiful Weather App using API with support for dark mode. Created by Jakub Sobański ( API ) and Martin Gogołowicz (UI, API help)
Beautiful Weather App using API with support for dark mode. Created by Jakub Sobański ( API ) and Martin Gogołowicz (UI, API help)

Flutter Weather App using API with darkmode support Flutter 2.8.1 Null Safety Beautiful Weather App using https://github.com/MonsieurZbanowanYY/Weathe

Jan 8, 2022