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, okayAnimatedBuilder
Do 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!