View the directory –>
The nature of animation
Animation, that is, moving pictures.
The Flutter reaches 60FPS, i.e., 16ms per frame. Each frame is an image.
For an Image, width=300,height=300
Frame 1: width=300,height=300
Frame 2: width=295,height=295
Frame 3: width=290,height=290
.
.
Frame 58: width=10,height=10
Frame 59: width=5,height=5
Frame 60: width=0,height=0
In one second, from frame 1 to frame 60, what you see is the Image shrinking. That’s animation.
The width and height attribute of Image changes continuously within 1 second, which is scale;
The position attribute of Image, which changes continuously within 1 second, is the moving effect;
The font size property of Text, which changes continuously over a period of time, is the effect of font size scaling;
.
That is, one or more attributes of a widget that change continuously over a period of time is the animation effect of the corresponding attributes of the widget.
Key points:
- Acts on the properties of the widget
- For a period of time
- The value of a property that changes continuously
This is all about ideas. How exactly does Flutter work?
Several classes of Flutter animation:
- AnimationController, set in one time, will continue to the generation of value between 0 and 1, can control the execution of the animation, such as positive executed, reverse, repeat, etc., can also add state monitor, such as correction of each frame, the entire animation state correction, etc.
- The AnimationController defaults to 0 to 1. You can use Tween to change the value range, such as 0 to 100, red to green, etc
- Curve AnimationController continuously generates values in an interval. Curve controls how values change within this interval. For example, in the initial example above, the value changes linearly with a fixed decrease of 5 each time. We can also change the algorithm by decreasing 60, 55, 50. This is a slowing effect of change. Flutter has some built-in effects, which we will look at later.
Have you noticed that the above content has nothing to do with widgets, but it is all about how the hype is generated over a period of time?
How do you make a connection?
In the build:
Build () {return Widget (XXX attributes: _AnimationController. Value); }Copy the code
Each time the value generated by the AnimationController changes, the build method is eventually called to change the interface, resulting in a continuous effect called animation.
Sample code above:
import 'package:flutter/material.dart'; class Test01AnimationController extends StatefulWidget { @override _Test01AnimationControllerState createState() => _Test01AnimationControllerState(); } class _Test01AnimationControllerState extends State<Test01AnimationController> with SingleTickerProviderStateMixin { int _count = 0; double _width = 0; Double _height = 0; // Image high AnimationController _controller; @override void initState() { super.initState(); _count = 0; Duration = 2000ms _controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 2000)); _controller.addListener(() {setState(() {_count++; _width = 300 * _controller.value; _height = 300 * _controller.value; }); }); // animation starts _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimationController"), centerTitle: true, ), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "images/a.JPG", width: _width, height: _height, ), Text("${_count} ${_controller.value}") ], ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); }}Copy the code
Brief summary:
- AnimationnController, which continuously generates a series of values
- Each time a value is generated, setState is applied to the widget’s properties
- The build method is re-executed after each setState
- You end up with a changing widget, the animation effect
Important class
AnimationController
A constructor
AnimationController
AnimationController({ double? Value, this.duration, this.reverseDuration, this.debugLabel, this.lowerBound = 0.0, this.upperBound = 1.0, this.animationBehavior = AnimationBehavior.normal, required TickerProvider vsync, }) : assert(lowerBound ! = null), assert(upperBound ! = null), assert(upperBound >= lowerBound), assert(vsync ! = null), _direction = _AnimationDirection.forward { _ticker = vsync.createTicker(_tick); _internalSetValue(value ?? lowerBound); }Copy the code
Characteristics of this construction method:
- There are upper and lower value boundaries
- During the execution of the animation, values within the upper and lower boundaries are continuously generated and obtained by the controller.value test.
AnimationController.unbounded
AnimationController. Unbounded ({double value = 0.0, enclosing duration, enclosing reverseDuration, enclosing debugLabel, required TickerProvider vsync, this.animationBehavior = AnimationBehavior.preserve, }) : assert(value ! = null), assert(vsync ! = null), lowerBound = double.negativeInfinity, upperBound = double.infinity, _direction = _AnimationDirection.forward { _ticker = vsync.createTicker(_tick); _internalSetValue(value); }Copy the code
Characteristics of this construction method:
- LowerBound and upperBound are infinite
- The value obtained from controller.value is Infinity.
- So the significance of this constructor is that it defines a duration of time, but the animation within that duration needs to be defined. Usually used for physical simulations.
attribute
value
It has two functions:
- During animation execution, controller. Value gets the value generated by the animation at the current point in time.
- When creating the AnimationController, set the initial value, such as value=0.5. The default value of the AnimationController is 0-1, so the animation will start at 0.5 and end at 1. If you set duration to 2 seconds, the actual execution time is about 1 second. If you set value=1, the animation will not be executed. That is, the AnimationController will generate continuously changing values according to duration, and after setting value, will take the value-1 segment.
_controller = new AnimationController(vsync: this, duration: const duration (milliseconds: 2000), value: 0.9); ` ` `Copy the code
Another key point is that value only works when it is between lowerBound and upperBound. If value is less than lowerBound, the value starts from lowerBound. If value is greater than upperBound, the animation ends and is never executed again. Value The default value is lowerBound.
duration
Animation duration #####reverseDuration Duration of animation when revrse is executed. If not set, duration is calculated by duration.
lowerBound
The default value is 0.0. When set, lowerBound must be lower than upperBounnd or an error will be reported
upperBound
The default 1.0
Example with lowerBound and upperBound set:
import 'package:flutter/material.dart'; class Test02AnimationController extends StatefulWidget { @override _Test02AnimationControllerState createState() => _Test02AnimationControllerState(); } class _Test02AnimationControllerState extends State<Test02AnimationController> with SingleTickerProviderStateMixin { int _count = 0; double _width = 0; Double _height = 0; // Image high AnimationController _controller; @override void initState() { super.initState(); _count = 0; Duration = 2000ms _controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 2000), value: 0, lowerBound: 0, upperBound: 20, animationBehavior: AnimationBehavior.normal ); _controller.addListener(() {setState(() {_count++; _width = 30 * _controller.value; _height = 30 * _controller.value; }); }); // animation starts _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimationController"), centerTitle: true, ), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "images/a.JPG", width: _width, height: _height, ), Text("${_count} ${_controller.value}") ], ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); }}Copy the code
animationBehavior
Note: this is limited to personal understanding, make a record, the follow-up study of the specific situation and then correct. You can skip this property for now
Flutter support many platforms, but some animation is on a specific platform does not support or require to simplify, and AccessibilityFeatures. DisableAnimations field is returned by the platform support, when to true, equipment will require flutter as soon as possible to reduce or to disable the animation. There are two ways to control:
- Animationbehavior.normal, which is default when using new AnimationController(), simplifies the animation by reducing its duration so that it ends sooner.
- AnimationBehavior. Preserve, when use new AnimationController unbounded () by default, this value AnimationController will retain its behavior, I understand is inertia. For repetitive animation, when not to consider AccessibilityFeatures disableAnimations, the default is this kind of behavior.
vsync
Register every frame of the screen to listen for callbacks to prevent off-screen animations, such as locking the screen without callbacks, to avoid consuming unnecessary resources. When creating AnimationController than must be introduced to this value, and AnimationController is usually created in the State, make the State with a SingleTickerProviderStateMixin, preach vsync, Into this.
Curve
Let’s take a look at the effect:
Curve describes the process of speed change during animation. The default effect of AnimationController is uniform speed. Curve can change this effect, such as acceleration, deceleration, rebound, etc. What actually controls is the rule that the AnimationController generates values within duration.
How to use:
AnimationController _controller; CurvedAnimation _curvedAnimation; @override void initState() { super.initState(); _controller = new AnimationController( vsync: this, duration: const Duration(milliseconds: 3000)); _curvedAnimation = new CurvedAnimation(parent: _controller, curve: Curves.ease); // Animation starts _controller.repeat(); }Copy the code
- Still creating the AnimationController
- When creating a CurvedAnimation, pass in the AnimationController and Curve. By curses. XXX, you can obtain the effects built into the flutter.
- AnimationController is still used for animation control, such as repeat, forward, reverse, etc.
- Later, the value will be obtained from CurvedAnimation, such as _curvedanimation.value.
The code for the above effect:
import 'package:flutter/material.dart'; class Test05CurveImage extends StatefulWidget { @override _Test05CurveImageState createState() => _Test05CurveImageState(); } class _Test05CurveImageState extends State<Test05CurveImage> with SingleTickerProviderStateMixin { double _width = 0; Double _height = 0; // Image high AnimationController _controller; CurvedAnimation _curvedAnimation; @override void initState() { super.initState(); _controller = new AnimationController( vsync: this, duration: const Duration(milliseconds: 5000)); _curvedAnimation = new CurvedAnimation(parent: _controller, curve: Curves.bounceInOut); _controller.addListener(() { setState(() { _width = 300 * _curvedAnimation.value; _height = 300 * _curvedAnimation.value; }); }); // animation starts _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimationController"), centerTitle: true, ), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "images/a.JPG", width: _width, height: _height, ), Text("_curvedAnimation.value=${_curvedAnimation.value}") ], ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); }}Copy the code
The Curve sources:
- Built-in flutter
- The custom
The Curve effect that Flutter provides
Currently 41, see the results:
The code is as follows:
import 'package:flutter/material.dart';
import 'dart:math';
class Test04Curve extends StatefulWidget {
@override
_Test04CurveState createState() => _Test04CurveState();
}
class _Test04CurveState extends State<Test04Curve>
with SingleTickerProviderStateMixin {
List<ItemModel> curveList;
double size = 120;
@override
void initState() {
super.initState();
curveList = [
ItemModel('linear', Curves.linear),
ItemModel('bounceIn', Curves.bounceIn),
ItemModel('bounceOut', Curves.bounceOut),
ItemModel('bounceInOut', Curves.bounceInOut),
ItemModel('ease', Curves.ease),
ItemModel('easeIn', Curves.easeIn),
ItemModel('easeInBack', Curves.easeInBack),
ItemModel('easeInCirc', Curves.easeInCirc),
ItemModel('easeInCubic', Curves.easeInCubic),
ItemModel('easeInExpo', Curves.easeInExpo),
ItemModel('easeInQuad', Curves.easeInQuad),
ItemModel('easeInQuart', Curves.easeInQuart),
ItemModel('easeInQuint', Curves.easeInQuint),
ItemModel('easeInSine', Curves.easeInSine),
ItemModel('easeInToLinear', Curves.easeInToLinear),
ItemModel('easeOut', Curves.easeOut),
ItemModel('easeOutBack', Curves.easeOutBack),
ItemModel('easeOutCirc', Curves.easeOutCirc),
ItemModel('easeOutCubic', Curves.easeOutCubic),
ItemModel('easeOutExpo', Curves.easeOutExpo),
ItemModel('easeOutQuad', Curves.easeOutQuad),
ItemModel('easeOutQuart', Curves.easeOutQuart),
ItemModel('easeOutQuint', Curves.easeOutQuint),
ItemModel('easeOutSine', Curves.easeOutSine),
ItemModel('easeInOut', Curves.easeInOut),
ItemModel('easeInOutBack', Curves.easeInOutBack),
ItemModel('easeInOutCirc', Curves.easeInOutCirc),
ItemModel('easeInOutCubic', Curves.easeInOutCubic),
ItemModel('easeInOutExpo', Curves.easeInOutExpo),
ItemModel('easeInOutQuad', Curves.easeInOutQuad),
ItemModel('easeInOutQuart', Curves.easeInOutQuart),
ItemModel('easeInOutQuint', Curves.easeInOutQuint),
ItemModel('easeInOutSine', Curves.easeInOutSine),
ItemModel('decelerate', Curves.decelerate),
ItemModel('elasticIn', Curves.elasticIn),
ItemModel('elasticOut', Curves.elasticOut),
ItemModel('elasticInOut', Curves.elasticInOut),
ItemModel('slowMiddle', Curves.slowMiddle),
ItemModel('fastLinearToSlowEaseIn', Curves.fastLinearToSlowEaseIn),
ItemModel('fastOutSlowIn', Curves.fastOutSlowIn),
ItemModel('linearToEaseOut', Curves.linearToEaseOut),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Curve"),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.all(10),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisExtent: size + 20,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
maxCrossAxisExtent: size + 10),
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 5),
child: Column(
children: [
MyCurveWidget(curveList[index].curve, size),
Text(curveList[index].name, style: TextStyle(fontSize: 10)),
],
),
);
},
itemCount: curveList.length,
),
),
);
}
}
class MyCurveWidget extends StatefulWidget {
Curve _curve;
double _size;
MyCurveWidget(this._curve, this._size);
@override
_MyCurveWidgetState createState() => _MyCurveWidgetState();
}
class _MyCurveWidgetState extends State<MyCurveWidget>
with SingleTickerProviderStateMixin {
AnimationController _controller;
CurvedAnimation _curvedAnimation;
@override
void initState() {
super.initState();
// 创建一个动画,设定持续时间duration为2000ms
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 6000));
_curvedAnimation =
new CurvedAnimation(parent: _controller, curve: widget._curve);
// 动画开始执行
_controller.repeat();
}
@override
Widget build(BuildContext context) {
return Container(
height: widget._size,
width: widget._size,
child: Center(
child: CustomPaint(
size: Size(widget._size - 10, widget._size - 10),
painter: MyCurvePainter(_curvedAnimation),
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class MyCurvePainter extends CustomPainter {
Animation<double> _animation;
MyCurvePainter(this._animation) : super(repaint: _animation);
@override
void paint(Canvas canvas, Size size) {
translateToCenter(canvas, size);
drawBg(canvas, size);
drawBoll(canvas, size);
}
void drawBg(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.black12
..strokeWidth = 1
..style = PaintingStyle.stroke;
Offset p1 = Offset(-size.width / 2, 0);
Offset p2 = Offset(size.width / 2, 0);
canvas.drawLine(p1, p2, paint);
canvas.drawCircle(Offset.zero, size.width / 2, paint);
}
void drawBoll(Canvas canvas, Size size) {
double radius = size.width / 2;
Paint paint = Paint()
..color = Colors.orangeAccent
..style = PaintingStyle.fill;
canvas.drawCircle(
Offset(-radius + size.width * _animation.value, 0), 5, paint);
double sweepAngle = pi + pi * 2 * _animation.value;
canvas.drawCircle(
Offset(radius * cos(sweepAngle), radius * sin(sweepAngle)), 5, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
void translateToCenter(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
}
}
class ItemModel {
String name;
Curve curve;
ItemModel(this.name, this.curve);
}
Copy the code
The custom Curve
Inherit Curve and rewrite the transformInternal method to define the change rules. Such as:
class MyCurve extends Curve { @override double transformInternal(double t) { return sin(pi / 2 * t); }}Copy the code
- The range of t is between 0 and 1
- Sine PI over 2 times t is a sine curve using t
The effect is as follows;
The code is as follows:
import 'package:flutter/material.dart'; import 'dart:math'; class Test06CustomCurve extends StatefulWidget { @override _Test06CustomCurveState createState() => _Test06CustomCurveState(); } class _Test06CustomCurveState extends State<Test06CustomCurve> with SingleTickerProviderStateMixin { double _width = 0; Double _height = 0; // Image high AnimationController _controller; CurvedAnimation _curvedAnimation; @override void initState() { super.initState(); _controller = new AnimationController( vsync: this, duration: const Duration(milliseconds: 5000)); _curvedAnimation = new CurvedAnimation(parent: _controller, curve: MyCurve()); _controller.addListener(() { setState(() { _width = 300 * _curvedAnimation.value; _height = 300 * _curvedAnimation.value; }); }); // animation starts _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimationController"), centerTitle: true, ), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "images/a.JPG", width: _width, height: _height, ), Text("_curvedAnimation.value=${_curvedAnimation.value}") ], ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } } class MyCurve extends Curve { @override double transformInternal(double t) { return sin(pi / 2 * t); }}Copy the code
Tween
The AnimationController can set the value range lowerbound-upperbound, which is of type double. Tween extends the value range and type to be a color, int, double, etc.
There are two important approaches:
- evaluate
- animate
Using the evaluate
The code is as follows;
import 'package:flutter/material.dart'; class Test07Tween extends StatefulWidget { @override _Test07TweenState createState() => _Test07TweenState(); } class _Test07TweenState extends State<Test07Tween> with SingleTickerProviderStateMixin { double _width = 0; Double _height = 0; // Image high AnimationController _controller; Tween sizeTween = new Tween(begin: 0.0, end: 300.0); @override void initState() { super.initState(); _controller = new AnimationController( vsync: this, duration: const Duration(milliseconds: 5000),upperBound: 1); _controller.addListener(() { setState(() { _width = sizeTween.evaluate(_controller); _height = sizeTween.evaluate(_controller); }); }); // animation starts _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimationController"), centerTitle: true, ), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "images/a.JPG", width: _width, height: _height, ), Row( children: [ Text("sizeTween.evaluate(_controller)="), Text("${sizeTween.evaluate(_controller)}") ], ) ], ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); }}Copy the code
Key points:
- New Tween(begin: 0.0, end: 300.0), create a Tween object and set the value range
- Creating a Controller
- Sizetween. evaluate(_controller), evaluate method gets the interpolation of the current point in time
What if lowerBound, upperBound and Tween exist at the same time? UpperBound =1, now we’ll change it to 2 to see what happens:
The image is doubled in size compared to upperBound=1. So the inference is a product.
Use the animate
We can retrieve a new Animation using the animate method provided by Tween. We can also retrieve a value from this Animation as follows:
import 'package:flutter/material.dart'; class Test08TweenAnimate extends StatefulWidget { @override _Test08TweenAnimateState createState() => _Test08TweenAnimateState(); } class _Test08TweenAnimateState extends State<Test08TweenAnimate> with SingleTickerProviderStateMixin { double _width = 0; Double _height = 0; // Image high AnimationController _controller; Animation<dynamic> _animation; Tween sizeTween = new Tween(begin: 0.0, end: 300.0); @override void initState() { super.initState(); _controller = new AnimationController( vsync: this, duration: const Duration(milliseconds: 5000)); _animation = sizeTween.animate(_controller); _controller.addListener(() { setState(() { _width = _animation.value; _height = _animation.value; }); }); // animation starts _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AnimationController"), centerTitle: true, ), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "images/a.JPG", width: _width, height: _height, ), Row( children: [ Text("_animation.value="), Text("${_animation.value}") ], ) ], ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); }}Copy the code
Key points:
- Tween: Tween sizeTween = new Tween(begin: 0.0, end: 300.0)
- Associated with controller: _animation = sizetween.animate (_controller);
- Use: _animation.value to get the interpolation
- Note that _controller is still used to control animation start, repeat, etc
Interval composite animation
Interval is a subclass of Curve and defines a value within the range [0,1], which can be [0.1,0.3] or [0.4,0.8]. As we know, the AnimationController as a whole describes an animation process, which is viewed as [0,1]. Interval selects a region within [0,1], such as [0.3,0.5]. When Tween specifies the Interval during animate, Indicates that the Tween begins at 0.3 and ends at 0.5.
For example, a square has a size change from 0 to 0.6 and a color change from 0.3 to 1.0. Effect:
Code:
import 'package:flutter/material.dart'; class Test29Interval extends StatefulWidget { @override _State createState() => _State(); } class _State extends State<Test29Interval> with SingleTickerProviderStateMixin { AnimationController _controller; Tween _size = new Tween<double>(begin: 0.0, end: 200.0); Tween _colorTween = new ColorTween(begin: Colors.orangeAccent, end: Colors.green); Animation<double> _animationSize; Animation<Color> _animationColor; @override void initState() { super.initState(); _controller = new AnimationController( vsync: this, duration: const Duration(milliseconds: 6000), ); _animationSize = _sizetween. animate(new CurvedAnimation(parent: _controller, curve: new Interval(0.0, 0.6))); _animationColor = _colortween. animate(new CurvedAnimation(parent: _controller, curve: new Interval(0.3, 1.0))); _controller.addListener(() { setState(() {}); }); // animation starts _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Test29Interval"), centerTitle: true, ), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: _animationSize.value, height: _animationSize.value, margin: EdgeInsets.all(10), color: _animationColor.value, ), Container( width: 400, child: Text("_animationSize.value = ${_animationSize.value}",style: TextStyle(fontSize: 14),), ) ], ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); }}Copy the code
AnimatedWidget
You should note that all of the above animation changes are maintained by setState, which would be tedious to use every time for a general purpose function. Therefore, Flutter provides an AnimatedWidget that does not explicitly call setState when used.
Here’s an example:
Code:
import 'package:flutter/material.dart'; class Test30AnimatiedWidget extends StatefulWidget { @override _State createState() => _State(); } class _State extends State<Test30AnimatiedWidget> with SingleTickerProviderStateMixin { AnimationController controller; Animation<double> animation; @override void initState() { // TODO: implement initState super.initState(); controller = new AnimationController( duration: const Duration(milliseconds: 6000), vsync: this); Animation = new Tween(begin: 0.0, end: 300.0). Animate (controller); controller.addStatusListener((status) { if (status == AnimationStatus.completed) { controller.reverse(); } else if (status == AnimationStatus.dismissed) { controller.forward(); }}); controller.forward(); } @override Widget build(BuildContext context) { return ImageAnimatiedWidget( animation: animation, ); } @override void dispose() { // TODO: implement dispose super.dispose(); controller.dispose(); } } class ImageAnimatiedWidget extends AnimatedWidget { @override Widget build(BuildContext context) { Animation animation = listenable; return Center( child: Container( child: Image.asset( "images/a2.png", width: animation.value, height: animation.value, ), // width: animation.value, // height: animation.value, ), ); } ImageAnimatiedWidget({Key key, Animation<double> animation}) : super(key: key, listenable: animation); }Copy the code
AnimatedBuilder
Similar to animatedWidgets, setState is hidden, providing another way to organize widgets.
Same as the above example, effect:
Code:
import 'package:flutter/material.dart'; class Test31AnimateBuilder extends StatefulWidget { @override _State createState() => _State(); } class _State extends State<Test31AnimateBuilder> with SingleTickerProviderStateMixin { AnimationController controller; Animation<double> animation; @override void initState() { // TODO: implement initState super.initState(); controller = new AnimationController( duration: const Duration(milliseconds: 6000), vsync: this); Animation = new Tween(begin: 0.0, end: 300.0). Animate (controller); controller.addStatusListener((status) { if (status == AnimationStatus.completed) { controller.reverse(); } else if (status == AnimationStatus.dismissed) { controller.forward(); }}); controller.forward(); } @override Widget build(BuildContext context) { return ImageAnimateBuilder(animation, MyImage()); } @override void dispose() { // TODO: implement dispose super.dispose(); controller.dispose(); } } class ImageAnimateBuilder extends StatelessWidget { final Animation animation; final Widget child; ImageAnimateBuilder(this.animation, this.child); @override Widget build(BuildContext context) { return Center( child: AnimatedBuilder( animation: animation, child: child, builder: (BuildContext context, Widget child) { return Container( width: animation.value, height: animation.value, child: child, ); },),); } } class MyImage extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Container( child: Image.asset( "images/a2.png", ), // width: animation.value, // height: animation.value, ), ); }}Copy the code
AnimatedSwitcher
This is a special widget provided by Flutter. The effect of flutter is to animate the child of the AnimatedSwitcher when changes are made to the old value and to animate the new value. Go and come refer to the forward and reverse of the same animation.
Look at an example, the number changes, the current number gradually shrink hidden, the new number is the process of magnification.
Effect:
Code:
import 'package:flutter/material.dart'; class Test32AnimatedSwitcher extends StatefulWidget { const Test32AnimatedSwitcher({Key key}) : super(key: key); @override _State createState() => _State(); } class _State extends State<Test32AnimatedSwitcher> { int _count = 0; @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ AnimatedSwitcher( duration: const Duration(milliseconds: 1000), transitionBuilder: (Widget child, Animation<double> Animation) {return ScaleTransition(child: Child, scale: animation);}, child: Text('$_count', // display the specified key, different keys are treated as different Text, so that the animation key can be executed: ValueKey<int>(_count), style: TextStyle(fontSize: 30), ), ), RaisedButton( child: const Text('+1',), onPressed: () { setState(() { _count += 1; }); }, ), ], ), ); }}Copy the code