https://github.com/felixblaschke/simple_animations
Flutter package for creating awesome animations.
https://github.com/felixblaschke/simple_animations
Last synced: 6 months ago
JSON representation
Flutter package for creating awesome animations.
- Host: GitHub
- URL: https://github.com/felixblaschke/simple_animations
- Owner: felixblaschke
- License: mit
- Created: 2019-03-30T17:38:22.000Z (over 6 years ago)
- Default Branch: main
- Last Pushed: 2025-05-03T10:52:00.000Z (7 months ago)
- Last Synced: 2025-05-03T11:32:42.599Z (7 months ago)
- Language: Dart
- Homepage:
- Size: 5.39 MB
- Stars: 1,049
- Watchers: 15
- Forks: 107
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-flutter-cn - Simple Animations - 由 [Felix Blaschke](https://github.com/felixblaschke) 开发,轻松创建令人赞叹的自定义动画。 (框架 / 动画)
- awesome-flutter-cn - Simple Animations - 简单又酷炫的动画集合,[Felix Blaschke](https://github.com/felixblaschke). (框架 / 动画)
- awesome-flutter - Simple Animations - Flutter package for creating awesome animations. ` 📝 2 months ago ` (Animation [🔝](#readme))
- fucking-awesome-flutter - Simple Animations - Create awesome custom animations easily by [Felix Blaschke](https://github.com/felixblaschke). (Frameworks / Animation)
- awesome-flutter - Simple Animations - Create awesome custom animations easily by [Felix Blaschke](https://github.com/felixblaschke). (Frameworks / Animation)
README
# 🎬 Simple Animations
[](https://pub.dartlang.org/packages/simple_animations)
[](https://github.com/Solido/awesome-flutter)
[](https://fluttergems.dev/animation-transition/)
**Simple Animations** simplifies the process of creating beautiful custom animations:
- Easily create **custom animations in stateless widgets**
- Animate **multiple properties** at once
- Create **staggered animations** within seconds
- Simplified working with **AnimationController** instances
- Debug animations
## Table of Contents
[**Quickstart**](#quickstart)
- [Animation Builder - Quickstart](#animation-builder---quickstart)
- [Movie Tween - Quickstart](#movie-tween---quickstart)
- [Animation Mixin - Quickstart](#animation-mixin---quickstart)
- [Animation Developer Tools - Quickstart](#animation-developer-tools---quickstart)
[**Animation Builder**](#animation-builder)
- [Essential parts of the animation](#essential-parts-of-the-animation)
- [PlayAnimationBuilder](#playanimationbuilder)
- [LoopAnimationBuilder](#loopanimationbuilder)
- [MirrorAnimationBuilder](#mirroranimationbuilder)
- [CustomAnimationBuilder](#customanimationbuilder)
[**Movie Tween**](#movie-tween)
- [Basic usage pattern](#basic-usage-pattern)
- [Scenes](#scenes)
- [Animate properties](#animate-properties)
- [Curves](#curves)
- [Extrapolation](#extrapolation)
- [Use developer tools](#use-developer-tools)
- [Animation duration](#animation-duration)
[**Animation Mixin**](#animation-mixin)
- [Basic usage pattern](#basic-usage-pattern)
- [Create multiple AnimationController](#create-multiple-animationcontroller)
[**Shortcuts for AnimationController**](#shortcuts-for-animationcontroller)
[**Animation Developer Tools**](#animation-developer-tools)
- [Basic usage pattern](#basic-usage-pattern)
- [Features and tricks](#features-and-tricks)
## Quickstart
Directly dive in and let the code speak for itself.
### Animation Builder - Quickstart
Animation Builder are powerful widgets to easily create custom animations.
```dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class ResizeCubeAnimation extends StatelessWidget {
const ResizeCubeAnimation({super.key});
@override
Widget build(BuildContext context) {
// PlayAnimationBuilder plays animation once
return PlayAnimationBuilder(
tween: Tween(begin: 100.0, end: 200.0), // 100.0 to 200.0
duration: const Duration(seconds: 1), // for 1 second
builder: (context, value, _) {
return Container(
width: value, // use animated value
height: value,
color: Colors.blue,
);
},
onCompleted: () {
// do something ...
},
);
}
}
class RotatingBox extends StatelessWidget {
const RotatingBox({super.key});
@override
Widget build(BuildContext context) {
// LoopAnimationBuilder plays forever: from beginning to end
return LoopAnimationBuilder(
tween: Tween(begin: 0.0, end: 2 * pi), // 0° to 360° (2π)
duration: const Duration(seconds: 2), // for 2 seconds per iteration
builder: (context, value, _) {
return Transform.rotate(
angle: value, // use value
child: Container(color: Colors.blue, width: 100, height: 100),
);
},
);
}
}
class ColorFadeLoop extends StatelessWidget {
const ColorFadeLoop({super.key});
@override
Widget build(BuildContext context) {
// MirrorAnimationBuilder plays forever: alternating forward and backward
return MirrorAnimationBuilder(
tween: ColorTween(begin: Colors.red, end: Colors.blue), // red to blue
duration: const Duration(seconds: 5), // for 5 seconds per iteration
builder: (context, value, _) {
return Container(
color: value, // use animated value
width: 100,
height: 100,
);
},
);
}
}
```
[**Read guide**](#animation-builder) or [**watch examples**](example/example.md#animation-builder).
---
### Movie Tween - Quickstart
Movie Tween combines multiple tween into one, including timeline control and value extrapolation.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
// Simple staggered tween
final tween1 = MovieTween()
..tween('width', Tween(begin: 0.0, end: 100),
duration: const Duration(milliseconds: 1500), curve: Curves.easeIn)
.thenTween('width', Tween(begin: 100, end: 200),
duration: const Duration(milliseconds: 750), curve: Curves.easeOut);
// Design tween by composing scenes
final tween2 = MovieTween()
..scene(
begin: const Duration(milliseconds: 0),
duration: const Duration(milliseconds: 500))
.tween('width', Tween(begin: 0.0, end: 400.0))
.tween('height', Tween(begin: 500.0, end: 200.0))
.tween('color', ColorTween(begin: Colors.red, end: Colors.blue))
..scene(
begin: const Duration(milliseconds: 700),
end: const Duration(milliseconds: 1200))
.tween('width', Tween(begin: 400.0, end: 500.0));
// Type-safe alternative
final width = MovieTweenProperty();
final color = MovieTweenProperty();
final tween3 = MovieTween()
..tween(width, Tween(begin: 0.0, end: 100))
..tween(color, ColorTween(begin: Colors.red, end: Colors.blue));
```
[**Read guide**](#movie-tween) or [**watch examples**](example/example.md#movie-tween).
---
### Animation Mixin - Quickstart
The **Animation Mixin** manages `AnimationController` instances for you.
No more boilerplate code.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
_MyWidgetState createState() => _MyWidgetState();
}
// Add AnimationMixin
class _MyWidgetState extends State with AnimationMixin {
late Animation size;
@override
void initState() {
// The AnimationController instance `controller` is already wired up.
// Just connect with it with the tweens.
size = Tween(begin: 0.0, end: 200.0).animate(controller);
controller.play(); // start the animation playback
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
width: size.value, // use animated value
height: size.value,
color: Colors.red,
);
}
}
```
[**Read guide**](#animation-mixin) or [**watch examples**](example/example.md#animation-mixin).
---
### Animation Developer Tools - Quickstart
Helps you fine tuning the animation. It allows you to pause anywhere, scroll around, speed up, slow down or focus on a certain part of the animation.

[**Read guide**](#animation-developer-tools)
## Animation Builder
Animation Builder enables developers to craft custom animations with simple widgets.
### Essential parts of the animation
You need three things to create an animation:
- **tween**: What _value_ is changing within the animation?
- **duration**: How long does the animation take?
- **builder**: How does the UI look like regarding the changing _value_?
#### Tween
The `tween` is the description of your animation. Mostly it will change a value from A to B. Tweens describe **what** will happen but **not how fast it will happen**.
```dart
import 'package:flutter/material.dart';
// Animate a color from red to blue
var colorTween = ColorTween(begin: Colors.red, end: Colors.blue);
// Animate a double value from 0 to 100
var doubleTween = Tween(begin: 0.0, end: 100.0);
```
To animate multiple properties, use a [Movie Tween](#movie-tween).
#### Duration
The `duration` is the time the animation takes.
#### Builder
The `builder` is a function that is called for **each new rendered frame** of your animation. It takes three parameters: `context`, `value` and `child`.
- `context` is your Flutter `BuildContext`
- `value` is **current value** of any animated variable, produced by the tween. If your tween is `Tween(begin: 0.0, end: 100.0)`, the `value` is a `double` somewhere between `0.0` and `100.0`.
- `child` can be a widget that you might pass in a Animation Builder widget. This widget stays constant and is not affected by the animation.
How often the `builder` function is called, depends on the animation duration, and the framerate of the device used.
### PlayAnimationBuilder
The PlayAnimationBuilder is a widget that plays an animation once.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
// Use type `Color?` because ColorTween produces type `Color?`
var widget = PlayAnimationBuilder(
tween: ColorTween(begin: Colors.red, end: Colors.blue), // define tween
duration: const Duration(seconds: 5), // define duration
builder: (context, value, _) {
return Container(
color: value, // use animated color
width: 100,
height: 100,
);
},
);
```
#### Delay
By default, animations will play automatically. You can set a `delay` to make `PlayAnimationBuilder` wait for a given amount of time.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder(
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
delay: const Duration(seconds: 2), // add delay
builder: (context, value, _) {
return Container(
color: value,
width: 100,
height: 100,
);
},
);
```
#### Non-linear motion
You can make your animation more appealing by applying non-linear motion behavior to it.
Just pass a `curve` into the widget.
Flutter comes with a set of predefined curves inside the `Curves` class.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder(
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
curve: Curves.easeInOut, // specify curve
builder: (context, value, _) {
return Container(
color: value,
width: 100,
height: 100,
);
},
);
```
#### Animation lifecycle
You can react to the animation status by setting callbacks.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder(
// lifecycle callbacks
onStarted: () => debugPrint('Animation started'),
onCompleted: () => debugPrint('Animation complete'),
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
builder: (context, value, _) =>
Container(color: value, width: 100, height: 100),
);
```
#### Using child widgets
Parts of the UI that are not effected by the animated value can be passed as a `Widget` into the `child` property. That `Widget` is available within the `builder` function.
They will not rebuild when animated value changes and therefore has a positive performance impact.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder(
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
// child gets passed into builder function
builder: (context, value, child) {
return Container(
color: value,
width: 100,
height: 100,
child: child, // use child
);
},
child: const Text('Hello World'), // specify child widget
);
```
#### Using keys
If Flutter swaps out a `PlayAnimationBuilder` with another different `PlayAnimationBuilder` in a rebuild, it may recycle the first one.
This may lead to a undesired behavior.
In such a case use the `key` property.
You may [watch this introduction](https://www.youtube.com/watch?v=kn0EOS-ZiIc) to `Key`.
#### App example
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void main() => runApp(
const MaterialApp(home: Scaffold(body: Center(child: AnimatedGreenBox()))));
class AnimatedGreenBox extends StatelessWidget {
const AnimatedGreenBox({super.key});
@override
Widget build(BuildContext context) {
return PlayAnimationBuilder(
// specify tween (from 50.0 to 200.0)
tween: Tween(begin: 50.0, end: 200.0),
// set a duration
duration: const Duration(seconds: 5),
// set a curve
curve: Curves.easeInOut,
// use builder function
builder: (context, value, child) {
// apply animated value obtained from builder function parameter
return Container(
width: value,
height: value,
color: Colors.green,
child: child,
);
},
child: const Text('Hello World'),
);
}
}
```
### LoopAnimationBuilder
A `LoopAnimationBuilder` repeatedly plays the animation from the start to the end over and over again.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = LoopAnimationBuilder(
// mandatory parameters
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
builder: (context, value, child) {
return Container(color: value, width: 100, height: 100, child: child);
},
// optional parameters
curve: Curves.easeInOut,
child: const Text('Hello World'),
);
```
### MirrorAnimationBuilder
A `MirrorAnimationBuilder` repeatedly plays the animation from the start to the end, then reverse to the start, then again forward and so on.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = MirrorAnimationBuilder(
// mandatory parameters
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
builder: (context, value, child) {
return Container(color: value, width: 100, height: 100, child: child);
},
// optional parameters
curve: Curves.easeInOut,
child: const Text('Hello World'),
);
```
### CustomAnimationBuilder
Use `CustomAnimationBuilder` if the animation widgets discussed above aren't sufficient for you use case. Beside all parameters mentioned for `PlayAnimationBuilder` it allows you actively control the animation.
#### Control the animation
The `control` parameter can be set to the following values:
| Control.VALUE | Description |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `stop` | Stops the animation at the current position. |
| `play` | Plays the animation from the current position to the end. |
| `playReverse` | Plays the animation from the current position reverse to the start. |
| `playFromStart` | Resets the animation position to the beginning (`0.0`) and starts playing to the end. |
| `playReverseFromEnd` | Resets the position of the animation to end (`1.0`) and starts playing backwards to the start. |
| `loop` | Endlessly plays the animation from the start to the end. |
| `mirror` | Endlessly plays the animation from the start to the end, then it plays reverse to the start, then forward again and so on. |
You can bind the `control` value to state variable and change it during the animation. The `CustomAnimationBuilder` will adapt to that.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void main() => runApp(
const MaterialApp(home: Scaffold(body: Center(child: SwappingButton()))));
class SwappingButton extends StatefulWidget {
const SwappingButton({super.key});
@override
_SwappingButtonState createState() => _SwappingButtonState();
}
class _SwappingButtonState extends State {
var control = Control.play; // define variable
void _toggleDirection() {
setState(() {
// let the animation play to the opposite direction
control = control == Control.play ? Control.playReverse : Control.play;
});
}
@override
Widget build(BuildContext context) {
return CustomAnimationBuilder(
control: control, // bind variable with control instruction
tween: Tween(begin: -100.0, end: 100.0),
duration: const Duration(seconds: 1),
builder: (context, value, child) {
// moves child from left to right
return Transform.translate(
offset: Offset(value, 0),
child: child,
);
},
child: OutlinedButton(
// clicking button changes animation direction
onPressed: _toggleDirection,
child: const Text('Swap'),
),
);
}
}
```
#### Start position
By default the animation starts from the beginning (`0.0`). You can change this by setting the `startPosition` parameter. It can be set to a value between `0.0` (beginning) and `1.0` (end).
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = CustomAnimationBuilder(
control: Control.play,
startPosition: 0.5, // set start position at 50%
duration: const Duration(seconds: 5),
tween: ColorTween(begin: Colors.red, end: Colors.blue),
builder: (context, value, child) {
return Container(color: value, width: 100, height: 100);
},
);
```
#### Animation lifecycle
You can react to the animation status by setting callbacks.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = CustomAnimationBuilder(
// lifecycle callbacks
onStarted: () => debugPrint('Animation started'),
onCompleted: () => debugPrint('Animation complete'),
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
builder: (context, value, child) {
return Container(color: value, width: 100, height: 100);
},
);
```
It's also possible to directly access the `AnimationStatusListener` of the internal `AnimationController`.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = CustomAnimationBuilder(
animationStatusListener: (AnimationStatus status) {
// provide listener
if (status == AnimationStatus.completed) {
debugPrint('Animation completed!');
}
},
tween: ColorTween(begin: Colors.red, end: Colors.blue),
duration: const Duration(seconds: 5),
builder: (context, value, child) {
return Container(color: value, width: 100, height: 100);
},
);
```
## Movie Tween
Movie Tween combines multiple tween into one, including timeline control and value extrapolation.
### Basic usage pattern
Create a new `MovieTween` and use the `tween()` to tween multiples values:
```dart
final tween = MovieTween();
tween.tween('width', Tween(begin: 0.0, end: 100.0),
duration: const Duration(milliseconds: 700));
tween.tween('height', Tween(begin: 100.0, end: 200.0),
duration: const Duration(milliseconds: 700));
```
You can use `..` to get a nice builder style syntax:
```dart
final tween = MovieTween()
..tween('width', Tween(begin: 0.0, end: 100.0),
duration: const Duration(milliseconds: 700))
..tween('height', Tween(begin: 100.0, end: 200.0),
duration: const Duration(milliseconds: 700));
```
In order to not repeat yourself, you can use `scene()` to create an explicit scene and apply both tween to it:
```dart
final tween = MovieTween();
tween.scene(duration: const Duration(milliseconds: 700))
..tween('width', Tween(begin: 0.0, end: 100.0))
..tween('height', Tween(begin: 100.0, end: 200.0));
```
Calling `tween()` creates a scene as well. Therefore you can just call `thenTween()` to create staggered animations.
```dart
final tween = MovieTween();
tween
.tween('width', Tween(begin: 0.0, end: 100.0),
duration: const Duration(milliseconds: 700))
.thenTween('width', Tween(begin: 100.0, end: 200.0),
duration: const Duration(milliseconds: 500));
```
You can use e.g. a `PlayAnimationBuilder` to bring the `MovieTween` alive:
```dart
@override
Widget build(BuildContext context) {
// create tween
var tween = MovieTween()
..scene(duration: const Duration(milliseconds: 700))
.tween('width', Tween(begin: 0.0, end: 100.0))
.tween('height', Tween(begin: 300.0, end: 200.0));
return PlayAnimationBuilder(
tween: tween, // provide tween
duration: tween.duration, // total duration obtained from MovieTween
builder: (context, value, _) {
return Container(
width: value.get('width'), // get animated width value
height: value.get('height'), // get animated height value
color: Colors.yellow,
);
},
);
}
```
`MovieTween` animates to `Movie` that offers you a `get()` method to obtain a single animated value.
### Scenes
A `MovieTween` can consist of multiple scenes with each scene having multiple tweened properties. Those scenes can be created
- implicitly using `tween()` or `thenTween()`,
- explicitly using `scene()` or `thenFor()`.
```dart
final tween = MovieTween();
// implicit scenes
final sceneA1 = tween.tween('x', Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 700));
final sceneA2 = sceneA1.thenTween('x', Tween(begin: 1.0, end: 2.0),
duration: const Duration(milliseconds: 500));
// explicit scenes
final sceneB1 = tween
.scene(duration: const Duration(milliseconds: 700))
.tween('x', Tween(begin: 0.0, end: 1.0));
final sceneB2 = sceneA1
.thenFor(duration: const Duration(milliseconds: 500))
.tween('x', Tween(begin: 1.0, end: 2.0));
```
#### Absolute scenes
You can add scenes anywhere in the timeline of your tween by using `tween.scene()`. You just need to provide two of these parameters:
- `begin` (start time of the scene)
- `duration` (duration of the scene)
- `end` (end time of the scene)
```dart
final tween = MovieTween();
// start at 0ms and end at 1500ms
final scene1 = tween.scene(
duration: const Duration(milliseconds: 1500),
);
// start at 200ms and end at 900ms
final scene2 = tween.scene(
begin: const Duration(milliseconds: 200),
duration: const Duration(milliseconds: 700),
);
// start at 700ms and end at 1400ms
final scene3 = tween.scene(
begin: const Duration(milliseconds: 700),
end: const Duration(milliseconds: 1400),
);
// start at 1000ms and end at 1600ms
final scene4 = tween.scene(
duration: const Duration(milliseconds: 600),
end: const Duration(milliseconds: 1600),
);
```
#### Relative scenes
You can also make scenes depend on each other by using `thenFor()`.
```dart
final tween = MovieTween();
final firstScene = tween
.scene(
begin: const Duration(seconds: 0),
duration: const Duration(seconds: 2),
)
.tween('x', ConstantTween(0));
// secondScene references the firstScene
final secondScene = firstScene
.thenFor(
delay: const Duration(milliseconds: 200),
duration: const Duration(seconds: 2),
)
.tween('x', ConstantTween(1));
```
It also possible to add an optional `delay` to add further time between the scenes.
#### Hint on code style
By using builder style Dart syntax and comments you can easily create a well-readable animation.
```dart
MovieTween()
/// fade in
.scene(
begin: const Duration(seconds: 0),
duration: const Duration(milliseconds: 300),
)
.tween('x', Tween(begin: 0.0, end: 100.0))
.tween('y', Tween(begin: 0.0, end: 200.0))
/// grow
.thenFor(duration: const Duration(milliseconds: 700))
.tween('x', Tween(begin: 100.0, end: 200.0))
.tween('y', Tween(begin: 200.0, end: 400.0))
/// fade out
.thenFor(duration: const Duration(milliseconds: 300))
.tween('x', Tween(begin: 200.0, end: 0.0))
.tween('y', Tween(begin: 400.0, end: 0.0));
```
### Animate properties
You can use `tween()` to specify a tween for single property.
```dart
final tween = MovieTween();
final scene = tween.scene(end: const Duration(seconds: 1));
scene.tween('width', Tween(begin: 0.0, end: 100.0));
scene.tween('color', ColorTween(begin: Colors.red, end: Colors.blue));
```
You can fine tune the timing with `shiftBegin` or `shiftEnd` for each property.
```dart
scene.tween('width', Tween(begin: 0.0, end: 100.0),
shiftBegin: const Duration(milliseconds: 200), // start later
shiftEnd: const Duration(milliseconds: -200) // end earlier
);
```
### Curves
You can customize the default easing curve at MovieTween, scene or property tween level.
```dart
final tween = MovieTween(curve: Curves.easeIn);
// scene1 will use Curves.easeIn defined by the MovieTween
final scene1 = tween.scene(duration: const Duration(seconds: 1));
// scene2 will use Curves.easeOut
final scene2 =
tween.scene(duration: const Duration(seconds: 1), curve: Curves.easeOut);
// will use Curves.easeIn defined by the MovieTween
scene1.tween('value1', Tween(begin: 0.0, end: 100.0));
// will use Curves.easeOut defined by scene2
scene2.tween('value2', Tween(begin: 0.0, end: 100.0));
// will use Curves.easeInOut defined by property tween
scene2.tween('value3', Tween(begin: 0.0, end: 100.0),
curve: Curves.easeInOut);
```
### Extrapolation
All values that are not explicitly set in the timeline will be extrapolated.
```dart
final tween = MovieTween()
// implicitly use 100.0 for width values from 0.0s - 1.0s
// 1.0s - 2.0s
..scene(
begin: const Duration(seconds: 1),
duration: const Duration(seconds: 1),
).tween('width', Tween(begin: 100.0, end: 200.0))
// implicitly use 200.0 for width values from 2.0s - 3.0s
// 3.0s - 4.0s
..scene(
begin: const Duration(seconds: 3),
end: const Duration(seconds: 4),
).tween('height', Tween(begin: 400.0, end: 500.0));
```
### Use developer tools
Creating complex tweens with multiple or staggered properties can be time consuming to create and maintain. I recommend using the [**Animation Developer Tools**](#animation-developer-tools) to streamline this process.

### Animation duration
Normally an `Animatable` or `Tween` doesn't contain a duration information. But `MovieTween` class contains a `duration` property that contains the total duration of the animation.
```dart
@override
Widget build(BuildContext context) {
final tween = MovieTween()
..tween('width', Tween(begin: 0.0, end: 100.0),
duration: const Duration(milliseconds: 700))
..tween('height', Tween(begin: 300.0, end: 200.0),
duration: const Duration(milliseconds: 700));
return PlayAnimationBuilder(
tween: tween,
duration: tween.duration, // use duration from MovieTween
builder: (context, value, _) {
return Container(
width: value.get('width'),
height: value.get('height'),
color: Colors.yellow,
);
},
);
}
```
_Hint: You can also multiply the `duration` value with a numeric factor in order to speed up or slow down an animation._
Of cause you can also use an own `Duration` for the animation.
## Animation Mixin
It reduces boilerplate code when using `AnimationController` instances.
### Basic usage pattern
Create an `AnimationController` just by adding `AnimationMixin` to your stateful widget:
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({super.key});
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
// Add AnimationMixin to state class
class _MyAnimatedWidgetState extends State
with AnimationMixin {
late Animation size;
@override
void initState() {
size = Tween(begin: 0.0, end: 200.0).animate(controller);
controller.play();
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(width: size.value, height: size.value, color: Colors.red);
}
}
```
💪 The `AnimationMixin` generates a preconfigured AnimationController as `controller`. You can just use it. No need to worry about initialization or disposing.
### Create multiple AnimationController
With multiple AnimationController you can have many parallel animations at the same time.
Anicoto's `AnimationMixin` enhances your **state class** with a method `createController()` to create multiple **managed\*** AnimationController. _("Managed" means that you don't need to care about initialization and disposing.)_
#### Create a managed AnimationController
First create a class variable of type `AnimationController`. Then inside the `initState() {...}` method call `createController()`. That's all.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({super.key});
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
// use AnimationMixin
class _MyAnimatedWidgetState extends State
with AnimationMixin {
late AnimationController sizeController; // declare custom AnimationController
late Animation size;
@override
void initState() {
sizeController = createController(); // create custom AnimationController
size = Tween(begin: 0.0, end: 200.0).animate(sizeController);
sizeController.play(duration: const Duration(seconds: 5));
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(width: size.value, height: size.value, color: Colors.red);
}
}
```
#### Create many managed AnimationController
Anicoto allows you to have as many AnimationController you want. Behind the scenes it keeps track of them.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({super.key});
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State
with AnimationMixin {
late AnimationController widthController;
late AnimationController heightController;
late AnimationController colorController;
late Animation width;
late Animation height;
late Animation color;
@override
void initState() {
widthController = createController()
..mirror(duration: const Duration(seconds: 5));
heightController = createController()
..mirror(duration: const Duration(seconds: 3));
colorController = createController()
..mirror(duration: const Duration(milliseconds: 1500));
width = Tween(begin: 100.0, end: 200.0).animate(widthController);
height = Tween(begin: 100.0, end: 200.0).animate(heightController);
color = ColorTween(begin: Colors.red, end: Colors.blue)
.animate(colorController);
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
width: width.value, height: height.value, color: color.value);
}
}
```
## Shortcuts for AnimationController
The extension for `AnimationController` adds four convenience functions:
- `controller.play()` plays animation and stops at the end.
- `controller.playReverse()` plays animation reversed and stops at the start.
- `controller.loop()` repetitively plays the animation from start to the end.
- `controller.mirror()` repetitively plays the animation forward, then backwards, then forward and so on.
Each of these methods take an optional `duration` named parameter to configure your animation action within one line of code.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void someFunction(AnimationController controller) {
controller.play(duration: const Duration(milliseconds: 1500));
controller.playReverse(duration: const Duration(milliseconds: 1500));
controller.loop(duration: const Duration(milliseconds: 1500));
controller.mirror(duration: const Duration(milliseconds: 1500));
}
```
You can use these methods nicely along the already existing `controller.stop()` and `controller.reset()` methods.
## Animation Developer Tools
The Animation Developer Tools allow you to create or review your animation step by step.
### Basic usage pattern
Wrap your UI with the `AnimationDeveloperTools` widget.
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyPage extends StatelessWidget {
const MyPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// put DevTools very high in the widget hierarchy
body: AnimationDeveloperTools(
child: Container(), // your UI
),
);
}
}
```
Enable developer mode on the animation you want to debug.
#### Using Animation Builder widgets
The Animation Builder widgets
- `PlayAnimationBuilder`
- `LoopAnimationBuilder`
- `MirrorAnimationBuilder`
- `CustomAnimationBuilder`
have a constructor parameter `developerMode` that can be set to `true`. It will connect to the closest `AnimationDeveloperTools` widget.
**Example**
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void main() => runApp(const MaterialApp(home: Scaffold(body: MyPage())));
class MyPage extends StatelessWidget {
const MyPage({super.key});
@override
Widget build(BuildContext context) {
return SafeArea(
// put DevTools very high in the widget hierarchy
child: AnimationDeveloperTools(
child: Center(
child: PlayAnimationBuilder(
tween: Tween(begin: 0.0, end: 100.0),
duration: const Duration(seconds: 1),
developerMode: true, // enable developer mode
builder: (context, value, child) {
return Container(
width: value,
height: value,
color: Colors.blue,
);
},
),
),
),
);
}
}
```

#### Using Animation Mixin
If your stateful widget uses `AnimationMixin` to manage your instances of `AnimationController` you can call `enableDeveloperMode()` to connect to the clostest `AnimationDeveloperMode` widget.
**Example**
```dart
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: SafeArea(
// put DevTools very high in the widget hierarchy
child: AnimationDeveloperTools(
child: Center(
child: MyAnimation(),
),
),
),
),
);
}
}
class MyAnimation extends StatefulWidget {
const MyAnimation({super.key});
@override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State with AnimationMixin {
late Animation size;
@override
void initState() {
size = Tween(begin: 0.0, end: 100.0).animate(controller);
enableDeveloperMode(controller); // enable developer mode
controller.forward();
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(width: size.value, height: size.value, color: Colors.blue);
}
}
```
### Features and tricks
The Animation Developer Tools come with several features that simplify your developer life:
- Regardless of the real animation, with developer mode activated the animation will always loop.
- You can use Flutter hot reloading for editing and debugging if your tween is created stateless.
- Use the slider to edit the animated scene while pausing.
- You can slow down the animation to look out for certain details.
- Use the interval buttons to focus on a time span of the animation.