注:本文代码基于Flutter SDK 3.13.5

一、前言

在上一文解读Flutter源码之runApp中,笔者对于StatefulWidgetStatefulElement源码的分析并未足够深入,而且State相关的分析也并未涉及。

因此,本文将会深入分析StatefulWidget的源码&State的生命周期。

二、分析StatefulWidget源码

看一个类的源码,最先入手方向应该是它的注释,因为官方document也是通过代码注释生成的,所以先看下StatefulWidget的注释。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/// 一个具有可变状态的widget
/// A widget that has mutable state.
///
/// 状态是以下信息:(1) 在构建widget时可以同步读取;(2) 在widget的生命周期中可能会发生变化。
/// widget实现者有责任确保在状态更改时使用 [State.setState] 及时通知 [State]。
/// State is information that (1) can be read synchronously when the widget is
/// built and (2) might change during the lifetime of the widget. It is the
/// responsibility of the widget implementer to ensure that the [State] is
/// promptly notified when such state changes, using [State.setState].
///
/// 一个stateful widget是“通过构建更具体地描述用户界面的其它widgets的一个星座”来描述用户界面的一部分的一个widget。
/// 构建过程递归地继续,直到用户界面的描述完全具体(例如,完全由 [RenderObjectWidget]s 组成,它描述具体的 [RenderObject]s)。
/// A stateful widget is a widget that describes part of the user interface by
/// building a constellation of other widgets that describe the user interface
/// more concretely. The building process continues recursively until the
/// description of the user interface is fully concrete (e.g., consists
/// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s).
///
/// 当您描述的用户界面部分可以动态更改时,Stateful widgets非常有用,例如由于具有内部时钟驱动状态,或取决于某些系统状态。
/// 对于仅依赖于对象本身的配置信息和widget在其中填充的 [BuildContext] 的组合,请考虑使用 [StatelessWidget]。
/// Stateful widgets are useful when the part of the user interface you are
/// describing can change dynamically, e.g. due to having an internal
/// clock-driven state, or depending on some system state. For compositions that
/// depend only on the configuration information in the object itself and the
/// [BuildContext] in which the widget is inflated, consider using
/// [StatelessWidget].
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=AqCMFXEmf3w}
///
/// [StatefulWidget] 实例本身是不可变的,并将其可变状态存储在由 [createState] 方法创建的单独 [State] 对象中,
/// 或者存储在 [State] 订阅的对象中,例如 [Stream] 或 [ChangeNotifier] 对象,其引用存储在 [StatefulWidget] 本身的final字段中。
/// [StatefulWidget] instances themselves are immutable and store their mutable
/// state either in separate [State] objects that are created by the
/// [createState] method, or in objects to which that [State] subscribes, for
/// example [Stream] or [ChangeNotifier] objects, to which references are stored
/// in final fields on the [StatefulWidget] itself.
///
/// 每当框架填充 [StatefulWidget] 时,框架都会调用 [createState],这意味着如果该widget已插入树中的多个位置,则多个 [State] 对象可能与同一个 [StatefulWidget] 关联。
/// 类似地,如果 [StatefulWidget] 从树中删除,然后再次插入到树中,框架将再次调用 [createState] 来创建一个新的 [State] 对象,从而简化 [State] 对象的生命周期。
/// The framework calls [createState] whenever it inflates a
/// [StatefulWidget], which means that multiple [State] objects might be
/// associated with the same [StatefulWidget] if that widget has been inserted
/// into the tree in multiple places. Similarly, if a [StatefulWidget] is
/// removed from the tree and later inserted in to the tree again, the framework
/// will call [createState] again to create a fresh [State] object, simplifying
/// the lifecycle of [State] objects.
///
/// 如果 [StatefulWidget] 的创建者使用 [GlobalKey] 作为其 [key],那么当它从树中的一个位置移动到另一个位置时,[StatefulWidget] 会保留相同的 [State] 对象。
/// 由于具有 [GlobalKey] 的widget最多可在树中的一个位置使用,因此使用 [GlobalKey] 的widget最多具有一个关联element。
/// 当将具有全局键的widget从树中的一个位置移动到另一个位置时,框架利用此属性,将与该widget相关联的(唯一)子树从旧位置移植到新位置(而不是在新位置重新创建子树)。
/// 与 [StatefulWidget] 关联的 [State] 对象与子树的其余部分一起嫁接,这意味着 [State] 对象在新位置重用(而不是重新创建)。
/// 但是,为了符合嫁接的条件,必须将widget插入到从旧位置删除的同一动画帧中的新位置。
/// A [StatefulWidget] keeps the same [State] object when moving from one
/// location in the tree to another if its creator used a [GlobalKey] for its
/// [key]. Because a widget with a [GlobalKey] can be used in at most one
/// location in the tree, a widget that uses a [GlobalKey] has at most one
/// associated element. The framework takes advantage of this property when
/// moving a widget with a global key from one location in the tree to another
/// by grafting the (unique) subtree associated with that widget from the old
/// location to the new location (instead of recreating the subtree at the new
/// location). The [State] objects associated with [StatefulWidget] are grafted
/// along with the rest of the subtree, which means the [State] object is reused
/// (instead of being recreated) in the new location. However, in order to be
/// eligible for grafting, the widget must be inserted into the new location in
/// the same animation frame in which it was removed from the old location.
///
/// 性能考虑
/// ## Performance considerations
/// 
/// [StatefulWidget]s 有两个主要类别。
/// There are two primary categories of [StatefulWidget]s.
///
/// 第一类是在[State.initState]中分配资源并在[State.dispose]中处理资源,但不依赖于[InheritedWidget]s或调用[State.setState]。
/// 此类widgets通常在应用程序或页面的根部使用,并通过 [ChangeNotifier]、[Stream] 或其它此类对象与子widget进行通信。
/// 遵循这种模式的Stateful widgets相对便宜(就 CPU 和 GPU 周期而言),因为它们构建一次就不再更新。因此,他们可以有一些复杂和深入的构建方法。
/// The first is one which allocates resources in [State.initState] and disposes
/// of them in [State.dispose], but which does not depend on [InheritedWidget]s
/// or call [State.setState]. Such widgets are commonly used at the root of an
/// application or page, and communicate with subwidgets via [ChangeNotifier]s,
/// [Stream]s, or other such objects. Stateful widgets following such a pattern
/// are relatively cheap (in terms of CPU and GPU cycles), because they are
/// built once then never update. They can, therefore, have somewhat complicated
/// and deep build methods.
///
/// 第二类是使用 [State.setState] 或依赖于 [InheritedWidget]s 的widget。
/// 这些通常会在应用程序的生命周期内重建多次,因此最小化重建此类widget的影响非常重要。
/// (它们也可以使用 [State.initState] 或 [State.didChangeDependency] 并分配资源,但重要的部分是它们重建。)
/// The second category is widgets that use [State.setState] or depend on
/// [InheritedWidget]s. These will typically rebuild many times during the
/// application's lifetime, and it is therefore important to minimize the impact
/// of rebuilding such a widget. (They may also use [State.initState] or
/// [State.didChangeDependencies] and allocate resources, but the important part
/// is that they rebuild.)
///
/// 有几种技术可以用来最小化重建stateful widget的影响:
/// There are several techniques one can use to minimize the impact of
/// rebuilding a stateful widget:
///
///  * 将状态推送到叶子。
///    例如,如果您的页面有一个滴答作响的时钟,则不要将状态放在页面顶部并在每次时钟滴答时重建整个页面,而是创建一个仅更新自身的专用时钟widget。
///  * Push the state to the leaves. For example, if your page has a ticking
///    clock, rather than putting the state at the top of the page and
///    rebuilding the entire page each time the clock ticks, create a dedicated
///    clock widget that only updates itself.
///
///  * 最小化由build方法及其创建的任何widgets传递创建的节点数量。
///    理想情况下,stateful widget只会创建一个小部件,并且该widget将是一个 [RenderObjectWidget]。 (显然这并不总是实用,但是widget越接近这个理想,它的效率就越高。)
///  * Minimize the number of nodes transitively created by the build method and
///    any widgets it creates. Ideally, a stateful widget would only create a
///    single widget, and that widget would be a [RenderObjectWidget].
///    (Obviously this isn't always practical, but the closer a widget gets to
///    this ideal, the more efficient it will be.)
///
///  * 如果子树没有更改,则缓存代表该子树的widget,并在每次可以使用时重新使用它。
///    为此,请将widget分配给“final”状态变量并在构建方法中重新使用它。
///    重用widget比创建新的(但配置相同的)widget要高效得多。
///    另一种缓存策略是将widget的可变部分提取到接受子参数的 [StatefulWidget] 中。
///  * If a subtree does not change, cache the widget that represents that
///    subtree and re-use it each time it can be used. To do this, assign
///    a widget to a `final` state variable and re-use it in the build method. It
///    is massively more efficient for a widget to be re-used than for a new (but
///    identically-configured) widget to be created. Another caching strategy
///    consists in extracting the mutable part of the widget into a [StatefulWidget]
///    which accepts a child parameter.
///
///  * 尽可能使用“const” widget。 (这相当于缓存一个widget并重新使用它。)
///  * Use `const` widgets where possible. (This is equivalent to caching a
///    widget and re-using it.)
///  
///  * 避免更改任何创建的子树的深度或更改子树中任何widget的类型。
///    例如,不要返回子控件或包装在[IgnorePointer]中的子控件,而是始终将子控件包装在[IgnorePointer]中并控制[IgnorePointer.ignoring]属性。
///    这是因为更改子树的深度需要重新构建、布局和绘制整个子树,而仅更改属性将需要对渲染树进行最小可能的更改(例如,在[IgnorePointer]的情况下,根本不需要布局或重新绘制)。   
///  * Avoid changing the depth of any created subtrees or changing the type of
///    any widgets in the subtree. For example, rather than returning either the
///    child or the child wrapped in an [IgnorePointer], always wrap the child
///    widget in an [IgnorePointer] and control the [IgnorePointer.ignoring]
///    property. This is because changing the depth of the subtree requires
///    rebuilding, laying out, and painting the entire subtree, whereas just
///    changing the property will require the least possible change to the
///    render tree (in the case of [IgnorePointer], for example, no layout or
///    repaint is necessary at all).
///
///  * 如果由于某种原因必须更改深度,请考虑将子树的公共部分包装在具有 [GlobalKey] 的小部件中,该 [GlobalKey] 在stateful widget的生命周期中保持一致。
///   (如果没有其它widget可以方便地分配key,则 [KeyedSubtree] widget可能对此很有用。)
///  * If the depth must be changed for some reason, consider wrapping the
///    common parts of the subtrees in widgets that have a [GlobalKey] that
///    remains consistent for the life of the stateful widget. (The
///    [KeyedSubtree] widget may be useful for this purpose if no other widget
///    can conveniently be assigned the key.)
///
/// {@macro flutter.flutter.widgets.framework.prefer_const_over_helper}
///
/// This video gives more explanations on why `const` constructors are important
/// and why a [Widget] is better than a helper method.
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo}
///
/// For more details on the mechanics of rebuilding a widget, see
/// the discussion at [Element.rebuild].
///
/// {@tool snippet}
///
/// This is a skeleton of a stateful widget subclass called `YellowBird`.
///
/// In this example, the [State] has no actual state. State is normally
/// represented as private member fields. Also, normally widgets have more
/// constructor arguments, each of which corresponds to a `final` property.
///
/// ```dart
/// class YellowBird extends StatefulWidget {
///   const YellowBird({ super.key });
///
///   @override
///   State<YellowBird> createState() => _YellowBirdState();
/// }
///
/// class _YellowBirdState extends State<YellowBird> {
///   @override
///   Widget build(BuildContext context) {
///     return Container(color: const Color(0xFFFFE306));
///   }
/// }
/// ```
/// {@end-tool}
/// {@tool snippet}
///
/// This example shows the more generic widget `Bird` which can be given a
/// color and a child, and which has some internal state with a method that
/// can be called to mutate it:
///
/// ```dart
/// class Bird extends StatefulWidget {
///   const Bird({
///     super.key,
///     this.color = const Color(0xFFFFE306),
///     this.child,
///   });
///
///   final Color color;
///   final Widget? child;
///
///   @override
///   State<Bird> createState() => _BirdState();
/// }
///
/// class _BirdState extends State<Bird> {
///   double _size = 1.0;
///
///   void grow() {
///     setState(() { _size += 0.1; });
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return Container(
///       color: widget.color,
///       transform: Matrix4.diagonal3Values(_size, _size, 1.0),
///       child: widget.child,
///     );
///   }
/// }
/// ```
/// {@end-tool}
///
/// 按照惯例,widget构造函数仅使用命名参数。同样按照约定,第一个参数是 [key],最后一个参数是 `child`、`children` 或等效参数。
/// By convention, widget constructors only use named arguments. Also by
/// convention, the first argument is [key], and the last argument is `child`,
/// `children`, or the equivalent.

可以看到,StatefulWidget的注释还是比较多的,这也恰恰反映了它的一个重要性,毕竟平时用它比较多。在StatefulWidget注释当中,它想尽可能地为API使用者讲明白StatefulWidget是什么,因此这不仅有常规的介绍,而且也有出于性能优化的一个考虑(目前来说,性能优化这块并不是本文的重点),以及最后还贴心地给出了使用示例,下面总结下StatefulWidget的特性:

1、StatefulWidget实例本身是不可变的,它是将其可变状态存储在由createState方法创建的单独 State对象中。

2、将StatefulWidget插入到树中的多个位置时,会创建多个单独的State实例;如果 StatefulWidget从树中删除,然后再次插入到树中,框架将再次调用createState来创建一个新的 State对象。

3、StatefulWidget的实现者在状态更改时可以使用State.setState及时通知State

4、如果StatefulWidget的创建者使用GlobalKey作为其key,那么当它从树中的一个位置移动到另一个位置时,StatefulWidget会保留相同的State对象。

5、…

这里给出了StatefulWidget的部分特性,主要是想让大家对StatefulWidget有一个初级认知,至于更多的特性需要从Flutter框架源码中去挖掘,这个任务留给个人自行去探索了。

OK,我们继续看StatefulWidget的源码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
abstract class StatefulWidget extends Widget {

  const StatefulWidget({ super.key });

  /// 创建一个StatefulElement来管理此widget在树中的位置
  /// 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);

  /// 在树中的给定位置为此widget创建可变状态
  /// Creates the mutable state for this widget at a given location in the tree.
  ///
  /// 子类应该重写此方法以返回其关联State子类的新创建的实例:
  /// Subclasses should override this method to return a newly created
  /// instance of their associated [State] subclass:
  ///
  /// ```dart
  /// @override
  /// State<SomeWidget> createState() => _SomeWidgetState();
  /// ```
  /// 
  /// 框架可以在StatefulWidget的生命周期内多次调用此方法。例如,如果widget被插入树中的多个位置,框架将为每个位置创建一个单独的State对象。
  /// 类似地,如果widget从树中删除,然后再次插入到树中,框架将再次调用createState来创建一个新的State对象,从而简化State对象的生命周期
  /// 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();
}

StatefulWidget的源码可以知道,它继承自抽象类Widget,重写了createElement方法,返回一个StatefulElement实例,并且在Widget的基础上新增了一个createState抽象方法,用于返回一个State实例。

这两个方法的功能从注释中也可以看的明明白白,关于StatefulWidget的分析先讲到这里。

三、分析StatefulElement源码

先看下StatefulElement的注释。

1
2
3
/// 使用 [StatefulWidget] 作为其配置的 [Element]。
/// An [Element] that uses a [StatefulWidget] as its configuration.
class StatefulElement extends ComponentElement

可以看到,StatefulElement的注释非常简要,从注释中并不能看出该Element的特性所在,不过可以知道的是StatefulElement继承自ComponentElement,那么来看下ComponentElement的注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/// 组成其它[Element]s的一个[Element].
/// An [Element] that composes other [Element]s.
/// 
/// ComponentElement不是直接创建RenderObject ,而是通过创建其它Element来间接创建RenderObject 
/// Rather than creating a [RenderObject] directly, a [ComponentElement] creates
/// [RenderObject]s indirectly by creating other [Element]s.
///
/// 与RenderObjectElement对比
/// Contrast with [RenderObjectElement].
abstract class ComponentElement extends Element

可以看到,ComponentElement属于组合型Element,它不是直接创建RenderObject,而是通过创建其它Element来间接创建RenderObject,并且ComponentElement继承自Element,那么来看下Element的注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/// 树中特定位置的 [Widget] 实例。
/// An instantiation of a [Widget] at a particular location in the tree.
///
/// Widgets描述了如何配置子树,但同一个widget可用于同时配置多个子树,因为widgets是不可变的。
/// [Element]表示使用widget来配置树中的特定位置。
/// 随着时间的推移,与给定Element关联的widget可能会发生变化,例如,如果父widget重建并为此位置创建新的widget。
/// Widgets describe how to configure a subtree but the same widget can be used
/// to configure multiple subtrees simultaneously because widgets are immutable.
/// An [Element] represents the use of a widget to configure a specific location
/// in the tree. Over time, the widget associated with a given element can
/// change, for example, if the parent widget rebuilds and creates a new widget
/// for this location.
///
/// Elements形成一棵树。大多数elements都有一个唯一的子元素,但某些widgets(例如 [RenderObjectElement] 的子类)可以有多个子元素。
/// Elements form a tree. Most elements have a unique child, but some widgets
/// (e.g., subclasses of [RenderObjectElement]) can have multiple children.
///
/// Elements具有以下生命周期:
/// Elements have the following lifecycle:
///
///  * 框架通过在将用作element初始配置的widget上调用 [Widget.createElement] 来创建一个element。
///  * The framework creates an element by calling [Widget.createElement] on the
///    widget that will be used as the element's initial configuration.
///
///  * 框架调用 [mount] 将新创建的element添加到给定父级中给定槽处的树中。
///    [mount] 方法负责填充任何子widgets,并根据需要调用 [attachRenderObject] 将任何关联的渲染对象附加到渲染树。
///  * The framework calls [mount] to add the newly created element to the tree
///    at a given slot in a given parent. The [mount] method is responsible for
///    inflating any child widgets and calling [attachRenderObject] as
///    necessary to attach any associated render objects to the render tree.
///  
///  * 此时,该element被认为是“active”并且可能出现在屏幕上。
///  * At this point, the element is considered "active" and might appear on
///    screen.
/// 
///  * 在某些时候,父级可能决定更改用于配置此element的widget,例如因为父级使用新状态进行了重建。
///    发生这种情况时,框架将使用新的widget调用 [update]。
///    新的widget将始终具有与旧widget相同的 [runtimeType] 和键。
///    如果父级希望更改树中此位置的widget的 [runtimeType] 或键,则可以通过卸载此element并在此位置填充新widget来实现。
///  * At some point, the parent might decide to change the widget used to
///    configure this element, for example because the parent rebuilt with new
///    state. When this happens, the framework will call [update] with the new
///    widget. The new widget will always have the same [runtimeType] and key as
///    old widget. If the parent wishes to change the [runtimeType] or key of
///    the widget at this location in the tree, it can do so by unmounting this
///    element and inflating the new widget at this location.
///
///  * 在某些时候,祖先可能决定从树中删除此element(或中间祖先),祖先通过调用 [deactivateChild] 本身来完成此操作。
///    停用中间祖先将从渲染树中删除该element的渲染对象,并将该element添加到 [owner] 的非活动element列表中,从而导致框架对此element调用 [deactivate]。
///  * At some point, an ancestor might decide to remove this element (or an
///    intermediate ancestor) from the tree, which the ancestor does by calling
///    [deactivateChild] on itself. Deactivating the intermediate ancestor will
///    remove that element's render object from the render tree and add this
///    element to the [owner]'s list of inactive elements, causing the framework
///    to call [deactivate] on this element.
/// 
///  * 此时,该元素被视为“inactive”并且不会出现在屏幕上。
///    element只能保持inactive状态直到当前动画帧结束。
///    在动画帧结束时,任何仍处于非活动状态的elements都将被卸载。
///  * At this point, the element is considered "inactive" and will not appear
///    on screen. An element can remain in the inactive state only until
///    the end of the current animation frame. At the end of the animation
///    frame, any elements that are still inactive will be unmounted.
/// 
///  * 如果该element被重新合并到树中(例如,因为它或其祖先之一具有可重用的global key),
///    框架将从 [owner] 的非活动element列表中删除该element,对该element调用 [activate],并将该element的渲染对象重新附加到渲染树。
///   (此时,该element再次被视为“active”并且可能出现在屏幕上。)
///  * If the element gets reincorporated into the tree (e.g., because it or one
///    of its ancestors has a global key that is reused), the framework will
///    remove the element from the [owner]'s list of inactive elements, call
///    [activate] on the element, and reattach the element's render object to
///    the render tree. (At this point, the element is again considered "active"
///    and might appear on screen.)
///  
///  * 如果在当前动画帧结束时该element没有重新合并到树中,框架将对该element调用 [unmount]。
///  * If the element does not get reincorporated into the tree by the end of
///    the current animation frame, the framework will call [unmount] on the
///    element.
///
///  * 此时,该element被视为“defunct”,并且将来不会合并到树中。
///  * At this point, the element is considered "defunct" and will not be
///    incorporated into the tree in the future.

可以看到,Element的注释还是挺多的,大部分是描述了它的生命周期。

OK,结合StatefulElementComponentElement以及Element三者的注释,总结一下StatefulElement的特性:

1、StatefulWidgetStatefulElement提供配置,而StatefulElement则是树中特定位置的StatefulWidget实例。

2、StatefulElement用来组成其它Element,是一种组合型的ElementStatefulElement不是直接创建RenderObject,而是通过创建其它Element来间接创建RenderObject

3、…

这里给出了StatefulElement的部分特性,余下的留给个人去探索。OK,继续看下StatefulElement的部分源码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class StatefulElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  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);
  }

  @override
  Widget build() => state.build(this);

  /// The [State] instance associated with this location in the tree.
  ///
  /// There is a one-to-one relationship between [State] objects and the
  /// [StatefulElement] objects that hold them. The [State] objects are created
  /// by [StatefulElement] in [mount].
  State<StatefulWidget> get state => _state!;
  State<StatefulWidget>? _state;
}  

