上一篇分析了StatelessWidget的源码,说道StatelessWidget肯定会联想到StatefulWidget。StatefulWidget是有状态的,那么这个状态是什么?为什么它可以保留当前状态进入下一帧的UI展示?带着这两个问题让我们进入StatefulWidget的源码世界看看。
关于StatefulWidget的StatefulElement和StatelessWidget的StatelessElement都是继承ComponentElement这个类。在讲述StatefulWidget的源码的时候因为有很多方法在“Flutter源码分析(一)先从StatelessWidget开始”已经详细讲解了,所以写这篇文章就没必要在重复一遍。强烈建议在阅读当前文章之前先阅读上一篇文章。
StatefulWidget
StatefulWidget是有状态的Widget的抽象类,所有继承它的都是有状态的Widget。那么有状态是什么意思呢?意思是可以将当前的内部状态保存下来。简单可以理解为StatefulWidget 可以将当前的状态从上一帧保存到下一帧。比如通过按钮修改某个Widget的颜色,进行刷新到下一帧显示出来,这个就需要用StatefulWidget来进行实现。我们来看一下StatefulWidget的源码:
abstract class StatefulWidget extends Widget {
/// Initializes [key] for subclasses.
const StatefulWidget({ Key key }) : super(key: key);
/// Creates a [StatefulElement] to manage this widget's location in the tree.
///
/// It is uncommon for subclasses to override this method.
@override
StatefulElement createElement() => StatefulElement(this);
/// Creates the mutable state for this widget at a given location in the tree.
///
/// Subclasses should override this method to return a newly created
/// instance of their associated [State] subclass:
///
/// ```dart
/// @override
/// _MyState createState() => _MyState();
/// ```
///
/// The framework can call this method multiple times over the lifetime of
/// a [StatefulWidget]. For example, if the widget is inserted into the tree
/// in multiple locations, the framework will create a separate [State] object
/// for each location. Similarly, if the widget is removed from the tree and
/// later inserted into the tree again, the framework will call [createState]
/// again to create a fresh [State] object, simplifying the lifecycle of
/// [State] objects.
@protected
@factory
State createState();
}
可以发现StatelessWidget需要实现build的方法,而StatefulWidget需要我们现实State这个类。通过它的汉语意思“状态”,我们能猜到State这个类是用来保存状态的实现。
同时可以看到StatelessWidget创建了StatelessElement,而StatefulWidget创建了StatefulElement的,分析StatelessWidget的源码的文章里说过,Widget就是Element的配置项,所以重点还需要看Element这个类。分析StatelessWidget的时候重点分析了StatelessElement这个类,那么分析StatefulWidget同样需要重点看一下StatefulElement这个类了。
State
先来看一下Staste的源码如下:
abstract class State<T extends StatefulWidget> with Diagnosticable {
...
/// an argument.
T get widget => _widget;
T _widget;
_StateLifecycle _debugLifecycleState = _StateLifecycle.created;
BuildContext get context => _element;
StatefulElement _element;
bool get mounted => _element != null;
@protected
@mustCallSuper
void initState() {
assert(_debugLifecycleState == _StateLifecycle.created);
}
@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }
@mustCallSuper
void reassemble() { }
@protected
void setState(VoidCallback fn) {
assert(fn != null);
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 dynamic 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().'
),
]);
}
// We ignore other types of return values so that you can do things like:
// setState(() => x = 3);
return true;
}());
_element.markNeedsBuild();
}
@protected
@mustCallSuper
void deactivate() { }
@protected
@mustCallSuper
void dispose() {
assert(_debugLifecycleState == _StateLifecycle.ready);
assert(() {
_debugLifecycleState = _StateLifecycle.defunct;
return true;
}());
}
@protected
Widget build(BuildContext context);
@protected
@mustCallSuper
void didChangeDependencies() { }
...
}
为了让同学可以看得更清楚一些,我将State的方法通过截图来显示出来了。下边我们来思考一个问题,State这个类是用来管理状态的,那么它提供的 initState, build, didChangeDependencies, didUpdateWidget, dispose, reassemble这些方法是用来干什么的呢?既然是管理状态当然需要提供一些生命周期的方法啊,这些方法其实就是和StatefulElement的生命周期挂钩的。我们通过下图看明了的看一下State(Element)的生命周期方法的调用:
总结State(Element)的生命周期
-
在Widget组件初始化初始化创建的时候会依次执行initState, didChangeDependencies,build方法。在组件移除之后会执行dispose方法。
-
在调用setState方法,会依次执行didUpdateWidget,build方法。
-
reassemble这个方法只有在点击热重载按钮的时候进行调用(仅用来测试时候使用)。
StastefulElement
接下来看一下StatefulElement的方法有哪些:
在上一篇StatelessWidget源码分析看到过StatelessElement这个类总共才实现了build, update两个方法。但在StatefulElement这个类中实现了不止有build,update还有activate,dectivate, mount, unmount等等跟Element的生命周期挂钩的方法。这是为什么呢?我们应该也能够猜到和State进行绑定的。那么先深入一下StatefulElement的源码。
首先会执行widget的createState方法创建State对象,并将State对象保存在当前的StatefulElement中。
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
assert(() {
if (!_state._debugTypesAreRight(widget)) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>'),
ErrorDescription(
'The createState function for ${widget.runtimeType} returned a state '
'of type ${_state.runtimeType}, which is not a subtype of '
'State<${widget.runtimeType}>, violating the contract for createState.'
),
]);
}
return true;
}());
assert(_state._element == null);
_state._element = this;
assert(
_state._widget == null,
'The createState function for $widget returned an old or invalid state '
'instance: ${_state._widget}, which is not null, violating the contract '
'for createState.',
);
_state._widget = widget;
assert(_state._debugLifecycleState == _StateLifecycle.created);
在执行build的方法的时候,它会执行子类的state.build(this);这个方法,由此我们可以知道再用StatefulWidget写布局的时候需要现在State的build方法里边。
@override Widget build() => _state.build(this);
又重写了_firstBuild, performRebuild等等(不清楚这两个方法的作用可以查看上篇文章,这里就不在重复讲解了)。就是为了将State与Element的生命周期进行绑定。
@override
void _firstBuild() {
assert(_state._debugLifecycleState == _StateLifecycle.created);
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
final dynamic debugCheckForReturnedFuture = _state.initState() as dynamic;
assert(() {
if (debugCheckForReturnedFuture is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('${_state.runtimeType}.initState() returned a Future.'),
ErrorDescription('State.initState() must be a void method without an `async` keyword.'),
ErrorHint(
'Rather than awaiting on asynchronous work directly inside of initState, '
'call a separate method to do this work without awaiting it.'
),
]);
}
return true;
}());
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
assert(() {
_state._debugLifecycleState = _StateLifecycle.initialized;
return true;
}());
_state.didChangeDependencies();
assert(() {
_state._debugLifecycleState = _StateLifecycle.ready;
return true;
}());
super._firstBuild();
}
final dynamic debugCheckForReturnedFuture = _state.initState() as dynamic;
@override
void performRebuild() {
if (_didChangeDependencies) {
_state.didChangeDependencies();
_didChangeDependencies = false;
}
super.performRebuild();
}
_state.didChangeDependencies();
@override
void _firstBuild() {
assert(_state._debugLifecycleState == _StateLifecycle.created);
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
final dynamic debugCheckForReturnedFuture = _state.initState() as dynamic;
assert(() {
if (debugCheckForReturnedFuture is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('${_state.runtimeType}.initState() returned a Future.'),
ErrorDescription('State.initState() must be a void method without an `async` keyword.'),
ErrorHint(
'Rather than awaiting on asynchronous work directly inside of initState, '
'call a separate method to do this work without awaiting it.'
),
]);
}
return true;
}());
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
assert(() {
_state._debugLifecycleState = _StateLifecycle.initialized;
return true;
}());
_state.didChangeDependencies();
assert(() {
_state._debugLifecycleState = _StateLifecycle.ready;
return true;
}());
super._firstBuild();
}
_state.didChangeDependencies();
@override
void reassemble() {
state.reassemble();
super.reassemble();
}
state.reassemble();
这里就不在过多展示其他的生命周期的源码了。总结一下StatefulElement,于StatelessElement相比,多了State的状态和重写了Element的生命周期进行绑定。那么它是如何实现跨帧保持状态呢?有没有注意到其实上边已经提及到了,因为State对象保存在当前的Element里边,而Element在仅仅Widget配置项修改的情况下并不会重新创建。所以当前上一次的状态已经会保存为当前状态。(关于这块逻辑具体可以看updateChild方法)。还有一个问题就是当我们修改完widget的配置项如何刷新UI呢?setState方法出场了。
setState
setState方式是用来由当前Widget和子Widget进行刷新的展示下一帧的UI。那让我们先看一下它的源码:
@protected
void setState(VoidCallback fn) {
assert(fn != null);
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 dynamic 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().'
),
]);
}
// We ignore other types of return values so that you can do things like:
// setState(() => x = 3);
return true;
}());
_element.markNeedsBuild();
}
可以看到该方法内部仅仅调用了两个方法。
1、先执行传入的方法。2、将element标记为“脏”,调用framework刷新机制等待刷新。
_element.markNeedsBuild();
void markNeedsBuild() {
assert(_debugLifecycleState != _ElementLifecycle.defunct);
if (!_active)
return;
...
if (dirty)
return;
_dirty = true;
owner.scheduleBuildFor(this);
}
至于标记为“脏”,在调用一帧刷新之后会执行到performRebuild这个方法。关于performRebuild方法的详细介绍已经在“Flutter源码分析先从StatelessWidget开始”讲过了,这里就不过多说了。最终它会调用build, updateChild方法重新刷新页面。
总结
到这里我们已经将StatelessWidget和StatefulWidget的源码分析已经到一段落尾声了。阅读源码的好处在于我们可以跟深入的了解当前的框架,了解它的实现机制。通过它的实现机制可以写出性能更好的代码。