官方文档ide
若是没有看过以前两节的,建议先看看前两节的内容post
addListener(...)
中调用 setState(...)
来给 widget 添加动画AnimatedWidget
的方法来给 widget 添加动画上一节中咱们也经过案例的形式展示出以上两种方式的区别,得出的结论就是以下: 使用 addListener(...)
和 setState(...)
给 Widget
添加动画的时候,会致使其余的 Widget
也跟着重绘,而使用 AnimatedWidget
的方式给 Widget
添加动画的时候只会重绘当前的 Widget
动画
咱们先来看看动画的执行流程。ui
咱们在使用动画的时候会初始化一个 AnimationController
this
AnimationController controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
复制代码
能够看出,初始化 AnimationController
须要传递两个参数,一个是 vsync
一个是 duration
,这个 duration
很容易理解,就是动画执行完毕须要的时长,那 vsync
是什么啊?上述代码中咱们传入了 this
,那是由于咱们在 State
类 with(混入,第一节 有讲) 了 SingleTickerProviderStateMixin
,下面咱们看看初始化 AnimationController
时都干了什么事,源码以下:spa
AnimationController({
double value,
this.duration,
this.debugLabel,
this.lowerBound: 0.0,
this.upperBound: 1.0,
@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);
}
复制代码
咱们抽重点出来看debug
_ticker = vsync.createTicker(_tick);
复制代码
这里调用了 vsync
的 createTicker(...)
方法建立了一个 Ticker
, 而咱们在初始化 AnimationController
的时候 vsync
参数传的是 SingleTickerProviderStateMixin
,SingleTickerProviderStateMixin#createTicker(...)
的源码以下:code
Ticker createTicker(TickerCallback onTick) {
...
_ticker = new Ticker(onTick, debugLabel: 'created by $this');
...
return _ticker;
}
复制代码
这个 Ticker
是个什么东西呢?看看它的注释吧!如下为 Ticker
类的部分注释对象
Calls its callback once per animation frame.继承
When created, a ticker is initially disabled. Call [start] to enable the ticker.
Creates a ticker that will call the provided callback once per frame while running.
start
方法来使 Ticker 变得可用(enable)建立 Ticker
到这儿就完了。 如何让Ticker
可用呢,也就是如何调用它的 start
方法呢?
咱们在启动动画的时候会调用 AnimationController#forward()
, 在 forward()
方法中就间接的会调用到 Ticker#start()
, Ticker#start()
最后会调用到 scheduleFrame()
,而这个方法里面调用了 ui.window.scheduleFrame();
,这个 scheduleFrame()
是一个 Native 方法,以下:
void scheduleFrame() native 'Window_scheduleFrame';
复制代码
上面的方法应该就是调度屏幕刷新的(若有错误,请指出,谢谢)。
到这里,Ticker
也就 start
了,而后 Ticker 就会在动画的每一帧去调一次本身的回调,这个回调 是在 AnimationController
中的构造方法中建立 Ticker
的时候传入的 _ticker = vsync.createTicker(_tick);
咱们来看看 _tick
部分源码:
void _tick(Duration elapsed) {
...
notifyListeners();
_checkStatusChanged();
}
复制代码
这里面调用了 notifyListeners()
和 _checkStatusChanged()
,咱们先来看看 notifyListeners()
,部分源码以下:
void notifyListeners() {
final List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
for (VoidCallback listener in localListeners) {
try {
if (_listeners.contains(listener))
// 执行回调
listener();
} catch (exception, stack) {
...
}
}
}
复制代码
能够看出,notifyListeners()
中有一个 VoidCallback
的集合,而后遍历这个集合,而且执行集合中全部的回调。
一般咱们会使用 .addListener((){...})
给动画添加监听,咱们在调用addListener((){...})
以后会执行以下源码
void addListener(VoidCallback listener) {
didRegisterListener();
_listeners.add(listener);
}
复制代码
将咱们传入的回调方法添加到_listeners
集合中,也就是上面 notifyListeners()
方法中遍历的集合。
这里咱们来小结一下动画的执行流程
notifyListeners()
方法,此方法中会去遍历_listeners
集合,并执行集合中的每个回调方法_listeners
集合中去ticker.srtart()
来启动 Ticker
,而后 Ticker
在动画的每一帧都会 执行回调,也就是咱们在第一步中传入的接下来就是咱们在 addListener((){...})
中干的事情了,一般会这样
_controller.addListener(() {
setState(() {
});
});
复制代码
也就是咱们会在传入的回调方法中去执行 setState((){})
方法,咱们能够看看文档:
setState(VoidCallback fn)
部分文档以下:
Notify the framework that the internal state of this object has changed.
Calling [setState] notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a [build] for this [State] object.
大概意思就是:通知 framework
当前对象的内部状态发生改变,这个操做会致使再次调用当前 State 对象的 build 方法,至关于重绘当前对象。
结合以前的分析,整个动画过程就是,当动画启动以后,会不停的去执行addListener((){...})
中的回调,而咱们在监听回调中又调用了setState(...)
方法,因此这就致使当前对象不停的重绘,也就出现了屏幕上的动画的效果。
回到以前的问题: 使用 addListener(...) 和 AnimatedWidget 为何会出现那种区别呢?
其实咱们来看下 AnimatedWidget
的源码立马就明白了
abstract class AnimatedWidget extends StatefulWidget {
const AnimatedWidget({
Key key,
@required this.listenable
}) : assert(listenable != null),
super(key: key);
final Listenable listenable;
@protected
Widget build(BuildContext context);
@override
_AnimatedState createState() => new _AnimatedState();
...
}
复制代码
咱们能够看到 AnimatedWidget
继承自 StatefulWidget
, 而 StateFulWidget
在初始化的时候会去建立一个 State
对象来管理自身的状态,也就是回去执行 createState()
方法。
其实到这里已经能够解释两种动画方式存在的区别,由于 AnimatedWidget
内部也有一个 State
对象来管理这自身的状态,而咱们以前经过查看文档也知道一个 State
对象只会维护当前对象的状态,因此即便重绘,也只会致使当前 State
对象的重绘,而不会致使其余 State
对象的重绘。
咱们来继续看看 AnimatedWidget
中的 createState()
干了什么事,它里面调用了_AnimatedState()
方法,部分源码以下:
class _AnimatedState extends State<AnimatedWidget> {
@override
void initState() {
super.initState();
widget.listenable.addListener(_handleChange);
}
...
@override
void dispose() {
widget.listenable.removeListener(_handleChange);
super.dispose();
}
void _handleChange() {
setState(() {
// The listenable's state is our build state, and it changed already.
});
}
@override
Widget build(BuildContext context) => widget.build(context);
复制代码
明白了吧!!!
_AnimatedState
在 initState
中给 widget
的 listenable
添加了监听,这里的listenable
就是咱们在初始化一个 AnimatedWidget
是传入的 Animation
对象。
而 addListener
中传入的回调是 _handleChange()
, 在 _handleChange()
中一样调用了 setState(...)
来触发当前对象重绘。
好了, 整个过程就是这样了。若是有什么错误,欢迎你们指出,谢谢!!!