可以看到,在StatefulElement的构造方法中,它做了四件事情。

  • 执行_state = widget.createState()创建了State实例,并赋值给StatefulElement的成员变量_state,也就是说StatefulElement持有了State实例引用。
  • 执行super(widget)调用父类ComponentElement的构造方法,然后一级一级往上传递this,也就是StatefulWidget本身引用,最后在Element的构造方法中赋值给它的成员变量_widget,说明Element会持有Widget的引用。
  • 执行state._element = this;,将StatefulElement赋值给State的成员变量_element,说明State也持有了StatefulElement引用。
  • 执行state._widget = widget;,将StatefulWidget赋值给State的成员变量_widget,说明State也持有了StatefulWidget引用。

OK,现在应该理清了StatefulWidgetStatefulElement以及State之间的关系。因此,可以得出以下结论:

  • StatefulElement持有了State实例引用
  • StatefulElement持有了StatefulWidget实例引用
  • State又会反过来持有了StatefulWidgetStatefulElement的引用
  • StatefulWidget只是负责创建StatefulElementState实例,并不持有它们

关于State的分析后面会讲到,在此之前,我们看下之前的一个runApp示例,补充之前没给出的StatefulWidget加载的一个执行流程图。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void main() {
  _runApp(const MyApp());
}

void _runApp(Widget app) {
  final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
  Timer.run(() {
    binding.attachRootWidget(app);
  });
  binding.scheduleWarmUpFrame();
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MyPage();
  }
}

