Creating Animations with AnimationController
1. Understanding AnimationController Class
| Property/Method | Description | Notes |
|---|---|---|
| AnimationController | Manages the state and timing of animations. | Core class for controlling animations in Flutter. |
| duration | Duration of the animation. | Defaults to 1 second. Can be set to Duration(milliseconds: 500). |
| value | Current value of the animation (0.0 to 1.0). | Read-only property. Updated automatically during animation. |
| forward() | Starts the animation from 0.0 to 1.0. | Returns a Future that completes when animation finishes. |
| reverse() | Reverses the animation from 1.0 to 0.0. | Useful for creating bounce or return effects. |
| reset() | Resets the animation to 0.0. | Does not stop the animation if it's running. |
| dispose() | Releases resources used by the controller. | Must be called to prevent memory leaks in widgets. |
Example: Creating an AnimationController
import 'package:flutter/material.dart';
class MyAnimatedWidget extends StatefulWidget {
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with TickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Container(
width: 100 + controller.value * 100,
height: 100 + controller.value * 100,
color: Colors.blue.withOpacity(controller.value),
);
},
);
}
}
Note: The
vsync parameter is required for AnimationController and should typically be set to this when using TickerProviderStateMixin.
Warning: Always call
dispose() on the controller to prevent memory leaks.
2. Creating AnimationController
Example: Basic AnimationController Setup
<AnimationController> controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
| Property | Values | Description |
|---|---|---|
| duration | Duration |
Length of the animation in time |
| vsync | TickerProvider |
Required for animations; typically this in widgets |
| lowerBound | double |
Minimum value of the animation (default: 0.0) |
| upperBound | double |
Maximum value of the animation (default: 1.0) |
| value | double |
Current value of the animation (0.0 to 1.0) |
Note: The
vsync parameter is required to prevent animation drift and ensure proper synchronization with the display refresh rate.
Example: AnimationController with TickerProviderStateMixin
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
3. Using TickerProvider
| Property | Description | Usage |
|---|---|---|
| TickerProvider | Interface for creating Ticker objects that drive animations. |
Implement in widgets that manage animation controllers. |
| Ticker | Object that provides time-based updates to animation controllers. | Used internally by AnimationController to drive animations. |
Example: Implementing TickerProvider
class MyAnimatedWidget extends StatefulWidget {
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with TickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this, // TickerProvider
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Transform.rotate(
angle: controller.value * 2 * pi,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
},
);
}
}
Note:
with TickerProviderStateMixin provides the vsync parameter required by AnimationController.
4. Controlling Animations
| Method | Description | Usage |
|---|---|---|
| forward() | Starts or resumes the animation in the forward direction | controller.forward(); |
| reverse() | Starts or resumes the animation in the reverse direction | controller.reverse(); |
| stop() | Stops the animation and resets the animation position to 0.0 | controller.stop(); |
| pause() | Pauses the animation at its current position | controller.pause(); |
| reset() | Resets the animation to its initial state (0.0) | controller.reset(); |
| animate() | Starts the animation from a specific position | controller.animate(0.5); |
Note: Use
forward() to begin or continue an animation. Use reverse() to play it backward.Warning: Calling
stop() resets the animation to 0.0, which may not be desired if you want to resume from a specific point.5. Using Animation Status
| Status | Description | Usage |
|---|---|---|
| AnimationStatus.dismissed | Animation is dismissed (ended at the start position) | Used to detect when animation completes and returns to initial state |
| AnimationStatus.forward | Animation is playing forward | Useful for tracking progress during forward animation |
| AnimationStatus.reverse | Animation is playing in reverse | Used to detect when animation plays backward |
| AnimationStatus.completed | Animation is completed (ended at the end position) | Triggers actions when animation finishes |
Example: Listening to Animation Status
final animationController = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
animationController.addStatusListener((status) {
switch (status) {
case AnimationStatus.forward:
print('Animation playing forward');
break;
case AnimationStatus.reverse:
print('Animation playing in reverse');
break;
case AnimationStatus.completed:
print('Animation completed');
break;
case AnimationStatus.dismissed:
print('Animation dismissed');
break;
}
});
Note: The
addStatusListener callback is called whenever the animation status changes, allowing for real-time updates or triggers based on animation state.6. Using Animation Values
| Property | Description | Values | Notes |
|---|---|---|---|
| value | Current value of the animation | double |
Read-only property, updated during animation |
| isAnimating | Indicates if the animation is currently running | bool |
True when animation is in progress |
| status | Current status of the animation | AnimationStatus |
Can be AnimationStatus.dismissed, AnimationStatus.forward, AnimationStatus.reverse, or AnimationStatus.completed |
Example: Using Animation Values in a Widget
class AnimatedWidgetExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
final animationController = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
return AnimatedBuilder(
animation: animationController,
builder: (context, child) {
return Container(
width: animationController.value * 200,
height: animationController.value * 100,
color: Color.lerp(
Colors.blue,
Colors.red,
animationController.value,
),
);
},
);
}
}
Note: The
value property of an AnimationController ranges from 0.0 to 1.0 during a typical animation cycle.
Animation Value Usage Summary
- value is used to get the current progress of an animation (0.0 to 1.0)
- isAnimating helps determine if an animation is currently running
- status provides the current state of the animation (forward, reverse, completed, dismissed)
7. Disposing AnimationControllers
Example: Properly disposing AnimationController
class AnimatedWidget extends StatefulWidget {
@override
_AnimatedWidgetState createState() => _AnimatedWidgetState();
}
class _AnimatedWidgetState extends State<AnimatedWidget> with TickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
}
@override
void dispose() {
controller.dispose(); // Important: dispose the controller
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Container(
width: controller.value * 200,
height: 100,
color: Colors.blue,
);
},
);
}
}
| Property | Description | Notes |
|---|---|---|
| controller.dispose() | Releases all resources used by the AnimationController | Must be called to prevent memory leaks |
Note: Always call
dispose() in the widget's dispose method to free up resources and avoid memory leaks.8. Using Tween Class
| Property | Description | Values | Notes |
|---|---|---|---|
| Tween.begin | Initial value of the animation | double |
Start point for interpolation |
| Tween.end | Final value of the animation | double |
End point for interpolation |
| Tween.evaluate | Evaluates the animation at a given value | double |
Returns interpolated value between begin and end |
Example: Tween Usage
import 'package:flutter/material.dart';
class TweenExample extends StatefulWidget {
@override
_TweenExampleState createState() => _TweenExampleState();
}
class _TweenExampleState extends State<TweenExample> with TickerProviderStateMixin {
late AnimationController controller;
late Tween<double> tween;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
tween = Tween<double>(begin: 0.0, end: 1.0);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
double value = tween.evaluate(controller);
return Container(
width: 100 + value * 100,
height: 100 + value * 100,
color: Color.lerp(Colors.blue, Colors.red, value),
);
},
);
}
}
Note:
Tween is used to interpolate between two values over the duration of an animation.
9. Chaining Tweens
Example: Chaining Tweens
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Chained Tweens")),
body: Center(
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: 1),
duration: Duration(seconds: 2),
builder: (context, value, child) {
return Container(
width: 100 + value * 100,
height: 100 + value * 100,
color: Color.lerp(Colors.blue, Colors.red, value),
);
},
),
),
),
);
}
}
| Property | Description | Values |
|---|---|---|
| tween | Defines the animation range | Tween<double>(begin: 0, end: 1) |
| duration | Duration of the animation | Duration(seconds: 2) |
| builder | Function to build the animated widget | (context, value, child) => Widget |
10. Using CurvedAnimation
| Property | Values | Description |
|---|---|---|
| parent | AnimationController |
The controller that drives the animation. |
| curve | Curve |
The curve to apply to the animation, e.g., Curves.easeInOut. |
| reverseCurve | Curve |
Optional reverse curve for the animation. |
Example: Using CurvedAnimation
final controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
final curvedAnimation = CurvedAnimation(
parent: controller,
curve: Curves.easeInOut,
);
AnimatedBuilder(
animation: curvedAnimation,
builder: (context, child) {
return Transform.scale(
scale: curvedAnimation.value,
);
},
)
Note:
CurvedAnimation modifies the timing of an animation using a curve, allowing for more natural motion.