In Flutter, animations add a layer of polish and engagement to your user interface. Flutter’s animation framework is highly flexible and allows developers to create simple to complex animations. One of the fundamental classes in Flutter’s animation framework is AnimationController
. Understanding how to use AnimationController
provides you with fine-grained control over your animations, enabling you to build customized and interactive experiences. This blog post dives deep into the usage of AnimationController
, including its properties, methods, and best practices.
What is AnimationController?
The AnimationController
is a special type of Animation
that manages the animation’s progression. It allows you to start, stop, reverse, and reset animations. Essentially, it drives the animation forward (or backward) and is crucial for managing the state of the animation. The AnimationController
linearly produces values that range from 0.0 to 1.0, during a given duration. This value can be used to drive various visual properties of your widgets, such as size, opacity, position, etc.
Why Use AnimationController?
- Fine-Grained Control: Provides precise control over animation behavior, including start, stop, reverse, and reset functionalities.
- Custom Animations: Enables the creation of complex, tailored animations beyond simple transitions.
- State Management: Effectively manages the state and progression of an animation, making it predictable and manageable.
- Integration with Tweens: Works seamlessly with
Tween
objects to define specific value changes over time.
How to Use AnimationController
Step 1: Initialize the AnimationController
First, you need to create an instance of AnimationController
within a StatefulWidget
, typically in the initState
method. It’s essential to dispose of the controller when the widget is no longer needed to prevent memory leaks. Therefore, the recommended approach is to manage the controller in StatefulWidget
. The AnimationController
requires a vsync
parameter, which is typically the this
reference if your State
class includes the TickerProviderStateMixin
or SingleTickerProviderStateMixin
.
import 'package:flutter/material.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({Key? key}) : super(key: key);
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State
with SingleTickerProviderStateMixin { // Or TickerProviderStateMixin
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2), // Animation duration
vsync: this, // The ticker provider
);
}
@override
void dispose() {
_controller.dispose(); // Dispose of the controller
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(); // Placeholder, to be replaced
}
}
Step 2: Using Tweens
AnimationController
provides a value from 0.0 to 1.0. To animate a specific property between a specific range, you’ll use a Tween
. A Tween
defines the beginning and ending values of an animation.
late Animation _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween(begin: 100.0, end: 200.0).animate(_controller)
..addListener(() {
setState(() {
// The state that has changed here is our "size" - managed by animation value
});
});
_controller.forward(); // Start the animation
}
In the example above:
- We created a
Tween
that transitions a value from 100.0 to 200.0. - We created an
Animation<double>
called_animation
and attached our animation controller to our tween’sanimate()
function - Added a listener to the
Animation
so it will redraw as the state has changed. This is extremely important. - Then call
_controller.forward()
to start the animation!
Step 3: Building the Animated Widget
Now you can use the _animation.value
in your widget to create an animation. In this example, the height
and width
are being animated by using animation controller.
import 'package:flutter/material.dart';
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({Key? key}) : super(key: key);
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween(begin: 100.0, end: 200.0).animate(_controller)
..addListener(() {
setState(() {
// The state that has changed here is our "size" - managed by animation value
});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: _animation.value,
width: _animation.value,
color: Colors.blue,
),
);
}
}
Step 4: Controlling the Animation
AnimationController
comes with useful functions to control your animation such as:
forward()
: Starts the animation from the beginning.reverse()
: Plays the animation backward from the end.repeat()
: Repeats the animation a specified number of times or indefinitely.stop()
: Stops the animation at its current value.reset()
: Resets the animation to its initial value (0.0).
ElevatedButton(
onPressed: () {
if (_controller.isCompleted) {
_controller.reverse();
} else {
_controller.forward();
}
},
child: const Text('Animate'),
)
The example above starts the animation or reverses it if it’s already completed.
Advanced Techniques
1. Chained Animations
Animations can be chained together to create complex sequences. This involves listening for the completion of one animation and then starting another.
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
// Start the next animation or action
_controller.reverse();
} else if (status == AnimationStatus.dismissed) {
_controller.forward();
}
});
2. Curved Animations
You can apply a curve to the animation to make it look more natural or dramatic. Flutter provides several predefined curves like Curves.easeIn
, Curves.easeInOut
, etc.
_animation = Tween(begin: 100.0, end: 200.0).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
),
);
3. Custom Tween
Flutter also support to use Custom Tween
by extending Tween<T>
class. Here an example for creating Custom Color Tween
.
class ColorTween extends Tween {
ColorTween({Color? begin, Color? end}) : super(begin: begin, end: end);
@override
Color? lerp(double t) {
if (begin == null || end == null) {
return begin ?? end;
}
return Color.lerp(begin, end, t);
}
}
//Usage
late Animation _colorAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_colorAnimation = ColorTween(begin: Colors.red, end: Colors.blue).animate(_controller);
..addListener(() {
setState(() {
// The state that has changed here is our "color" - managed by animation value
});
});
}
//Widget
Container(
height: _animation.value,
width: _animation.value,
color: _colorAnimation.value,
),
Best Practices
- Dispose Controller: Always dispose of the
AnimationController
in thedispose
method to prevent memory leaks. - Use Curves: Incorporate curves to make animations look more natural.
- Optimize Performance: Be mindful of the performance impact of animations, especially complex ones.
- Use
AnimatedWidget
orAnimatedBuilder
: For complex animations, useAnimatedWidget
orAnimatedBuilder
to optimize the rebuild process. - Combine with Hero: If you want to animate a shared Widget between 2 different screens, the easiest option is by using Hero Widget.
Conclusion
The AnimationController
is a pivotal component of Flutter’s animation framework, granting you the ability to manage and fine-tune animations with precision. By understanding and utilizing the various functionalities of AnimationController
—such as controlling animation state, chaining animations, and incorporating curves—you can craft visually appealing and highly engaging user interfaces. Properly implemented animations elevate the user experience, making your Flutter applications stand out.