class MyPage extends StatefulWidget {
  const MyPage({super.key});

  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  @override
  Widget build(BuildContext context) {
    return const ColoredBox(color: Colors.pinkAccent);
  }
}

StatefulWidget加载的一个执行流程图如下所示。

四、分析State源码

遇事不决,直接找State的注释。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/// [StatefulWidget] 的逻辑和内部状态
/// The logic and internal state for a [StatefulWidget].
///
/// 状态是以下信息:(1) 在构建widget时可以同步读取;(2) 在widget的生命周期中可能会发生变化。
/// widget实现者有责任确保在状态更改时使用 [State.setState] 及时通知 [State]。
/// State is information that (1) can be read synchronously when the widget is
/// built and (2) might change during the lifetime of the widget. It is the
/// responsibility of the widget implementer to ensure that the [State] is
/// promptly notified when such state changes, using [State.setState].
///
/// [State] 对象是由框架在填充 [StatefulWidget] 并将其插入到树中时调用 [StatefulWidget.createState] 方法创建的。
/// 因为给定的 [StatefulWidget] 实例可以多次填充(例如,该widget一次在多个位置合并到树中),可能有多个 [State] 对象与给定的 [StatefulWidget] 实例关联。
/// 类似地,如果 [StatefulWidget] 从树中删除,然后再次插入到树中,框架将再次调用 [StatefulWidget.createState] 以创建一个新的 [State] 对象,从而简化 [State] 对象的生命周期。
/// [State] objects are created by the framework by calling the
/// [StatefulWidget.createState] method when inflating a [StatefulWidget] to
/// insert it into the tree. Because a given [StatefulWidget] instance can be
/// inflated multiple times (e.g., the widget is incorporated into the tree in
/// multiple places at once), there might be more than one [State] object
/// associated with a given [StatefulWidget] instance. Similarly, if a
/// [StatefulWidget] is removed from the tree and later inserted in to the tree
/// again, the framework will call [StatefulWidget.createState] again to create
/// a fresh [State] object, simplifying the lifecycle of [State] objects.
///
/// [State] 对象具有以下生命周期:
/// [State] objects have the following lifecycle:
///
///  * 框架通过调用 [StatefulWidget.createState] 创建一个 [State] 对象。
///  * The framework creates a [State] object by calling
///    [StatefulWidget.createState].
///
///  * 新创建的 [State] 对象与 [BuildContext] 关联。
///    这种关联是永久的:[State] 对象永远不会更改其 [BuildContext]。
///    但是,[BuildContext] 本身可以与其子树一起在树中移动。此时,[State]对象就被认为是[mounted]。
///  * The newly created [State] object is associated with a [BuildContext].
///    This association is permanent: the [State] object will never change its
///    [BuildContext]. However, the [BuildContext] itself can be moved around
///    the tree along with its subtree. At this point, the [State] object is
///    considered [mounted].
///
///  *  框架调用[initState]。[State]的子类应覆盖 [initState]去执行依赖于[BuildContext]或widget的一次性初始化,当调用[initState]方法时,它们分别作为[context]和[widget]属性可用。
///  * The framework calls [initState]. Subclasses of [State] should override
///    [initState] to perform one-time initialization that depends on the
///    [BuildContext] or the widget, which are available as the [context] and
///    [widget] properties, respectively, when the [initState] method is
///    called.
///
///  *  框架调用[didChangeDependency]。
///     [State] 的子类应重写 [didChangeDependency] 以执行涉及 [InheritedWidget] 的初始化。
///     如果调用 [BuildContext.dependOnInheritedWidgetOfExactType],则如果inherited widgets随后发生更改或者widget在树中移动,则将再次调用 [didChangeDependency] 方法。
///  * The framework calls [didChangeDependencies]. Subclasses of [State] should
///    override [didChangeDependencies] to perform initialization involving
///    [InheritedWidget]s. If [BuildContext.dependOnInheritedWidgetOfExactType] is
///    called, the [didChangeDependencies] method will be called again if the
///    inherited widgets subsequently change or if the widget moves in the tree.
///
///  * 此时,[State] 对象已完全初始化,框架可能会多次调用其 [build] 方法来获取此子树的用户界面的描述。
///    [State] 对象可以通过调用其 [setState] 方法自发请求重建其子树,这表明它们的某些内部状态已发生变化,可能会影响该子树中的用户界面。
///  * At this point, the [State] object is fully initialized and the framework
///    might call its [build] method any number of times to obtain a
///    description of the user interface for this subtree. [State] objects can
///    spontaneously request to rebuild their subtree by calling their
///    [setState] method, which indicates that some of their internal state
///    has changed in a way that might impact the user interface in this
///    subtree.
///
///  * 在此期间,父widget可能会重建并请求更新树中的此位置以显示具有相同 [runtimeType] 和 [Widget.key] 的新widget。
///    发生这种情况时,框架将更新 [widget] 属性以引用新的widget,然后使用先前的widget作为参数调用 [didUpdateWidget] 方法。
///    [State] 对象应覆盖 [didUpdateWidget] 以响应其关联widget中的更改(例如,启动隐式动画)。
///    框架总是在调用 [didUpdateWidget] 之后调用 [build],这意味着 [didUpdateWidget] 中对 [setState] 的任何调用都是多余的。
///  * During this time, a parent widget might rebuild and request that this
///    location in the tree update to display a new widget with the same
///    [runtimeType] and [Widget.key]. When this happens, the framework will
///    update the [widget] property to refer to the new widget and then call the
///    [didUpdateWidget] method with the previous widget as an argument. [State]
///    objects should override [didUpdateWidget] to respond to changes in their
///    associated widget (e.g., to start implicit animations). The framework
///    always calls [build] after calling [didUpdateWidget], which means any
///    calls to [setState] in [didUpdateWidget] are redundant. (See alse the
///    discussion at [Element.rebuild].)
///
///  * 在开发过程中,如果发生热重载(无论是通过按“r”从命令行“flutter”工具启动,还是从 IDE 启动),都会调用 [reassemble] 方法。
///    这提供了重新初始化在 [initState] 方法中准备的任何数据的机会。
///  * During development, if a hot reload occurs (whether initiated from the
///    command line `flutter` tool by pressing `r`, or from an IDE), the
///    [reassemble] method is called. This provides an opportunity to
///    reinitialize any data that was prepared in the [initState] method.
/// 
///  * 如果包含 [State] 对象的子树从树中删除(例如,因为父级使用不同的 [runtimeType] 或 [Widget.key] 构建了一个widget),则框架调用 [deactivate] 方法。
///    子类应该重写此方法,以清除该对象与树中其它elements之间的任何链接(例如,如果您为祖先提供了指向后代的 [RenderObject] 的指针)。
///  * If the subtree containing the [State] object is removed from the tree
///    (e.g., because the parent built a widget with a different [runtimeType]
///    or [Widget.key]), the framework calls the [deactivate] method. Subclasses
///    should override this method to clean up any links between this object
///    and other elements in the tree (e.g. if you have provided an ancestor
///    with a pointer to a descendant's [RenderObject]).
///
///  * 此时,框架可能会将该子树重新插入到树的另一部分中。
///    如果发生这种情况,框架将确保调用 [build] 以使 [State] 对象有机会适应其在树中的新位置。
///    如果框架确实重新插入该子树,它将在子树从树中删除的动画帧结束之前执行此操作。
///    因此,[State] 对象可以推迟释放大部分资源,直到框架调用它们的 [dispose] 方法。
///  * At this point, the framework might reinsert this subtree into another
///    part of the tree. If that happens, the framework will ensure that it
///    calls [build] to give the [State] object a chance to adapt to its new
///    location in the tree. If the framework does reinsert this subtree, it
///    will do so before the end of the animation frame in which the subtree was
///    removed from the tree. For this reason, [State] objects can defer
///    releasing most resources until the framework calls their [dispose]
///    method.
///
///  * 如果框架在当前动画帧结束时没有重新插入此子树,框架将调用 [dispose],这表明此 [State] 对象将永远不会再次构建。
///    子类应该重写此方法以释放该对象保留的任何资源(例如,停止任何活动的动画)。
///  * If the framework does not reinsert this subtree by the end of the current
///    animation frame, the framework will call [dispose], which indicates that
///    this [State] object will never build again. Subclasses should override
///    this method to release any resources retained by this object (e.g.,
///    stop any active animations).
///
///  * 框架调用 [dispose] 后,[State] 对象被视为已卸载,并且 [mounted] 属性为 false。
///    此时调用[setState]是错误的。生命周期的这个阶段是终结阶段:无法重新挂载已释放的 [State] 对象。 
///  * After the framework calls [dispose], the [State] object is considered
///    unmounted and the [mounted] property is false. It is an error to call
///    [setState] at this point. This stage of the lifecycle is terminal: there
///    is no way to remount a [State] object that has been disposed.

