Top Related Projects
Flutter runtime for Rive
Load and get full control of your Rive files in a Flutter project using this library.
Render After Effects animations natively on Android and iOS, Web, and React Native
swipe display drawer with flowing & bouncing effects.
A collection of useful packages maintained by the Flutter team
Quick Overview
Simple Animations is a Flutter package that provides an easy-to-use framework for creating custom animations. It offers a variety of pre-built animations and tools to create complex, custom animations with minimal code. The library is designed to simplify the process of adding animations to Flutter applications.
Pros
- Easy to use and integrate into existing Flutter projects
- Provides a wide range of pre-built animations
- Offers flexibility for creating custom, complex animations
- Well-documented with numerous examples and tutorials
Cons
- May have a slight learning curve for developers new to animation concepts
- Some advanced features might require deeper understanding of Flutter's animation system
- Performance impact on complex animations in resource-constrained devices
Code Examples
- Creating a simple fade-in animation:
PlayAnimation<double>(
tween: Tween<double>(begin: 0.0, end: 1.0),
duration: const Duration(seconds: 1),
builder: (context, child, value) {
return Opacity(
opacity: value,
child: child,
);
},
child: const Text('Fade In Text'),
)
- Implementing a custom animation using CustomAnimation:
CustomAnimation<double>(
control: Control.mirror,
tween: Tween<double>(begin: -100, end: 100),
duration: const Duration(seconds: 2),
builder: (context, child, value) {
return Transform.translate(
offset: Offset(value, 0),
child: child,
);
},
child: const FlutterLogo(size: 50),
)
- Using MultiTween for complex animations:
MultiTween<AniProps>()
..add(AniProps.opacity, Tween<double>(begin: 0.0, end: 1.0))
..add(AniProps.translateY, Tween<double>(begin: 100.0, end: 0.0))
Getting Started
To use Simple Animations in your Flutter project:
-
Add the dependency to your
pubspec.yaml
:dependencies: simple_animations: ^5.0.0+3
-
Import the package in your Dart file:
import 'package:simple_animations/simple_animations.dart';
-
Start using the animations in your widgets, for example:
PlayAnimation<double>( tween: Tween<double>(begin: 0.0, end: 1.0), duration: const Duration(seconds: 1), builder: (context, child, value) { return Opacity( opacity: value, child: child, ); }, child: const Text('Animated Text'), )
Competitor Comparisons
Flutter runtime for Rive
Pros of Rive Flutter
- Provides a complete animation ecosystem with a dedicated editor
- Supports complex, interactive animations with state machines
- Offers runtime manipulation of animations
Cons of Rive Flutter
- Steeper learning curve due to the Rive editor and concepts
- Larger file size and potential performance overhead for simple animations
- Requires external tooling (Rive editor) for creating animations
Code Comparison
Rive Flutter:
final riveFile = await RiveFile.asset('assets/animation.riv');
final artboard = riveFile.mainArtboard;
artboard.addController(SimpleAnimation('idle'));
Simple Animations:
PlayAnimation<double>(
tween: Tween<double>(begin: 0, end: 300),
duration: const Duration(seconds: 2),
builder: (context, child, value) => Container(
width: value,
height: value,
color: Colors.blue,
),
)
Rive Flutter excels in complex, interactive animations with its dedicated editor and runtime manipulation capabilities. However, it has a steeper learning curve and may be overkill for simple animations. Simple Animations offers a more straightforward approach for basic animations directly in code, making it easier to implement and maintain for simpler use cases.
Load and get full control of your Rive files in a Flutter project using this library.
Pros of Flare-Flutter
- Provides a complete design-to-code workflow with the Flare design tool
- Supports complex vector animations and interactive animations
- Offers runtime manipulation of animations
Cons of Flare-Flutter
- Steeper learning curve due to the need to use the Flare design tool
- Limited to vector-based animations
- Requires exporting animations from Flare, which may add complexity to the workflow
Code Comparison
Flare-Flutter:
FlareActor(
"assets/animation.flr",
animation: "idle",
fit: BoxFit.contain,
)
simple_animations:
PlayAnimation<double>(
tween: Tween<double>(begin: 0, end: 200),
duration: const Duration(seconds: 2),
builder: (context, child, value) {
return Container(width: value, height: value, color: Colors.blue);
},
)
Flare-Flutter requires using the Flare design tool and exporting animations, while simple_animations allows for creating animations directly in code. Flare-Flutter is better suited for complex vector animations, while simple_animations offers more flexibility for programmatic animations.
Render After Effects animations natively on Android and iOS, Web, and React Native
Pros of Lottie-Android
- Supports complex animations created in Adobe After Effects
- Large ecosystem with tools and plugins for designers
- Extensive documentation and community support
Cons of Lottie-Android
- Larger file size and potentially higher resource usage
- Steeper learning curve for developers unfamiliar with After Effects
- Limited runtime customization of animations
Code Comparison
Simple Animations:
PlayAnimation<double>(
tween: Tween<double>(begin: 0.0, end: 200.0),
duration: const Duration(seconds: 2),
builder: (context, child, value) {
return Container(width: value, height: value, color: Colors.blue);
},
)
Lottie-Android:
val animationView = findViewById<LottieAnimationView>(R.id.animation_view)
animationView.setAnimation(R.raw.animation)
animationView.playAnimation()
Simple Animations offers a more code-centric approach, allowing developers to create animations programmatically. This provides greater flexibility and easier integration with dynamic data. Lottie-Android, on the other hand, relies on pre-designed animations, which can be more visually complex but less adaptable at runtime.
Simple Animations is lightweight and easy to use for simple animations, while Lottie-Android excels in reproducing high-fidelity animations created by designers. The choice between the two depends on the project's requirements, team composition, and desired level of animation complexity.
swipe display drawer with flowing & bouncing effects.
Pros of FlowingDrawer
- Specialized for creating animated drawer menus with a flowing effect
- Provides a ready-to-use UI component for navigation drawers
- Offers customizable animation parameters for the drawer's behavior
Cons of FlowingDrawer
- Limited to drawer animations, less versatile for general-purpose animations
- Less actively maintained, with fewer recent updates
- Smaller community and fewer resources compared to Simple Animations
Code Comparison
FlowingDrawer:
mLeftDrawer = (FlowingDrawer) findViewById(R.id.drawerlayout);
mLeftDrawer.setMenuSize(MenuSize);
mLeftDrawer.setTouchMode(ElasticDrawer.TOUCH_MODE_BEZEL);
mLeftDrawer.setOnDrawerStateChangeListener(new ElasticDrawer.OnDrawerStateChangeListener() {
@Override
public void onDrawerStateChange(int oldState, int newState) {
// Handle state changes
}
});
Simple Animations:
PlayAnimation<double>(
tween: Tween<double>(begin: 0.0, end: 200.0),
duration: const Duration(seconds: 2),
builder: (context, child, value) {
return Container(
width: value,
height: value,
color: Colors.blue,
);
},
)
A collection of useful packages maintained by the Flutter team
Pros of packages
- Comprehensive collection of official Flutter packages
- Maintained by the Flutter team, ensuring high quality and compatibility
- Wide range of functionalities covered, from animations to platform integrations
Cons of packages
- May be overwhelming for beginners due to the large number of packages
- Some packages might be more complex to use compared to simple_animations
- Updates and changes can potentially affect multiple packages simultaneously
Code comparison
simple_animations:
PlayAnimation<double>(
tween: Tween(begin: 0.0, end: 200.0),
duration: const Duration(seconds: 2),
builder: (context, child, value) {
return Container(width: value, height: value, color: Colors.blue);
},
)
packages (using animations package):
AnimatedContainer(
width: _expanded ? 200.0 : 0.0,
height: _expanded ? 200.0 : 0.0,
duration: const Duration(seconds: 2),
curve: Curves.easeInOut,
color: Colors.blue,
)
Summary
While packages offers a comprehensive suite of official Flutter packages, simple_animations provides a more focused and potentially easier-to-use solution for animations. packages benefits from official support and a wide range of functionalities, but may be more complex for specific use cases. simple_animations offers a simpler API for animations but has a narrower scope. The choice between the two depends on the project's requirements and the developer's familiarity with Flutter ecosystem.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
ð¬ Simple Animations
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
- Animation Builder - Quickstart
- Movie Tween - Quickstart
- Animation Mixin - Quickstart
- Animation Developer Tools - Quickstart
- Essential parts of the animation
- PlayAnimationBuilder
- LoopAnimationBuilder
- MirrorAnimationBuilder
- CustomAnimationBuilder
- Basic usage pattern
- Scenes
- Animate properties
- Curves
- Extrapolation
- Use developer tools
- Animation duration
Shortcuts for AnimationController
Quickstart
Directly dive in and let the code speak for itself.
Animation Builder - Quickstart
Animation Builder are powerful widgets to easily create custom animations.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class ResizeCubeAnimation extends StatelessWidget {
const ResizeCubeAnimation({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// PlayAnimationBuilder plays animation once
return PlayAnimationBuilder<double>(
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({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// LoopAnimationBuilder plays forever: from beginning to end
return LoopAnimationBuilder<double>(
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({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// MirrorAnimationBuilder plays forever: alternating forward and backward
return MirrorAnimationBuilder<Color?>(
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,
);
},
);
}
}
Movie Tween - Quickstart
Movie Tween combines multiple tween into one, including timeline control and value extrapolation.
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<double>(begin: 0.0, end: 400.0))
.tween('height', Tween<double>(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<double>(begin: 400.0, end: 500.0));
// Type-safe alternative
final width = MovieTweenProperty<double>();
final color = MovieTweenProperty<Color?>();
final tween3 = MovieTween()
..tween<double>(width, Tween(begin: 0.0, end: 100))
..tween<Color?>(color, ColorTween(begin: Colors.red, end: Colors.blue));
Animation Mixin - Quickstart
The Animation Mixin manages AnimationController
instances for you.
No more boilerplate code.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
@override
_MyWidgetState createState() => _MyWidgetState();
}
// Add AnimationMixin
class _MyWidgetState extends State<MyWidget> with AnimationMixin {
late Animation<double> size;
@override
void initState() {
// The AnimationController instance `controller` is already wired up.
// Just connect with it with the tweens.
size = Tween<double>(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,
);
}
}
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.
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.
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<double>(begin: 0.0, end: 100.0);
To animate multiple properties, use a 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 FlutterBuildContext
-
value
is current value of any animated variable, produced by the tween. If your tween isTween<double>(begin: 0.0, end: 100.0)
, thevalue
is adouble
somewhere between0.0
and100.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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
// Use type `Color?` because ColorTween produces type `Color?`
var widget = PlayAnimationBuilder<Color?>(
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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder<Color?>(
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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder<Color?>(
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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder<Color?>(
// 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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = PlayAnimationBuilder<Color?>(
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 to Key
.
App example
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({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return PlayAnimationBuilder<double>(
// specify tween (from 50.0 to 200.0)
tween: Tween<double>(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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = LoopAnimationBuilder<Color?>(
// 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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = MirrorAnimationBuilder<Color?>(
// 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.
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({Key? key}) : super(key: key);
@override
_SwappingButtonState createState() => _SwappingButtonState();
}
class _SwappingButtonState extends State<SwappingButton> {
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<double>(
control: control, // bind variable with control instruction
tween: Tween<double>(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).
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = CustomAnimationBuilder<Color?>(
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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = CustomAnimationBuilder<Color?>(
// 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
.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
var widget = CustomAnimationBuilder<Color?>(
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:
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:
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:
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.
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:
@override
Widget build(BuildContext context) {
// create tween
var tween = MovieTween()
..scene(duration: const Duration(milliseconds: 700))
.tween('width', Tween<double>(begin: 0.0, end: 100.0))
.tween('height', Tween<double>(begin: 300.0, end: 200.0));
return PlayAnimationBuilder<Movie>(
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()
orthenTween()
, - explicitly using
scene()
orthenFor()
.
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)
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()
.
final tween = MovieTween();
final firstScene = tween
.scene(
begin: const Duration(seconds: 0),
duration: const Duration(seconds: 2),
)
.tween('x', ConstantTween<int>(0));
// secondScene references the firstScene
final secondScene = firstScene
.thenFor(
delay: const Duration(milliseconds: 200),
duration: const Duration(seconds: 2),
)
.tween('x', ConstantTween<int>(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.
MovieTween()
/// fade in
.scene(
begin: const Duration(seconds: 0),
duration: const Duration(milliseconds: 300),
)
.tween('x', Tween<double>(begin: 0.0, end: 100.0))
.tween('y', Tween<double>(begin: 0.0, end: 200.0))
/// grow
.thenFor(duration: const Duration(milliseconds: 700))
.tween('x', Tween<double>(begin: 100.0, end: 200.0))
.tween('y', Tween<double>(begin: 200.0, end: 400.0))
/// fade out
.thenFor(duration: const Duration(milliseconds: 300))
.tween('x', Tween<double>(begin: 200.0, end: 0.0))
.tween('y', Tween<double>(begin: 400.0, end: 0.0));
Animate properties
You can use tween()
to specify a tween for single property.
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.
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.
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.
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<double>(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<double>(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 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.
@override
Widget build(BuildContext context) {
final tween = MovieTween()
..tween('width', Tween<double>(begin: 0.0, end: 100.0),
duration: const Duration(milliseconds: 700))
..tween('height', Tween<double>(begin: 300.0, end: 200.0),
duration: const Duration(milliseconds: 700));
return PlayAnimationBuilder<Movie>(
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:
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({Key? key}) : super(key: key);
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
// Add AnimationMixin to state class
class _MyAnimatedWidgetState extends State<MyAnimatedWidget>
with AnimationMixin {
late Animation<double> size;
@override
void initState() {
size = Tween<double>(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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({Key? key}) : super(key: key);
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
// use AnimationMixin
class _MyAnimatedWidgetState extends State<MyAnimatedWidget>
with AnimationMixin {
late AnimationController sizeController; // declare custom AnimationController
late Animation<double> size;
@override
void initState() {
sizeController = createController(); // create custom AnimationController
size = Tween<double>(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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({Key? key}) : super(key: key);
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State<MyAnimatedWidget>
with AnimationMixin {
late AnimationController widthController;
late AnimationController heightController;
late AnimationController colorController;
late Animation<double> width;
late Animation<double> height;
late Animation<Color?> 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<double>(begin: 100.0, end: 200.0).animate(widthController);
height = Tween<double>(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.
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.
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
class MyPage extends StatelessWidget {
const MyPage({Key? key}) : super(key: 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
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({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SafeArea(
// put DevTools very high in the widget hierarchy
child: AnimationDeveloperTools(
child: Center(
child: PlayAnimationBuilder<double>(
tween: Tween<double>(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
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: 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({Key? key}) : super(key: key);
@override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> with AnimationMixin {
late Animation<double> size;
@override
void initState() {
size = Tween<double>(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.
Top Related Projects
Flutter runtime for Rive
Load and get full control of your Rive files in a Flutter project using this library.
Render After Effects animations natively on Android and iOS, Web, and React Native
swipe display drawer with flowing & bouncing effects.
A collection of useful packages maintained by the Flutter team
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot