简洁概括:flutter 程序中包含两种动画 代码动画,绘图动画
代码动画是以小部件为中心,它们倾向于增强特定现有小部件的外观或过渡,而不是单独充当独立的小部件。
绘图的动画 是从无到有画出来的。它们通常是独立,如游戏角色,或者涉及纯粹用代码表达具有挑战性的转换。
而我们平常用的动画都是代码动画, 绘制动画更加复杂。
Flutter 代码的动画有两种风格 隐式动画和显式动画
隐式动画依赖于简单地为一些小部件属性设置一个新值,Flutter 负责将其从当前值设置为新值。这些小部件易于使用且功能强大(比如位置的改变、大小的改变等)一般内置都以Animated开头
显式动画需要一个 AnimationController。它们被称为“显式”,因为它们仅在明确要求时才开始制作动画。你可以使用显式动画来做所有你能用隐式动画做的事情,必须手动管理 AnimationController 的生命周期,因为它不是一个小部件,这意味着将它放在一个有状态的小部件中。一般内置都以Transition结尾
所以实现动画的时候要根据自己的需求去决定选择什么动画。
1 我的动画是否会永远重复?我所说的“永远”是指它在某个屏幕上,或者只要某个条件为真,例如音乐播放。
2 动画中的值是否不连续。
3 多个小部件是否以协调的方式一起制作动画
只要满足一个就选择显示动画
官方动画说明地址 Introduction to animations | Flutter
动画的效果实际看到的是快速连续显示的许多静止图像。这就是电影的运作方式。单独的图片在电影中被称为帧——因为数字屏幕的工作方式类似——它们在这里也被称为帧。电影通常每秒显示 24 帧。现代数字设备每秒显示 60 到 120 帧。
现在再来回顾代码。
实现动画一般都要实现一个方法
实现这个方法的目的是控制器需要
class ...... with SingleTickerProviderStateMixin{
同时要创建一个控制器
late final AnimationController _controller = AnimationController( duration: const Duration(milliseconds: 150), //需要一个TickerProvider对象 vsync: this, );
...省略
}
看下SingleTickerProviderStateMixin 里面有什么
可以看到实现的必须是state 子类 类型必须是StatefulWidget子类
@optionalTypeArgs mixin SingleTickerProviderStateMixinon State implements TickerProvider { Ticker? _ticker; //创建一个_ticker @override Ticker createTicker(TickerCallback onTick) { ...省略 _ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by ${describeIdentity(this)}' : null); _updateTickerModeNotifier(); _updateTicker(); // Sets _ticker.mute correctly. return _ticker!; } @override void activate() { ...省略 } void _updateTicker() { ...省略 } void _updateTickerModeNotifier() { ...省略 } ...省略 }
看看 Ticker
描述 每个动画帧调用一次它的回调。
这里可以明白 动画的每一帧都调用了他的回调
Ticker(this._onTick, { this.debugLabel }) { assert(() { _debugCreationStack = StackTrace.current; return true; }()); }
比如
var ticker = Ticker((elapsed) => print('hello'));
ticker.start();
每一帧都会打印, 如果不ticker.dispose()。他会一只运行下去
而 SingleTickerProviderStateMixin 就是为了解决管理的麻烦 Ticker就不用在我们自己管理
同时也可以让flutter框架获取当前sate更多的信息
如果不实SingleTickerProviderStateMixin 现那你AnimationController 要自己去实现一个管理
再来看AnimationController
AnimationController({ //动画的值 double? value, //动画持续时间 this.duration, //反转时间 this.reverseDuration, //调试时候打印自己的标签 this.debugLabel, //动画起始值 也可以自定义比如 100 this.lowerBound = 0.0, //动画结束值 也可以自定义比如 1000 this.upperBound = 1.0, //动画模式 this.animationBehavior = AnimationBehavior.normal, required TickerProvider vsync, }) : ...省略 _direction = _AnimationDirection.forward { //当前state 创建一个对象 _ticker = vsync.createTicker(_tick); _internalSetValue(value ?? lowerBound); }
官方事例
class _MyWidgetState extends Statewith SingleTickerProviderStateMixin { AnimationController _controller; int i = 0; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); _controller.addListener(_update); _controller.forward(); } void _update() { setState(() { i = (_controller.value * 299792458).round(); }); } @override Widget build(BuildContext context) { return Text('$i m/s'); } }
这就是一个简单的动画,
隐式动画 内置的Animated开头
解隐动画就是没有控制器,而且不能重复
如 //主要更改自身的属性值的时候setstate 会触发动画 AnimatedContainer({ ...省略 //执行曲线 Curve curve = Curves.linear, //持续时间 required Duration duration, //完成回调 VoidCallback? onEnd, })
显式动画 内置以Transition 结尾
比如 小部件随意拖动 放开后自动靠边
Stack( children: [ //内置的动画 PositionedTransition( //Tween 补间,表示一个区间值 rect: RelativeRectTween( //初始位置 begin: const RelativeRect.fromLTRB(x, y,.,.), //结束位置 end: const RelativeRect.fromLTRB(endX,endY,.,.), ).animate( CurvedAnimation( parent: _controller, curve: Curves.elasticOut, ), ), child: null, ), ], );
//拖动的时候不断更新动画,让他满足自己动画的标准 onPanUpdate: (details) { setState(() { x += details.delta.dx; y += details.delta.dy; endX = 0; \\或者靠右坐标 endY=y; }); },
//拖动结束后开始动画 onPanEnd: (details) { _controller.forward(); },
所以上述效果主要就是不断的刷新。动画的初始位置(这里的初始位置就是ui上显示的位置)和动画的结束位置(就是靠边的最终位置)