Flutter开发进阶之瞧瞧State
书接上回
上回说到StatefulWidget
会将自身作为参数创建StatefulElement
,然后StatefulElement的创建过程首先要调用_state = widget.createState()
,会创建并持有一个state通过对其生命周期的管理去对Widget
进行刷新,创建state后还会将自身交给state持有state._element = this;
。
接下来会通过解析State
的构成去理解State
。
首先瞧瞧State
里有些什么?
abstract class State<T extends StatefulWidget> with Diagnosticable {
T get widget => _widget!;
T? _widget;
_StateLifecycle _debugLifecycleState = _StateLifecycle.created;
bool _debugTypesAreRight(Widget widget) => widget is T;
BuildContext get context {
assert(() {
if (_element == null) {
throw FlutterError(
'This widget has been unmounted, so the State no longer has a context (and should be considered defunct). \n'
'Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.',
);
}
return true;
}());
return _element!;
}
StatefulElement? _element;
bool get mounted => _element != null;
void initState() {
assert(_debugLifecycleState == _StateLifecycle.created);
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterWidgetsLibrary,
className: '$State',
object: this,
);
}
}
void didUpdateWidget(covariant T oldWidget) { }
void reassemble() { }
void setState(VoidCallback fn) {
assert(() {
if (_debugLifecycleState == _StateLifecycle.defunct) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called after dispose(): $this'),
ErrorDescription(
'This error happens if you call setState() on a State object for a widget that '
'no longer appears in the widget tree (e.g., whose parent widget no longer '
'includes the widget in its build). This error can occur when code calls '
'setState() from a timer or an animation callback.',
),
ErrorHint(
'The preferred solution is '
'to cancel the timer or stop listening to the animation in the dispose() '
'callback. Another solution is to check the "mounted" property of this '
'object before calling setState() to ensure the object is still in the '
'tree.',
),
ErrorHint(
'This error might indicate a memory leak if setState() is being called '
'because another object is retaining a reference to this State object '
'after it has been removed from the tree. To avoid memory leaks, '
'consider breaking the reference to this object during dispose().',
),
]);
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called in constructor: $this'),
ErrorHint(
'This happens when you call setState() on a State object for a widget that '
"hasn't been inserted into the widget tree yet. It is not necessary to call "
'setState() in the constructor, since the state is already assumed to be dirty '
'when it is initially created.',
),
]);
}
return true;
}());
final Object? result = fn() as dynamic;
assert(() {
if (result is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() callback argument returned a Future.'),
ErrorDescription(
'The setState() method on $this was called with a closure or method that '
'returned a Future. Maybe it is marked as "async".',
),
ErrorHint(
'Instead of performing asynchronous work inside a call to setState(), first '
'execute the work (without updating the widget state), and then synchronously '
'update the state inside a call to setState().',
),
]);
}
return true;
}());
_element!.markNeedsBuild();
}
void deactivate() { }
void activate() { }
void dispose() {
assert(_debugLifecycleState == _StateLifecycle.ready);
assert(() {
_debugLifecycleState = _StateLifecycle.defunct;
return true;
}());
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
}
Widget build(BuildContext context);
void didChangeDependencies() { }
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
assert(() {
properties.add(EnumProperty<_StateLifecycle>('lifecycle state', _debugLifecycleState, defaultValue: _StateLifecycle.ready));
return true;
}());
properties.add(ObjectFlagProperty<T>('_widget', _widget, ifNull: 'no widget'));
properties.add(ObjectFlagProperty<StatefulElement>('_element', _element, ifNull: 'not mounted'));
}
}
@optionalTypeArgs
为可选类型参数,允许在使用泛型时省略类型参数,通常情况下不需要关心具体类型,一般在这些方法中都可以通过返回值自动判断。
@protected
为元注解,表示一个成员(字段、方法、构造函数)受到保护,只能在定义它们的类和子类中访问。
@mustCallSuper
用于标记类中的方法,指示子类在重写@override
该方法时必须调用超类的实现,若重写时未调用Dart的Analyzer会发出警告。
通过上文可知StatefulElement
的创建会调用_state = widget.createState()
会创建一个管理StatefulWidget
的实例。
enum _StateLifecycle {
created,
initialized,
ready,
defunct,
}
State
具有对应的生命周期,不同的方法调用后会变更不同的状态,对应的状态下又会调用不同的方法。
State``State createState()
后会变为created并调用State.initState
,然后initialized等待State.didChangeDependencies
,接着ready等待build
或State.dispose
,最后为defunct,此时State就不能重建了。
_StateLifecycle _debugLifecycleState = _StateLifecycle.created;
可以看到State
默认为created,印证了上面的猜想。
BuildContext get context {
assert(() {
if (_element == null) {
throw FlutterError(
'This widget has been unmounted, so the State no longer has a context (and should be considered defunct). \n'
'Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.',
);
}
return true;
}());
return _element!;
}
StatefulElement? _element;
context
类型为BuildContext
与StatelessWidget
类似。
bool get mounted => _element != null;
_element
的创建有一个准备的过程,当State已经添加进Tree中时,也就获取到了_element
,所以_element
是在State创建后再将值赋给state._element
。
void setState(VoidCallback fn) {
assert(() {
if (_debugLifecycleState == _StateLifecycle.defunct) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called after dispose(): $this'),
ErrorDescription(
'This error happens if you call setState() on a State object for a widget that '
'no longer appears in the widget tree (e.g., whose parent widget no longer '
'includes the widget in its build). This error can occur when code calls '
'setState() from a timer or an animation callback.',
),
ErrorHint(
'The preferred solution is '
'to cancel the timer or stop listening to the animation in the dispose() '
'callback. Another solution is to check the "mounted" property of this '
'object before calling setState() to ensure the object is still in the '
'tree.',
),
ErrorHint(
'This error might indicate a memory leak if setState() is being called '
'because another object is retaining a reference to this State object '
'after it has been removed from the tree. To avoid memory leaks, '
'consider breaking the reference to this object during dispose().',
),
]);
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called in constructor: $this'),
ErrorHint(
'This happens when you call setState() on a State object for a widget that '
"hasn't been inserted into the widget tree yet. It is not necessary to call "
'setState() in the constructor, since the state is already assumed to be dirty '
'when it is initially created.',
),
]);
}
return true;
}());
final Object? result = fn() as dynamic;
assert(() {
if (result is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() callback argument returned a Future.'),
ErrorDescription(
'The setState() method on $this was called with a closure or method that '
'returned a Future. Maybe it is marked as "async".',
),
ErrorHint(
'Instead of performing asynchronous work inside a call to setState(), first '
'execute the work (without updating the widget state), and then synchronously '
'update the state inside a call to setState().',
),
]);
}
return true;
}());
_element!.markNeedsBuild();
}
页面的刷新通过调用setState()
,通过条件会调用markNeedsBuild()
,然后通过scheduleBuildFor(Element element)
将element添加进_dirtyElements
并重新构建。
需要注意的是重构的条件要求生命周期不能为defunct、不能为created是还未获取到_element
和回调不能为Future。
build
可以出现的情况有initState
之后、didUpdateWidget
之后、setState
之后、InheritedWidget
的依赖发生变化之后和在deactivate
之后重新移动到Tree中其他位置时。
本文到这里还未完成闭环,还缺一个重要拼图,待续。