对于State,要关注的重点是它的生命周期,这点从注释中对State生命周期的大量描述也可以看出,这里总结下State生命周期的特性:

1、框架通过调用StatefulWidget.createState创建一个State对象,新创建的State对象与BuildContext关联。

2、State的子类可以覆盖initState方法去执行一次性初始化,此时contextwidget属性可用。

3、State的子类可以重写didChangeDependency以执行涉及InheritedWidget的初始化。

4、State对象完全初始化后,可以通过调用其setState方法自发请求重建其子树。

5、如果父widget重建并请求更新树中的此位置以显示具有相同runtimeTypeWidget.key的新widget,框架将更新widget属性以引用新的widget,然后使用先前的widget作为参数调用didUpdateWidget方法,State的子类可以覆盖didUpdateWidget方法以响应其关联widget中的更改。

6、在开发过程中,如果发生热重载会调用reassemble方法。

7、如果包含State对象的子树从树中删除,那么会调用deactivate方法,State的子类可以覆盖deactivate方法做一些清除Element链接的操作。

8、如果框架在当前动画帧结束时没有重新插入此子树,框架将调用dispose,这表明此State对象将永远不会再次构建。

9、框架调用dispose后,State对象被视为已卸载,并且mounted属性为false

10、…

这里给出了State生命周期的部分特性,余下的留给个人去探索。OK,继续看下State生命周期相关方法的源码。

