Lightweight Flutter Animations
Overview
First we need to create the abstract class:
abstract class AnimationWidget<T extends StatefulWidget> extends State<T> with SingleTickerProviderStateMixin { Duration elapsed = Duration.zero; Duration delta = Duration.zero; late final Ticker ticker; BoxConstraints constraints = const BoxConstraints.tightFor();
@override void initState() { super.initState(); ticker = createTicker((elapsed) { delta = elapsed - this.elapsed; this.elapsed = elapsed; update(elapsed); if (mounted) setState(() {}); }); ticker.start(); WidgetsBinding.instance.addPostFrameCallback(start); }
@override void dispose() { ticker.dispose(); super.dispose(); }
@override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, dimens) { constraints = dimens; return paint(context, dimens); }); }
void start(Duration time) {}
void update(Duration time);
Widget paint(BuildContext context, BoxConstraints constraints); }
This will let us replace State with AnimationWidget and not need to call setState to rebuild the ui.
Example
For the example we need an inline canvas painter:
class InlinePainter extends CustomPainter { InlinePainter({ required this.draw, super.repaint, });
final void Function(Canvas canvas, Size size) draw;
@override void paint(Canvas canvas, Size size) { draw(canvas, size); }
@override bool shouldRepaint(CustomPainter oldDelegate) => true; }
And the example using the new widget class:
import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart';
class SimpleExample extends StatefulWidget { const SimpleExample({Key? key}) : super(key: key);
@override State<SimpleExample> createState() => _SimpleExampleState(); }
class _SimpleExampleState extends AnimationWidget<SimpleExample> { var x = 0.0; var y = 0.0; var z = 0.0;
@override void update(Duration time) { final t = delta.inMilliseconds / 1000; x += t; y += t; z += t; }
@override Widget paint(BuildContext context, BoxConstraints constraints) { return Material( child: Center( child: Container( width: 100, height: 100, transform: Matrix4.identity() ..rotateX(x) ..rotateY(y) ..rotateZ(z), child: const Text( 'Hello World', style: TextStyle( fontSize: 30, fontWeight: FontWeight.bold, ), ), ), ), ); } }
Demo