本篇源码来源 flutter_stable_1.22.5
0. 问题背景
- 针对页面 A (
with SingleTickerProviderStateMixin或with TickerProviderStateMixin) 跳转页面 B,再返回页面 A 过程中的生命周期变化。
1. Demo 代码如下:
/// Page A:
class TickerPage1 extends StatefulWidget {
@override
_TickerPage1State createState() => _TickerPage1State();
}
class _TickerPage1State extends State<TickerPage1> with SingleTickerProviderStateMixin{
// 测试点-0
// class _TickerPage1State extends State<TickerPage1> with TickerProviderStateMixin{
AnimationController _animationController;
@override
void initState() {
super.initState();
print('$runtimeType ----- initState');
// 测试点-1
// _animationController = AnimationController(vsync: this);
// 测试点-2
// createTicker((_){});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('$runtimeType ----- didChangeDependencies');
// 测试点-3
// print(TickerMode.of(context));
}
@override
void didUpdateWidget(covariant TickerPage1 oldWidget) {
super.didUpdateWidget(oldWidget);
print('$runtimeType ----- didUpdateWidget');
}
@override
void dispose() {
print('$runtimeType ----- dispose');
_animationController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
print('$runtimeType ----- build');
return Scaffold(
appBar: AppBar(),
body: Center(
child: RaisedButton(
child: Text('TickerPage1'),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => _TickerPage2())),
),
),
);
}
}
/// Page B
class _TickerPage2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print('$runtimeType ----- build');
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text('TickerPage2'),
),
);
}
}
Page A 标注了四个测试点,可以试着放开任一测试点,看看打印结果。
1.省时间版本
- 正常没有任何 TickerProviderStateMixin(无论是否 Single),生命周期回调顺序如下:
Page A ----- initState
Page A ----- didChangeDependencies
Page A ----- build
Page A --- push ---> Page B
Page B ----- build
Page B --- pop ---> Page A
// nothing
- Page A with SingleTickerProviderStateMixin 后变化(无 AnimtionController 之类依赖 TickerProvider):
同上结论1
- Page A with TickerProviderStateMixin 变化:
Page A ----- initState
Page A ----- didChangeDependencies
Page A ----- build
Page A --- push ---> Page B
Page B ----- build
Page B --- pop ---> Page A
Page A ----- didChangeDependencies
Page A ----- build
- Page A with SingleTickerProviderStateMixin 后变化(有 AnimtionController 之类依赖 TickerProvider ):
同上结论3
-
本质不在于是否
with SingleTickerProviderStateMixin或with TickerProviderStateMixin(测试点0),是否使用了AnimationController或创建了 Ticker (测试点1、2),而是取决于是否绑定了TickerMode(测试点3)。 -
结论:
SingleTickerProviderStateMixin(被 AnimtionController 绑定后) 或TickerProviderStateMixin会在didChangeDependencies中通过TickMode.of(context)绑定_EffectiveTickerMode(InheritedWidget 类型)从而获得 Ticker 是否处于 enable 状态。因此,页面 A 跳转页面 B 再回来的场景中,会因为 TickerMode 状态的变化,而重新构建(didChangeDependencies ---> build)。
2. 源码版本
1、ticker_provider.dart 文件看下相关类的目录结构
2、从 AnimationController 看起
// 测试点-1
AnimationController _animationController = AnimationController(vsync: this);
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);
}
第 18 行,vsync.createTicker(_tick) 在构造函数里创建了一个 Ticker。
abstract class TickerProvider {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const TickerProvider();
/// Creates a ticker with the given callback.
///
/// The kind of ticker provided depends on the kind of ticker provider.
@factory
Ticker createTicker(TickerCallback onTick);
}
TickerProvider 是一个抽象类,SingleTickerProviderStateMixin 和 TickerProviderStateMixin分别是具体的实现子类。
3、先看 SingleTickerProviderStateMixin 类
mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
Ticker _ticker;
@override
Ticker createTicker(TickerCallback onTick) {
_ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
return _ticker;
}
// ...已剔除无关代码
@override
void didChangeDependencies() {
if (_ticker != null)
_ticker.muted = !TickerMode.of(context);
super.didChangeDependencies();
}
第 5 行,创建了一个 Ticker 对象 。这里对应的就是 AnimationController 初始化过程中的 _ticker = vsync.createTicker(_tick);
第 13 行开始,didChangeDependencies方法里,当 _ticker 不为空,会调用 TickerMode.of(context)方法。两个点:1. 不为空,对应的就是有没调用 createTicker方法或者说是有没有初始化 AnimationController(vsync: this)。 2.TickerMode.of(context)后面一起讲,它是我们这次的核心关键^^。
4、再对比看下 TickerProviderStateMixin 类
mixin TickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
Set<Ticker> _tickers;
@override
Ticker createTicker(TickerCallback onTick) {
_tickers ??= <_WidgetTicker>{};
final _WidgetTicker result = _WidgetTicker(onTick, this, debugLabel: 'created by $this');
_tickers.add(result);
return result;
}
// ...已剔除无关代码
@override
void didChangeDependencies() {
final bool muted = !TickerMode.of(context);
if (_tickers != null) {
for (final Ticker ticker in _tickers) {
ticker.muted = muted;
}
}
super.didChangeDependencies();
}
}
class _WidgetTicker extends Ticker {
_WidgetTicker(TickerCallback onTick, this._creator, { String debugLabel }) : super(onTick, debugLabel: debugLabel);
final TickerProviderStateMixin _creator;
@override
void dispose() {
_creator._removeTicker(this);
super.dispose();
}
}
第 5 行,同样是创建 Ticker 对象,不过这里创建的是 _WidgetTicker类型的 Ticker 对象并返回,同时塞进了局部变量 _tickers 集合里。这里对应的就是每一个 AnimationController 初始化过程的 _ticker = vsync.createTicker(_tick);,拿到的是 _WidgetTicker (Ticker 子类型),并且被 TickerProviderStateMixin 管理在集合_tickers里了。
第 15 行,这里可以看到 TickerProviderStateMixin 是直接调用了 TickerMode.of(context)。
5、来看核心关键点 TickerMode.of(context)
class TickerMode extends StatelessWidget {
const TickerMode({
Key key,
@required this.enabled,
this.child,
}) : assert(enabled != null),
super(key: key);
// ...已剔除无关代码
static bool of(BuildContext context) {
final _EffectiveTickerMode widget = context.dependOnInheritedWidgetOfExactType<_EffectiveTickerMode>();
return widget?.enabled ?? true;
}
@override
Widget build(BuildContext context) {
return _EffectiveTickerMode(
enabled: enabled && TickerMode.of(context),
child: child,
);
}
}
class _EffectiveTickerMode extends InheritedWidget {
const _EffectiveTickerMode({
Key key,
@required this.enabled,
Widget child,
}) : assert(enabled != null),
super(key: key, child: child);
final bool enabled;
// ...已剔除无关代码
}
第 11 行, TickerMode.of(context) 通过 context.dependOnInheritedWidgetOfExactType 查找控件树中类型是 _EffectiveTickerMode 的控件并建立依赖关系(如何建立依赖本篇不讨论)。而后者是一个 InheritedWidget 类型的控件,含有一个 enable 的局部变量,标示当前 TickerMode 的状态。
真相大白!
页面 PageA with SingleTickerProviderStateMixin 或 with TickerProviderStateMixin会在 didChangeDependencies 生命周期里调用 TickerMode.of(context)获取 TickerMode 状态,从而建立起依赖关系(实际是对 _EffectiveTickerMode 的包装 Widget)。
6、最后一点 TickerMode 为何可以在页面跳转过程中回调不同状态的?
答案还是要从 Overlay 这个大舞台里找到。
class OverlayState extends State<Overlay> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
// This list is filled backwards and then reversed below before
// it is added to the tree.
final List<Widget> children = <Widget>[];
bool onstage = true;
int onstageCount = 0;
for (int i = _entries.length - 1; i >= 0; i -= 1) {
final OverlayEntry entry = _entries[i];
if (onstage) {
onstageCount += 1;
children.add(_OverlayEntryWidget(
key: entry._key,
entry: entry,
));
if (entry.opaque)
onstage = false;
} else if (entry.maintainState) {
children.add(_OverlayEntryWidget(
key: entry._key,
entry: entry,
tickerEnabled: false,
));
}
}
return _Theatre(
skipCount: children.length - onstageCount,
children: children.reversed.toList(growable: false),
);
}
// ...已剔除无关代码
}
第 13 行与 20 行,Overlay 最终通过 build 返回的剧场里,将舞台上将可见 & 不可见但需要保持状态的“一幕幕” OverlayEntry 包裹进 _OverlayEntryWidget 再排序。
class _OverlayEntryWidget extends StatefulWidget {
const _OverlayEntryWidget({
@required Key key,
@required this.entry,
this.tickerEnabled = true,
}) : assert(key != null),
assert(entry != null),
assert(tickerEnabled != null),
super(key: key);
final OverlayEntry entry;
final bool tickerEnabled;
@override
_OverlayEntryWidgetState createState() => _OverlayEntryWidgetState();
}
class _OverlayEntryWidgetState extends State<_OverlayEntryWidget> {
@override
Widget build(BuildContext context) {
return TickerMode(
enabled: widget.tickerEnabled,
child: widget.entry.builder(context),
);
}
void _markNeedsBuild() {
setState(() { /* the state that changed is in the builder */ });
}
}
第 5 行,入参 this.tickerEnabled 默认 true。
第 21 行,入参 OverlayEntry 最终被包裹进入 TickerMode ,并根据其是否可见(onstage)附带了 Ticker 的状态。
3. 结语
欢迎大家不吝指正,我也会尽量削减此系列的篇幅,保证小而美!
完结撒花,感谢大家观看!