4.1、initState方法

看下initState方法的注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/// 当该对象插入到树中时调用。  
/// Called when this object is inserted into the tree.
///
/// 框架将为它创建的每个 [State] 对象调用此方法一次。
/// The framework will call this method exactly once for each [State] object
/// it creates.
///
/// 重写此方法以执行初始化,该初始化取决于此对象插入树中的位置(即 [context])或用于配置此对象的widget(即 [widget])。
/// Override this method to perform initialization that depends on the
/// location at which this object was inserted into the tree (i.e., [context])
/// or on the widget used to configure this object (i.e., [widget]).
///
/// {@template flutter.widgets.State.initState}
/// 如果 [State] 的 [build] 方法依赖于一个本身可以更改状态的对象,例如 [ChangeNotifier] 或 [Stream],或者可以订阅接收通知的其它对象,
/// 那么请务必订阅并在 [initState]、[didUpdateWidget] 和 [dispose] 中正确取消订阅:
/// If a [State]'s [build] method depends on an object that can itself
/// change state, for example a [ChangeNotifier] or [Stream], or some
/// other object to which one can subscribe to receive notifications, then
/// be sure to subscribe and unsubscribe properly in [initState],
/// [didUpdateWidget], and [dispose]:
///
///  * 在[initState]中,订阅该对象。
///  * In [initState], subscribe to the object.
///  * 在 [didUpdateWidget] 中,如果更新的小部件配置需要替换对象,则取消订阅旧对象并订阅新对象。
///  * In [didUpdateWidget] unsubscribe from the old object and subscribe
///    to the new one if the updated widget configuration requires
///    replacing the object.
///  * 在[dispose]中,取消订阅该对象。
///  * In [dispose], unsubscribe from the object.
///
/// {@endtemplate}
///
/// 您不能从此方法中使用 [BuildContext.dependOnInheritedWidgetOfExactType]。
/// 但是,在此方法之后将立即调用 [didChangeDependency],并且可以在此处使用 [BuildContext.dependOnInheritedWidgetOfExactType]。
/// You cannot use [BuildContext.dependOnInheritedWidgetOfExactType] from this
/// method. However, [didChangeDependencies] will be called immediately
/// following this method, and [BuildContext.dependOnInheritedWidgetOfExactType] can
/// be used there.
///
/// 该方法的实现应该从调用继承的方法开始,如“super.initState()”。
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.initState()`.
@protected
@mustCallSuper
void initState() {
  // 启用断言时跟踪 [State] 对象的生命周期。
  // 该状态表明[State] 对象已创建。此时会调用[State.initState]。
  assert(_debugLifecycleState == _StateLifecycle.created);
  if (kFlutterMemoryAllocationsEnabled) {
    MemoryAllocations.instance.dispatchObjectCreated(
      library: _flutterWidgetsLibrary,
      className: '$State',
      object: this,
    );
  }
}

关于initState方法的说明都在注释中了,那框架在什么地方触发的initState方法呢?

调用了ComponentElementmount方法,在mount方法中又调用了_firstBuild方法。

_firstBuild方法被子类StatefulElement重写了,并且在该方法中调用了initState方法。

最后附上initState方法的执行流程图。

4.2、didChangeDependencies方法

看下didChangeDependencies方法的注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// 当此 [State] 对象的依赖项发生更改时调用。
/// Called when a dependency of this [State] object changes.
///
/// 例如,如果先前对 [build] 的调用引用了后来更改的 [InheritedWidget],则框架将调用此方法来通知此对象有关更改的信息。
/// For example, if the previous call to [build] referenced an
/// [InheritedWidget] that later changed, the framework would call this
/// method to notify this object about the change.
///
/// 该方法也会在 [initState] 之后立即调用。从此方法调用 [BuildContext.dependOnInheritedWidgetOfExactType] 是安全的。
/// This method is also called immediately after [initState]. It is safe to
/// call [BuildContext.dependOnInheritedWidgetOfExactType] from this method.
///
/// 子类很少重写此方法,因为框架总是在依赖项更改后调用 [build]。
/// 某些子类确实会重写此方法,因为当它们的依赖项发生变化时,它们需要执行一些昂贵的工作(例如,网络获取),而对于每个构建来说,这些工作都太昂贵了。
/// Subclasses rarely override this method because the framework always
/// calls [build] after a dependency changes. Some subclasses do override
/// this method because they need to do some expensive work (e.g., network
/// fetches) when their dependencies change, and that work would be too
/// expensive to do for every build.
@protected
@mustCallSuper
void didChangeDependencies() { }

关于didChangeDependencies方法的说明都在注释中了,那框架在什么地方触发的didChangeDependencies方法呢?

调用了ComponentElementmount方法,在mount方法中又调用了_firstBuild方法。

_firstBuild方法被子类StatefulElement重写了,并且在该方法中调用了initState方法,随后又调用了didChangeDependencies方法,最后执行super._firstBuild(),也就是调用ComponentElement_firstBuild方法。

ComponentElement_firstBuild方法中,执行了rebuild方法。

rebuild方法中又会调用performRebuild方法,该方法被子类StatefulElementComponentElement重写了,这里我们看StatefulElement重写的performRebuild方法,可以看到执行了didChangeDependencies方法。

最后附上didChangeDependencies方法的执行流程图。

4.3、build方法

看下build方法的注释。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/// 描述此widget代表的用户界面部分。
/// Describes the part of the user interface represented by this widget.
///
/// 框架在许多不同的情况下调用此方法。例如:
/// The framework calls this method in a number of different situations. For
/// example:
///
///  * 在调用 [initState] 之后
///  * After calling [initState].
///  * 在调用 [didUpdateWidget] 之后
///  * After calling [didUpdateWidget].
///  * 在收到对 [setState] 的调用之后。
///  * After receiving a call to [setState].
///  * 此 [State] 对象的依赖项更改后(例如,先前 [build] 更改引用的 [InheritedWidget])。
///  * After a dependency of this [State] object changes (e.g., an
///    [InheritedWidget] referenced by the previous [build] changes).
///  * 调用 [deactivate] 后,然后将 [State] 对象重新插入到树中的另一个位置。
///  * After calling [deactivate] and then reinserting the [State] object into
///    the tree at another location.
///
/// 该方法可以在每个帧中调用,并且除了构建widget之外不应该有任何副作用。
/// This method can potentially be called in every frame and should not have
/// any side effects beyond building a widget.
///
/// 框架将此widget下面的子树替换为此方法返回的widget,方法是更新现有子树或删除子树并填充新子树,
/// 具体取决于此方法返回的widget是否可以更新现有子树的根,通过调用 [Widget.canUpdate] 确定。
/// The framework replaces the subtree below this widget with the widget
/// returned by this method, either by updating the existing subtree or by
/// removing the subtree and inflating a new subtree, depending on whether the
/// widget returned by this method can update the root of the existing
/// subtree, as determined by calling [Widget.canUpdate].
///
/// 通常,实现会返回新创建的widgets星座,这些widgets配置有来自该widget的构造函数、给定的 [BuildContext] 和此 [State] 对象的内部状态的信息。
/// Typically implementations return a newly created constellation of widgets
/// that are configured with information from this widget's constructor, the
/// given [BuildContext], and the internal state of this [State] object.
///
/// 给定的 [BuildContext] 包含有关在树中构建此小部件的位置的信息。
/// 例如,上下文为树中的该位置提供一组inherited widgets。
/// [BuildContext] 参数始终与此 [State] 对象的 [context] 属性相同,并且在此对象的生命周期内保持不变。
/// 此处冗余地提供了 [BuildContext] 参数,以便此方法与 [WidgetBuilder] 的签名匹配。
/// The given [BuildContext] contains information about the location in the
/// tree at which this widget is being built. For example, the context
/// provides the set of inherited widgets for this location in the tree. The
/// [BuildContext] argument is always the same as the [context] property of
/// this [State] object and will remain the same for the lifetime of this
/// object. The [BuildContext] argument is provided redundantly here so that
/// this method matches the signature for a [WidgetBuilder].
///
/// 设计讨论
/// ## Design discussion
///
/// 为什么 [build] 方法在 [State] 上,而不是在 [StatefulWidget] 上?
/// ### Why is the [build] method on [State], and not [StatefulWidget]?
///
/// 将 `Widget build(BuildContext context)` 方法放在 [State] 上,
/// 而不是将 `Widget build(BuildContext context, State state)` 方法放在 [StatefulWidget] 上,
/// 可以为开发人员在子类化 [StatefulWidget] 时提供更大的灵活性。
/// Putting a `Widget build(BuildContext context)` method on [State] rather
/// than putting a `Widget build(BuildContext context, State state)` method
/// on [StatefulWidget] gives developers more flexibility when subclassing
/// [StatefulWidget].
///
/// 例如,[AnimatedWidget]是[StatefulWidget]的子类,它引入了一个抽象的`Widget build(BuildContext context)`方法供其子类实现。
/// 例如,[AnimatedWidget]是[StatefulWidget]的子类,它引入了一个抽象的`Widget build(BuildContext context)`方法供其子类实现。
/// For example, [AnimatedWidget] is a subclass of [StatefulWidget] that
/// introduces an abstract `Widget build(BuildContext context)` method for its
/// subclasses to implement. If [StatefulWidget] already had a [build] method
/// that took a [State] argument, [AnimatedWidget] would be forced to provide
/// its [State] object to subclasses even though its [State] object is an
/// internal implementation detail of [AnimatedWidget].
///
/// 从概念上讲,[StatelessWidget] 也可以以类似的方式实现为 [StatefulWidget] 的子类。
/// 如果 [build] 方法位于 [StatefulWidget] 而不是 [State],那么这将不再可能。
/// Conceptually, [StatelessWidget] could also be implemented as a subclass of
/// [StatefulWidget] in a similar manner. If the [build] method were on
/// [StatefulWidget] rather than [State], that would not be possible anymore.
///
/// 将 [build] 函数放在 [State] 而不是 [StatefulWidget] 上也有助于避免与隐式捕获 `this` 的闭包相关的一类错误。
/// 如果您在 [StatefulWidget] 上的 [build] 函数中定义了一个闭包,
/// 则该闭包将隐式捕获“this”,即当前的widget实例,并且在范围内具有该实例的(不可变)字段:
/// Putting the [build] function on [State] rather than [StatefulWidget] also
/// helps avoid a category of bugs related to closures implicitly capturing
/// `this`. If you defined a closure in a [build] function on a
/// [StatefulWidget], that closure would implicitly capture `this`, which is
/// the current widget instance, and would have the (immutable) fields of that
/// instance in scope:
///
/// ```dart
/// // (this is not valid Flutter code)
/// class MyButton extends StatefulWidgetX {
///   MyButton({super.key, required this.color});
///
///   final Color color;
///
///   @override
///   Widget build(BuildContext context, State state) {
///     return SpecialWidget(
///       handler: () { print('color: $color'); },
///     );
///   }
/// }
/// ```
///
/// 例如,假设父级构建“MyButton”时“color”为蓝色,则 print 函数中的“color”指的是蓝色,正如预期的那样。
/// 现在,假设父级用绿色重建“MyButton”。
/// 第一次构建创建的闭包仍然隐式引用原始widget,并且“颜色”仍然打印蓝色,
/// 即使widget已更新为绿色;如果该闭包比它的widget寿命更长,它会打印过时的信息。
/// For example, suppose the parent builds `MyButton` with `color` being blue,
/// the `$color` in the print function refers to blue, as expected. Now,
/// suppose the parent rebuilds `MyButton` with green. The closure created by
/// the first build still implicitly refers to the original widget and the
/// `$color` still prints blue even through the widget has been updated to
/// green; should that closure outlive its widget, it would print outdated
/// information.
///
/// 相反,使用 [State] 对象上的 [build] 函数,在 [build] 期间创建的闭包隐式捕获 [State] 实例而不是 widget 实例:
/// In contrast, with the [build] function on the [State] object, closures
/// created during [build] implicitly capture the [State] instance instead of
/// the widget instance:
///
/// ```dart
/// class MyButton extends StatefulWidget {
///   const MyButton({super.key, this.color = Colors.teal});
///
///   final Color color;
///   // ...
/// }
///
/// class MyButtonState extends State<MyButton> {
///   // ...
///   @override
///   Widget build(BuildContext context) {
///     return SpecialWidget(
///       handler: () { print('color: ${widget.color}'); },
///     );
///   }
/// }
/// ```
///
/// 现在,当父级用绿色重建“MyButton”时,第一个构建创建的闭包仍然引用 [State] 对象,该对象在重建过程中保留,
/// 但框架已更新 [State] 对象的 [widget] 属性以引用新的 `MyButton` 实例,并且 `{widget.color}` 按预期打印绿色。
/// Now when the parent rebuilds `MyButton` with green, the closure created by
/// the first build still refers to [State] object, which is preserved across
/// rebuilds, but the framework has updated that [State] object's [widget]
/// property to refer to the new `MyButton` instance and `${widget.color}`
/// prints green, as expected.
///
/// See also:
///
///  * [StatefulWidget], which contains the discussion on performance considerations.
@protected
Widget build(BuildContext context);

