The official documentation

AnimatedWidget

Remember how you updated the status of widgets in the previous section? What we did last time was: first create the animation, then add a listener to the animation addListener(…). In the addListener (…). We did one important thing in this method: setState((){}), because this is the only thing that causes the widget to be redrawn.

This time we use the AnimatedWidget to animate without giving the animation addListener(…) And setState((){}), the AnimatedWidget itself draws itself using the value of the current Animation. Of course, we pass the Animation in the way of constructing parameters.

Look at the code:

class AnimatedContainer extends AnimatedWidget {

  AnimatedContainer({Key key, Animation<double> animation})
      : super (key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Center(
      child: Container(
        decoration: BoxDecoration(
            color: Colors.redAccent
        ),
        margin: EdgeInsets.symmetric(vertical: 10.0), height: animation.value, width: animation.value, ), ); }}Copy the code

In the code above, we define an AnimatedContainer that inherits the AnimatedWidget, and then we define a constructor. Notice that in the constructor we define an Animation and pass that Animation to the super class, We can look at the listEnable: animation parameter, which is a listenable type, as follows:

/// The [Listenable] to which this widget is listening.
///
/// Commonly an [Animation] or a [ChangeNotifier].
final Listenable listenable;
Copy the code

Then look at the Animation class:

abstract class Animation<T> extends Listenable implements ValueListenable<T> {... }Copy the code

Animation is a subclass of Listenable, so in our custom AnimatedContainer class we can pass an Animation parameter as Listenable in the parent class.

It is also easy to use the AnimatedContainer we defined above as a widget.

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedWidgetDemo',
      theme: ThemeData(
          primaryColor: Colors.redAccent
      ),
      home: Scaffold(
          appBar: AppBar(
            title: Text('AnimatedWidgetDemo'), ), body: AnimatedContainer(animation: animation,) ), ); ` `}Copy the code

You can see that we passed in an Animation object when we instantiated the AnimatedContainer.

AnimatedWidgetDemo.dart

The effect is as follows:

AnimatedBuilder

Let’s see how it works first, okayAnimatedBuilderDo the same as above

Part of the code is as follows:

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilderExample',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('animatedBuilderExample'),
        ),
        body: Center(
          child: AnimatedBuilder(
            animation: _animation,
            child: Container(
              decoration: BoxDecoration(color: Colors.redAccent),
            ),
            builder: (context, child) {
              returnContainer( width: _animation.value, height: _animation.value, child: child, ); },),),),); }Copy the code

Because AnimatedBuilder inherits from AnimatedWidget,

class AnimatedBuilder extends AnimatedWidget {... }Copy the code

So you can use AnimatedBuilder directly as a widget

The key parts of the above code are as follows:

body: Center(
  child: AnimatedBuilder(
    animation: _animation,
    child: Container(
      decoration: BoxDecoration(color: Colors.redAccent),
    ),
    builder: (context, child) {
      returnContainer( width: _animation.value, height: _animation.value, child: child, ); },),),Copy the code

The effect is as follows:

AnimatedBuilderExample_2.dart

The builder anonymous class is called every time the animation value changes

AnimatedBuilder uses the simplified structure as follows:

AnimatedBuilder(
    animateion: ... ,
    child: ... ,
    builder: (context, child) {
        return Container(
            width: ... ,
            height: ... ,
            child: child
        )
    }
)
Copy the code

The above code may look confusing, as the inside child appears to be specified twice, one outside and one inside. In fact, the outer child is passed to the AnimatedBuilder, which in turn passes the child as a parameter to the inner anonymous Builder.

We can verify the above statement by assigning a key to the outer child, and then printing the key of the child parameter inside the Builder.

body: Center(
  child: AnimatedBuilder(
    animation: _animation,
    child: Container(
      decoration: BoxDecoration(color: Colors.redAccent),
    key: Key("android"),
    ),
    builder: (context, child) {
      print("child.key: ${child.key}");
      returnContainer( width: _animation.value, height: _animation.value, child: child, ); },),),Copy the code

We add a key to the outer child and print the key to the child in the Builder

The output is as follows:

flutter: child.key: [<'android'>]
Copy the code

The difference between

Let’s look at AnimatedBuilder AnimatedWidget and add addListener{} listener and fire setState(…) inside. What’s the difference between these three ways of doing animation?

To see the difference, use a third-party control called RandomContainer, which changes its color each time the screen is repainted.

First add a dependency random_PK: any to pubspec.yaml as follows:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^ 0.1.2
  # RandomContainer
  random_pk: any
Copy the code

Let’s start with a small example of RandomContainer that changes color each time the screen is repainted.

Draw a RandomContainer with a width and height of 200.0 on the screen and add a click event to it. What you do inside the click event is call setState(…). Let the widget be redrawn with the following code:

body: Center(
  child: GestureDetector(
    onTap: _changeColor,
    child: RandomContainer(
      width: 200.0,
      height: 200.0,),),),Copy the code

To use RandomContainer, import ‘package:random_pk/random_pk.dart’;

Click event code is as follows:

void _changeColor() {
    setState(() {});
  }
Copy the code

AnimatedBuilderExample_1.dart

The effect is as follows:

Now let’s use three different ways to achieve the same effect to see what the difference is:

AnimatedWidget

_controller =
        AnimationController(vsync: this, duration: Duration(seconds: 5))
          ..repeat();
Copy the code
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilder',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnimatedBuilder'),
        ),
        body: Center(
          child: RandomContainer(
            width: 200.0,
            height: 200.0,
            child: AnimatedWidgetDemo( // new
              animation: _controller,
            ),
          ),
        ),
      ),
    );
  }
Copy the code

The effect is as follows:

AnimatedBuilder

_controller =
        AnimationController(vsync: this, duration: Duration(seconds: 5))
          ..repeat();
Copy the code
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilder',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnimatedBuilder'),
        ),
        body: Center(
          child: RandomContainer(
            width: 200.0,
            height: 200.0,
            child: AnimatedBuilderDemo( // new
              child: getContainer(),
              animation: _controller,
            ),
          ),
        ),
      ),
    );
  }
Copy the code

The AnimatedBuilder has the same effect as the AnimatedWidget.

Now let’s look at theaddListener{}It callssetState(...)That is, we are inIn the previous sectionThe way to achieve animation

_controller =
        AnimationController(vsync: this, duration: Duration(seconds: 5)).. repeat() .. addListener(() { setState(() {}); });Copy the code
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilder',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnimatedBuilder'),
        ),
        body: Center(
          child: RandomContainer(
            width: 200.0,
            height: 200.0,
            child: Transform.rotate( // new
              child: getContainer(),
              angle: _controller.value * 2.0 * pi,
            ),
          ),
        ),
      ),
    );
  }
Copy the code

Results the following

You see…

AnimatedBuilderExample_3.dart

RandomContainer reference

If there are any mistakes, please also point out, thank you!