The use of animation in Flutter

After learning how to build static pages, you need to add a little animation to improve the user experience.

Animation static picture according to the pre-defined rules, in a certain period of time constantly fine-tuning, resulting in changes in the effect. The realization of animation from static to dynamic mainly depends on the visual residual effect of human eyes. So, for an animation system to animate, it needs to do three things:

  1. Determine the law of screen change;
  2. Set the animation cycle according to this rule, and then start the animation
  3. Redraw the surface by constantly fetching the current value of the animation.

All three things are done in Flutter via an Animation, an AnimationController, and a Listener.

Animation: Continuously outputs the current value of the Animation according to predetermined rules during the Animation cycle.

AnimationController: Controls the Animation, setting the Animation duration, starting and stopping the Animation.

Listener: For animation Listener, according to the callback, to get the current value of the animation, rendering.

class _AnimationDemo1State extends State with SingleTickerProviderStateMixin { AnimationController controller; Animation<double> animation; @override void initState() { super.initState(); Controller = AnimationController(vsync: this, duration: duration (milliseconds: 1000)); Animation = Tween(begin: 50.0, end: 250.0).animate(controller).. AddListener (() {// Update state setState(() {}); }); // When launching the animation, use repeat(reverse: true) to repeat the animation back and forth. controller.repeat(reverse: true); // Listen for animation state. At the end of the animation, the reverse is executed; Restart execution when the animation completes its reverse execution. Animation. AddStatusListener ((status) {if (status = = AnimationStatus.com pleted) {/ / at the end of the animation, it reverses the controller. The reverse (); } else if (status = = AnimationStatus dismissed) {/ / in the animation when reverse has been completed, restart the execution controller. The forward (); }}); // Start animating Controller.forward (); } @override Widget build(BuildContext context) {return Scaffold(appBar: appBar (title: Text(" base animation "),), body: Center( child: Container( width: animation.value, height: animation.value, color: Colors.yellow, ), ), ); } @override void dispose() {// Stop animation controller.stop(); super.dispose(); }}Copy the code

As you can see, the animation only provides the animation data, so we also need to listen to the animation execution progress and use setState in the callback to force the refresh screen to see the animation effect. These steps are fixed. Is there an easier way?

AnimatedWidget and AnimatedBuilder

With these two widgets, we can simplify our animation steps. AnimatedWidget: Put the monitor on the animation into the AnimatedWidget, where you can retrieve the value of the animation for page redrawing.

Class AnimatedWidgetDemo extends AnimatedWidget {AnimatedWidgetDemo({Key Key, Key Key, Key Key); Animation<double> animation}) : super(key: key, listenable: animation); @override Widget build(BuildContext context) { final Animation<double> animation = listenable; return Container( width: animation.value, height: animation.value, color: Colors.red, ); } } class AnimationDemo2 extends StatefulWidget { @override State<StatefulWidget> createState() { return _AnimationDemo2State(); } } class _AnimationDemo2State extends State with SingleTickerProviderStateMixin { AnimationController controller; Animation<double> animation; @override void initState() { super.initState(); Controller = AnimationController(vsync: this, duration: duration (milliseconds: 1000)); Animation = Tween(begin: 50.0, end: 250.0). Animate (controller); // When launching the animation, use repeat(reverse: true) to repeat the animation back and forth. controller.repeat(reverse: true); // Listen for animation state. At the end of the animation, the reverse is executed; Restart execution when the animation completes its reverse execution. Animation. AddStatusListener ((status) {if (status = = AnimationStatus.com pleted) {/ / at the end of the animation, it reverses the controller. The reverse (); } else if (status = = AnimationStatus dismissed) {/ / in the animation when reverse has been completed, restart the execution controller. The forward (); }}); // Start animating Controller.forward (); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimatedWidgetDemo"), ), body: Center( child: AnimatedWidgetDemo( animation: animation, ), ), ); } @override void dispose() { controller.stop(); super.dispose(); }}Copy the code

AnimatedBuilder: Separate animation and rendering logic.

class _AnimationDemo3State extends State with SingleTickerProviderStateMixin { AnimationController controller; Animation<double> animation; @override void initState() { super.initState(); Controller = AnimationController(vsync: this, duration: duration (milliseconds: 1000)); Animation = Tween(begin: 50.0, end: 250.0). Animate (controller); // When launching the animation, use repeat(reverse: true) to repeat the animation back and forth. controller.repeat(reverse: true); // Listen for animation state. At the end of the animation, the reverse is executed; Restart execution when the animation completes its reverse execution. Animation. AddStatusListener ((status) {if (status = = AnimationStatus.com pleted) {/ / at the end of the animation, it reverses the controller. The reverse (); } else if (status = = AnimationStatus dismissed) {/ / in the animation when reverse has been completed, restart the execution controller. The forward (); }}); // Start animating Controller.forward (); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimatedWidgetDemo"), ), body: AnimatedBuilder( animation: animation, builder: (context, child) => Center( child: Container( width: animation.value, height: animation.value, color: Colors.deepPurple, ), ), )); } @override void dispose() { controller.stop(); super.dispose(); }}Copy the code

You can see that by passing in the Animation through the AnimatedBuilder, you can get the value of the animation in the widget inside, and then set the corresponding properties to update the UI.

By comparing the above methods, only changes have been made to the animation listener, from adding the listener itself to inheriting the AnimationWidget passed in to the animation object, which can be used in the animation object. After passing in the Animation object directly to the AnimationBuild, the literal child widget gets the properties of the animation and updates the UI. The basic idea is the same.

Hero animation

Realize the animation switch effect from small picture to large picture page step by step, and when the user closes the large picture, also realize the original way back to the animation. There’s a name for this kind of control animation that’s Shared across pages: Shared Element Transition.

Flutter has a similar concept, the Hero control. Hero allows smooth page switching between shared elements on two pages.

To implement the shared element transformation, we need to wrap the two components in Hero and set the same tag “Hero” for both.

class AnimationDemo4 extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("hero "),), body: GestureDetector(// gesture monitor click child: hero (tag: 'hero'), // set the shared tag child: Center( child: Container(width: 80, height: 80, child: FlutterLogo()), )), onTap: () { Navigator.of(context) .push(MaterialPageRoute(builder: (_) => Page2())); // click to open the second page},); } } class Page2 extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(" second page "),), body: Hero(tag: 'Hero ', // Set the shared tag child: Center(child: Container(width: 320, height: 320, child: FlutterLogo()), ))); }}Copy the code

conclusion

Animation in Flutter is done by Animation, AnimationController, and Listener. Animation sets the change rule of Animation, AnimationController sets the Animation duration, repetition times, start and stop Animation to complete the management of Animation. Finally, Listener is used to complete the monitoring of Animation, change widget properties and refresh UI. Flutter provides simple and convenient animationWidgets and AnimationBuild widgets to help us separate animation logic from UI update logic and better achieve complex animation effects. Finally, Flutter provides a shared element animation Hero.