关于build方法的说明都在注释中了,那框架在什么地方触发的build方法呢?

  • 在调用initState方法之后
  • 在调用didUpdateWidget方法之后
  • 在调用setState方法之后。
  • State对象的依赖项更改后(例如,先前build更改引用的InheritedWidget)。
  • 调用deactivate后,然后将State对象重新插入到树中的另一个位置。

关于build方法的各个触发条件,这里就不带大家一一去看源码了,感兴趣的可以自己去探索下。

4.4、didUpdateWidget方法

看下didUpdateWidget方法的注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// 每当widget配置更改时调用。
/// Called whenever the widget configuration changes.
///
/// 如果父widget重建并请求树中的此位置更新以显示具有相同 [runtimeType] 和 [Widget.key] 的新widget,
/// 框架将更新此 [State] 对象的 [widget] 属性以引用新的widget,然后使用前一个widget作为参数调用此方法。
/// If the parent widget rebuilds and requests that this location in the tree
/// update to display a new widget with the same [runtimeType] and
/// [Widget.key], the framework will update the [widget] property of this
/// [State] object to refer to the new widget and then call this method
/// with the previous widget as an argument.
///
/// 重写此方法以在 [widget] 更改时做出响应(例如,启动隐式动画)。
/// Override this method to respond when the [widget] changes (e.g., to start
/// implicit animations).
///
/// 框架总是在调用 [didUpdateWidget] 之后调用 [build],这意味着 [didUpdateWidget] 中对 [setState] 的任何调用都是多余的。
/// The framework always calls [build] after calling [didUpdateWidget], which
/// means any calls to [setState] in [didUpdateWidget] are redundant.
///
/// {@macro flutter.widgets.State.initState}
///
/// 该方法的实现应该从调用继承的方法开始,如“super.didUpdateWidget(oldWidget)”。
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.didUpdateWidget(oldWidget)`.
@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }

关于didUpdateWidget方法的说明都在注释中了,那框架在什么地方触发的didUpdateWidget方法呢?

当Widget通过build方法构建后,就会执行ElementupdateChild方法,然后将该Widget的引用作为第二个参数newWidget传入到updateChild方法中。

然后通过WidgetcanUpdate方法判断newWidget是否可用于更新当前将oldWidget作为其配置的Element

如果canUpdate方法返回true,就会执行child.update(newWidget),也就是执行Elementupdate方法。该方法被子类StatefulElement重写了,所以看下StatefulElementupdate方法。

可以看到,StatefulElementupdate方法中执行了StatedidUpdateWidget方法,这里就不附上didUpdateWidget方法的执行流程图了。

4.5、deactivate方法

看下deactivate方法的注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// 当该对象从树中删除时调用。
/// Called when this object is removed from the tree.
///
/// 每当框架从树中删除此 [State] 对象时,框架都会调用此方法。
/// 在某些情况下,框架会将 [State] 对象重新插入树的另一部分(例如,如果由于使用 [GlobalKey],包含此 [State] 对象的子树从树中的一个位置嫁接到另一个位置) )。
/// 如果发生这种情况,框架将调用 [activate] 以使 [State] 对象有机会重新获取它在 [deactivate] 中释放的任何资源。
/// 然后,它还将调用 [build] 以使 [State] 对象有机会适应其在树中的新位置。
/// 如果框架确实重新插入该子树,它将在子树从树中删除的动画帧结束之前执行此操作。
/// 因此,[State] 对象可以推迟释放大部分资源,直到框架调用它们的 [dispose] 方法。
/// The framework calls this method whenever it removes this [State] object
/// from the tree. In some cases, the framework will reinsert the [State]
/// object into another part of the tree (e.g., if the subtree containing this
/// [State] object is grafted from one location in the tree to another due to
/// the use of a [GlobalKey]). If that happens, the framework will call
/// [activate] to give the [State] object a chance to reacquire any resources
/// that it released in [deactivate]. It will then also call [build] to give
/// the [State] object a chance to adapt to its new location in the tree. If
/// the framework does reinsert this subtree, it will do so before the end of
/// the animation frame in which the subtree was removed from the tree. For
/// this reason, [State] objects can defer releasing most resources until the
/// framework calls their [dispose] method.
///
/// 子类应该重写此方法,以清除该对象与树中其它elements之间的任何链接(例如,如果您为祖先提供了指向后代的 [RenderObject] 的指针)。
/// Subclasses should override this method to clean up any links between
/// this object and other elements in the tree (e.g. if you have provided an
/// ancestor with a pointer to a descendant's [RenderObject]).
///
/// 该方法的实现应该以调用继承的方法结束,如“super.deactivate()”。
/// Implementations of this method should end with a call to the inherited
/// method, as in `super.deactivate()`.
@protected
@mustCallSuper
void deactivate() { }

关于deactivate方法的说明都在注释中了,那框架在什么地方触发的deactivate方法呢?

当Widget通过build方法构建后,就会执行ElementupdateChild方法,然后将该Widget的引用作为第二个参数newWidget传入到updateChild方法中。

这里先解释下红框部分:

  • 上面红框:表示如果newWidgetnull,并且child不为null,那么需要将child删除,因为它不再具有配置。
  • 下面红框:表示如果newWidgetchild两者都不为空,并且如果Widget.canUpdate()返回false,表明新的Widget与旧的Widget不相同了,就需要先将老的child从渲染树中去除,重新去挂载新child

这里多插一嘴,updateChild方法作用就是使用给定的新配置更新给定的child,它是widgets系统的核心,每次我们根据更新的配置添加、更新或删除child时都会调用它。

这里总结下updateChild方法的执行:

newWidget == null newWidget != null
child == null 返回null 返回新的Element
child != null 旧的child被移除, 返回null 如果可能,更新旧child,返回child或新的Element

在红框部分,都会调用deactivateChild方法,然后在deactivateChild方法中执行owner!._inactiveElements.add(child)

_InactiveElementsadd方法中,执行了_deactivateRecursively()

_InactiveElements_deactivateRecursively()中,执行了element.deactivate()

Elementdeactivate方法被子类StatefulElement重写了,所以看下StatefulElementdeactivate方法。

可以看到,StatefulElementdeactivate方法中执行了Statedeactivate方法,这里就不附上deactivate方法的执行流程图了。

4.6、dispose方法

看下dispose方法的注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// 当该对象从树中永久删除时调用。
/// Called when this object is removed from the tree permanently.
///
/// 当此 [State] 对象不再构建时,框架将调用此方法。
/// 框架调用 [dispose] 后,[State] 对象被视为已卸载,并且 [mounted] 属性为 false。此时调用[setState]是错误的。
/// 生命周期的这个阶段是终结阶段:无法重新挂载已释放的 [State] 对象。
/// The framework calls this method when this [State] object will never
/// build again. After the framework calls [dispose], the [State] object is
/// considered unmounted and the [mounted] property is false. It is an error
/// to call [setState] at this point. This stage of the lifecycle is terminal:
/// there is no way to remount a [State] object that has been disposed.
///
/// 子类应该重写此方法以释放该对象保留的任何资源(例如,停止任何活动的动画)。
/// Subclasses should override this method to release any resources retained
/// by this object (e.g., stop any active animations).
///
/// {@macro flutter.widgets.State.initState}
///
/// 该方法的实现应该以调用继承的方法结束,如“super.dispose()”。
/// Implementations of this method should end with a call to the inherited
/// method, as in `super.dispose()`.
@protected
@mustCallSuper
void dispose() {
  assert(_debugLifecycleState == _StateLifecycle.ready);
  assert(() {
    _debugLifecycleState = _StateLifecycle.defunct;
    return true;
  }());
  if (kFlutterMemoryAllocationsEnabled) {
    MemoryAllocations.instance.dispatchObjectDisposed(object: this);
  }
}

关于dispose方法的说明都在注释中了,那框架在什么地方触发的dispose方法呢?

RendererBindinginitInstances方法中,会调用addPersistentFrameCallback方法注册一个持久帧回调。

在持久帧回调_handlePersistentFrameCallback中调用了RendererBindingdrawFrame方法。

RendererBindingdrawFrame方法被子类WidgetsBinding重写了,所以看下WidgetsBindingdrawFrame方法。

WidgetsBindingdrawFrame方法中执行了BuildOwnerfinalizeTree方法。

BuildOwnerfinalizeTree方法中执行了_InactiveElements_unmountAll方法。

_InactiveElements_unmountAll方法中执行了_unmount方法。

_InactiveElements_unmount方法中又调用了Elementunmount方法。

Elementunmount方法被子类StatefulElement重写了,所以看下StatefulElementunmount方法。

可以看到,StatefulElementunmount方法中执行了Statedispose方法,这里就不附上dispose方法的执行流程图了。

4.7、setState方法

看下setState方法的注释。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
/// 通知框架该对象的内部状态已更改。
/// Notify the framework that the internal state of this object has changed.
///
/// 每当您更改 [State] 对象的内部状态时,请在传递给 [setState] 的函数中进行更改:
/// Whenever you change the internal state of a [State] object, make the
/// change in a function that you pass to [setState]:
///
/// ```dart
/// setState(() { _myState = newValue; });
/// ```
///
/// 提供的回调将立即同步调用。它不能返回 future(回调不能是“异步”),因为这样就不清楚状态何时实际被设置。
/// The provided callback is immediately called synchronously. It must not
/// return a future (the callback cannot be `async`), since then it would be
/// unclear when the state was actually being set.
///
/// 调用 [setState] 通知框架此对象的内部状态已更改,可能会影响此子树中的用户界面,这会导致框架为此 [State] 对象安排 [build]。
/// 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.
///
/// 如果您只是直接更改状态而不调用 [setState],框架可能不会安排 [build],并且此子树的用户界面可能不会更新以反映新状态。
/// If you just change the state directly without calling [setState], the
/// framework might not schedule a [build] and the user interface for this
/// subtree might not be updated to reflect the new state.
///
/// 通常,建议 [setState] 方法仅用于包装对状态的实际更改,而不是可能与更改相关的任何计算。
/// 例如,这里将 [build] 函数使用的值递增,然后将更改写入磁盘,但只有递增被包装在 [setState] 中:
/// Generally it is recommended that the [setState] method only be used to
/// wrap the actual changes to the state, not any computation that might be
/// associated with the change. For example, here a value used by the [build]
/// function is incremented, and then the change is written to disk, but only
/// the increment is wrapped in the [setState]:
///
/// ```dart
/// Future<void> _incrementCounter() async {
///   setState(() {
///     _counter++;
///   });
///   Directory directory = await getApplicationDocumentsDirectory(); // from path_provider package
///   final String dirName = directory.path;
///   await File('$dirName/counter.txt').writeAsString('$_counter');
/// }
/// ```
///
/// 框架调用[dispose]后调用该方法是错误的。您可以通过检查[mounted]属性是否为true来判断调用该方法是否合法。
/// It is an error to call this method after the framework calls [dispose].
/// You can determine whether it is legal to call this method by checking
/// whether the [mounted] property is true.
@protected
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().',
        ),
      ]);
    }
    // We ignore other types of return values so that you can do things like:
    //   setState(() => x = 3);
    return true;
  }());
  _element!.markNeedsBuild();
}

