This tutorial shows you how to create animation using AnimatedWidget
in Flutter.
Flutter has a class named AnimatedWidget
. It's a widget that rebuilds each time its Listenable
changes value. That behavior makes it possible for us to create a custom animation by creating a class that extends AnimatedWidget
and providing the Listenable
.
Using AnimatedWidget
First, we need to create a class that extends AnimatedWidget
and override the build
method. The widget's constructor needs to call its superclass' constructor which is shown below.
const AnimatedWidget({
Key key,
@required this.listenable,
})
That means it's required to pass a listenable
argument whose type is Listenable
. How to get the value that can be passed as listenable
will be explained later.
Assuming the listenable
is already passed, we can access it within the class that extends AnimatedWidget. It can be any instance whose class extends Listenable
. In this tutorial, we are going to use an AnimationController
which is a common type to be passed as the Listenable
.
AnimatedWidget
performs rebuild when the value of its Listenable
changes. In the following example, we want to get the value of the Animation
to apply rotation transformation. The current animation value is used to determine the rotation angle. At each animation tick, the animation value changes and therefore the rotation angle also changes.
class RotatingSquare extends AnimatedWidget {
const RotatingSquare({Key? key, required AnimationController controller})
: super(key: key, listenable: controller);
Animation<double> get _progress => listenable as Animation<double>;
@override
Widget build(BuildContext context) {
return Transform.rotate(
angle: _progress.value * 2.0 * math.pi,
child: Container(
width: 200.0,
height: 200.0,
color: Colors.teal,
child: const Center(
child: Text(
'Woolha.com',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 24,
),
),
),
),
);
}
In order to create the create an instance of RotatingSquare
class above, we have to pass an AnimationController
. We need to create a class that extends State
and use TickerProviderStateMixin
. That makes it possible to use this
keyword to be passed as vsync
when calling the constructor of AnimationController
. The animation needs to be started, such as by using forward()
, reverse()
, or repeat()
. Having created the AnimationController
, now it becomes possible to call the constructor of _RotationgSquare
.
class _AnimatedWidgetExample extends StatefulWidget {
@override
State<StatefulWidget> createState() =>
new _AnimatedWidgetExampleState();
}
class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample> with TickerProviderStateMixin {
late AnimationController _controller;
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 5),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Woolha.com Flutter Tutorial'),
),
body: Center(
child: RotatingSquare(controller: _controller),
),
);
}
}
Output:
Full Code
Here's the full code of this tutorial.
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Woolha.com Flutter Tutorial',
home: _AnimatedWidgetExample(),
);
}
}
class _AnimatedWidgetExample extends StatefulWidget {
@override
State<StatefulWidget> createState() =>
new _AnimatedWidgetExampleState();
}
class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample> with TickerProviderStateMixin {
late AnimationController _controller;
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 5),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Woolha.com Flutter Tutorial'),
),
body: Center(
child: RotatingSquare(controller: _controller),
),
);
}
}
class RotatingSquare extends AnimatedWidget {
const RotatingSquare({Key? key, required AnimationController controller})
: super(key: key, listenable: controller);
Animation<double> get _progress => listenable as Animation<double>;
@override
Widget build(BuildContext context) {
return Transform.rotate(
angle: _progress.value * 2.0 * math.pi,
child: Container(
width: 200.0,
height: 200.0,
color: Colors.teal,
child: const Center(
child: Text(
'Woolha.com',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 24,
),
),
),
),
);
}
}
Another way to create animation is using AnimatedBuilder
. Unlike AnimatedWidget
that requires us to create a class for the widget to be animated, AnimatedBuilder
can be used to create an animation as a part of a larger build function.