StatefulWidget, StatelessWidget and InheritedWidget
Last updated
Was this helpful?
Last updated
Was this helpful?
状态就是信息,其中包含了
Stateful widget和Stateless widget是Flutter widget中的基本概念。
简单来讲,如果一个widget需要在实例化后管理内部状态,那这个widget就应该是stateful,即有状态的。例如checkbox就包含“选中”与“非选中”两种状态,而我们并不希望因为用户改变了checkbox的状态就重绘整个widget甚至整个界面。而Stateless widget仅能在每次实例化的时候基于传入的参数绘制widget,以后如果需要改变则需要重新实例化该widget。StatelessWidget适用于仅用做信息展示而无需用户交互的widget。
这里给出原生Stateful和Stateless widget的列表供参考
与Stateless Widget只需实现一个类不同,要实现一个Stateful Widget需要创建两个类,分别继承自StatefulWidget和State。
StatefulWidget封装widget对外的调用方法,而State类管理widget内部状态以及重绘方法,即build()方法。
StateWidget实例化时调用createState()方法进行widget初始化,当widget状态变化时,state对象调用setState()方法来通知框架重绘widget。
很多时候我们会发现混淆的地方,特别是当自定义widget中存在一种层次解构的时候。例如如果我们要做一个会员卡widget A,但是其中又要嵌入一个自定义图表widget B,其可在条状图和饼状图间进行切换来显示会员消费趋势和比例。
选择的依据在于“谁会来管理widget的状态?是widget 自己还是其父widget?“
如果widget自己会管理状态,例如在B中包含来一个switch widget,那B就应该是有状态的,A就可以是无状态的;
如果widget状态由父widget管理,例如B仅是一个图表widget,而switch开关在A上。那B就是无状态,而A则是有状态的。
这里有三个例子,示范三种不同的状态管理实现方式。
There are several techniques one can use to minimize the impact of rebuilding a stateless widget:
Use const
widgets where possible, and provide a const
constructor for the widget so that users of the widget can also do so.
There are several techniques one can use to minimize the impact of rebuilding a stateful 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.
If a subtree does not change, cache the widget that represents that subtree and re-use it each time it can be used. It is massively more efficient for a widget to be re-used than for a new (but identically-configured) widget to be created. Factoring out the stateful part into a widget that takes a child argument is a common way of doing this.
Use const
widgets where possible. (This is equivalent to caching a widget and re-using it.)
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 is promptly notified when such state changes, using .
The method of a stateless widget is typically only called in three situations: the first time the widget is inserted in the tree, when the widget's parent changes its configuration, and when an it depends on changes.
If a widget's parent will regularly change the widget's configuration, or if it depends on inherited widgets that frequently change, then it is important to optimize the performance of the method to maintain a fluid rendering performance.
Minimize the number of nodes transitively created by the build method and any widgets it creates. For example, instead of an elaborate arrangement of s, s, s, and es to position a single child in a particularly fancy manner, consider using just an or a. Instead of an intricate layering of multiple s and with s to draw just the right graphical effect, consider a single widget.
Consider refactoring the stateless widget into a stateful widget so that it can use some of the techniques described at , such as caching common parts of subtrees and using s when changing the tree structure.
If the widget is likely to get rebuilt frequently due to the use of s, consider refactoring the stateless widget into multiple widgets, with the parts of the tree that change being pushed to the leaves. For example instead of building a tree with four widgets, the inner-most widget depending on the , consider factoring out the part of the build function that builds the inner-most widget into its own widget, so that only the inner-most widget needs to be rebuilt when the theme changes.
There are two primary categories of s.
The first is one which allocates resources in and disposes of them in , but which does not depend on s or call . Such widgets are commonly used at the root of an application or page, and communicate with subwidgets via s, 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.
The second category is widgets that use or depend on 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 or and allocate resources, but the important part is that they rebuild.)
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 . (Obviously this isn't always practical, but the closer a widget gets to this ideal, the more efficient it will be.)
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 , always wrap the child widget in an and control the 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 , for example, no layout or repaint is necessary at all).
If the depth must be changed for some reason, consider wrapping the common parts of the subtrees in widgets that have a that remains consistent for the life of the stateful widget. (The widget may be useful for this purpose if no other widget can conveniently be assigned the key.)