碍于篇幅有限,本文不会去分析setState方法的执行流程,后续可能会出一篇来专门分析setState方法。

4.8、reassemble方法

看下reassemble方法的注释。

1
2
3
4
5
6
7
8
9
/// {@macro flutter.widgets.Element.reassemble}
///
/// 除了调用此方法之外,还保证在发出重组信号时调用 [build] 方法。因此,大多数widgets不需要在 [reassemble] 方法中执行任何操作。
/// In addition to this method being invoked, it is guaranteed that the
/// [build] method will be invoked when a reassemble is signaled. Most
/// widgets therefore do not need to do anything in the [reassemble] method.
@protected
@mustCallSuper
void reassemble() { }

reassemble方法是专门为开发调试提供的,通过点击ASFlutter Hot Reload按钮来触发,并且在Release模式下永远不会被调用。所以关于reassemble方法的触发流程,这里就不带大家一一去看源码了,感兴趣的可以自己去探索下。

至此,State的生命周期方法分析完毕,最后给出State的生命周期流程图。

五、State生命周期示例

现在要实现的需求:

1、点击“减一”按钮,由MyChild控制setState进行构建

2、点击“+”、“添加或移除MyChild控件”按钮,由MyParent控制setState进行构建

3、MyParentMyChild中的计数器值要保持一致

