Animating CustomPainter
is easy. We just need to repaint the whole canvas on each AnimationController.value
change.
Create AnimationController
in a StatefulWidget
that is parent of the CustomPainter
:
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
AnimationController _animationController;
_MyAppState() {
// Start the animation in 2 seconds after screen open.
Future.delayed(Duration(seconds: 2))
.then((_) => _animationController.forward());
}
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
);
}
@override
void dispose() {
// Properly dispose the controller. This is important!
_animationController.dispose();
super.dispose();
}
...
}
In our CustomPainter
create Tween
objects for each transform we want to apply. In this example, we transform object offset, scale and color. Therefore we create 3 Tweens.
class AnimatedCustomPainter extends CustomPainter {
final _paint = Paint();
final Animation<double> _size;
final Animation<double> _offset;
final Animation<Color> _color;
AnimatedCustomPainter(Animation<double> animation)
: _size = Tween<double>(begin: 50, end: 150).animate(animation),
_offset = Tween<double>(begin: 200, end: 0).animate(animation),
_color =
ColorTween(begin: Colors.red, end: Colors.blue).animate(animation),
super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
_paint.color = _color.value;
canvas.drawCircle(
Offset(
size.width / 2,
size.height / 2 + _offset.value,
),
_size.value,
_paint,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
Note that we call super(repaint: animation)
when constructing AnimatedCustomPainter
. The CustomPainter
would then repaint the canvas on each animation.value
change. If we don’t call super(repaint: animation)
, our painter won’t animate.
That’s it! The full source code can be found below:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
AnimationController _animationController;
_MyAppState() {
// Start the animation in 2 seconds after screen open.
Future.delayed(Duration(seconds: 2))
.then((_) => _animationController.forward());
}
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
);
}
@override
void dispose() {
// Properly dispose the controller. This is important!
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
color: Colors.white,
child: CustomPaint(
painter: AnimatedCustomPainter(
_animationController.view,
),
),
),
);
}
}
class AnimatedCustomPainter extends CustomPainter {
final _paint = Paint();
final Animation<double> _size;
final Animation<double> _offset;
final Animation<Color> _color;
AnimatedCustomPainter(Animation<double> animation)
: _size = Tween<double>(begin: 50, end: 150).animate(animation),
_offset = Tween<double>(begin: 200, end: 0).animate(animation),
_color =
ColorTween(begin: Colors.red, end: Colors.blue).animate(animation),
super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
_paint.color = _color.value;
canvas.drawCircle(
Offset(
size.width / 2,
size.height / 2 + _offset.value,
),
_size.value,
_paint,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}