4、不同操作场景下,观察MyChild的生命周期变化

那么,最终的实现代码如下。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyParent(title: 'State生命周期演示'),
    );
  }
}

class MyParent extends StatefulWidget {
  const MyParent({super.key, required this.title});

  final String title;

  @override
  State<MyParent> createState() => _MyParentState();
}

class _MyParentState extends State<MyParent> {
  int _counter = 0;
  bool _isShowCounter = true;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  void _toggleCounter() {
    setState(() {
      _isShowCounter = !_isShowCounter;
    });
  }

  void _counterChanged(int counter) => _counter = counter;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: _isShowCounter
          ? MyChild(counter: _counter, counterChanged: _counterChanged)
          : Container(),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
          const SizedBox(height: 10),
          FloatingActionButton(
            onPressed: _toggleCounter,
            tooltip: 'ToggleCounter',
            child: Icon(_isShowCounter ? Icons.toggle_on : Icons.toggle_off),
          )
        ],
      ), 
    );
  }
}

class MyChild extends StatefulWidget {
  final int counter;
  final ValueChanged<int> counterChanged;

  const MyChild({super.key, required this.counter, required this.counterChanged});

  @override
  State<MyChild> createState() => _MyChildState();
}

class _MyChildState extends State<MyChild> {
  int _counter = 0;

  void _decrementCounter() {
    setState(() {
      debugPrint('setState');
      _counter--;
    });
    widget.counterChanged(_counter);
  }

  @override
  void initState() {
    super.initState();
    debugPrint('initState');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _counter = widget.counter;
    debugPrint('didChangeDependencies');
  }

  @override
  void didUpdateWidget(covariant MyChild oldWidget) {
    super.didUpdateWidget(oldWidget);
    _counter = widget.counter;
    debugPrint('didUpdateWidget');
  }

  @override
  Widget build(BuildContext context) {
    debugPrint('build');
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text(
            'You have pushed the button this many times:',
          ),
          Text(
            '$_counter',
            style: Theme.of(context).textTheme.headlineMedium,
          ),
          ElevatedButton(
            onPressed: _decrementCounter,
            child: const Text('减一'),
          ),
        ],
      ),
    );
  }

  @override
  void reassemble() {
    super.reassemble();
    debugPrint('reassemble');
  }

  @override
  void deactivate() {
    super.deactivate();
    debugPrint('deactivate');
  }

  @override
  void dispose() {
    super.dispose();
    debugPrint('dispose');
  }
}

程序运行后,效果如图下所示。

  • 程序刚运行起来,打印日志为:
1
2
3
I/flutter ( 7658): initState
I/flutter ( 7658): didChangeDependencies
I/flutter ( 7658): build
  • 点击“减一”按钮,打印日志为:
1
2
I/flutter ( 7658): setState
I/flutter ( 7658): build
  • 点击“+”按钮,打印日志为:
1
2
I/flutter ( 7658): didUpdateWidget
I/flutter ( 7658): build
  • 点击“Flutter Hot Reload”按钮,打印日志为:
1
2
3
I/flutter ( 7658): reassemble
I/flutter ( 7658): didUpdateWidget
I/flutter ( 7658): build
  • 点击“添加或移除MyChild控件”按钮,此时状态为移除,那么打印日志为:
1
2
I/flutter ( 7658): deactivate
I/flutter ( 7658): dispose
  • 点击“添加或移除MyChild控件”按钮,此时状态为添加,那么打印日志为:
1
2
3
I/flutter ( 7658): initState
I/flutter ( 7658): didChangeDependencies
I/flutter ( 7658): build