“This is the 7th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
preface
For those who are new to Flutter, Flutter is easy to develop once they have “figured out” the StatelessWidgets and StatefulWidgets. This is usually fine when you write a Demo — you can update the interface using setState, but when the page and business logic get complicated, there are a lot of performance issues that you don’t know about and solve. Therefore, it’s worth taking a closer look at StatefulWidget.
The classification of StatefulWidget
The StatefulWidget actually comes in two types, and that is whether the setState method is called to refresh the interface. If you are using a StatefulWidget only because some properties need to be initialized in initState, the overhead of such a StatefulWidget is small and can be used without fear. However, if you’re going to call setState frequently to refresh the interface, be careful! Many performance problems are caused by setState.
The rendering mechanism of StatefulWidget
First of all, Flutter is rendered frame by frame, so any time your interface changes, you actually change the display frame. Of course, Flutter makes use of the previous render element, the RenderObject, as much as possible for performance. Let’s use a simple example from an official YouTube video to illustrate how the entire StatefulWidget’s rendering process changes with setState.
class ItemCounter extends StatefulWidget {
ItemCounter({Key? key, required this.name}) : super(key: key);
final String name;
@override
_ItemCounterState createState() => _ItemCounterState();
}
class _ItemCounterState extends State<ItemCounter> {
int count = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Text('${widget.name}: $count'), onTap: () { setState(() { count++; }); }); }}Copy the code
As we discussed in the previous chapter, rendering control is actually done through Element Tree, and Widget Tree only provides configuration information. For the example above, the State object is not actually accessible to the outside world; it is actually held by the StatefulElement.
StatefulElement(StatefulWidget widget)
: state = widget.createState(),
/ /...
Copy the code
The mapping between the various elements of the entire component is shown below (the images below are from the original video), and the actual element rendered by StatefulElement is also a StatelessElement. The example above starts out as a rendering component like Text(Tom:0) (the GestureDetector is not a rendering element) where count in State has a value of 0.
Now let’s see what happens when we click to trigger count increment by 1. When the count changes, we know that the build method is called again and a Text(Tom: 1) The new Widget, which will discard the old Text(Tom:0) when the Flutter arrives on the next frame, will be replaced by a new Widget called Text(Tom: 1). However, for performance reasons, because Flutter detects that the two widgets correspond to the same component type, it points StatefulElement updates directly to the new Widget rather than building a new StatefulElement. The following two images show the whole process.
Note that if a node type in the component tree has not changed but is replaced with a new Widget of the same type, the StatefulElement will not be removed to create a new object even if the property changes. Instead, update the old StatefulElement to point to the new Widget. For example, we changed the previous ItemCounter(name: ‘Tom’) to ItemCounter(name: ‘Dan’). This process can be illustrated in the following 4 graphs.
This process is reflected in the didUpdateWidget’s lifecycle function.
@override
void didUpdateWidget(covariant ItemCounter oldWidget) {
super.didUpdateWidget(oldWidget);
}
Copy the code
Precautions for using StatefulWidget
With the above analysis, it’s easy to understand the official recommendations for using StatefulWidgets that need to be refreshed by calling setState.
- Push state maintenance in the component tree down to the leaf node as far as possible. This makes sense; the higher the level of state maintenance, the larger the reconstructed component tree. Of course, placing state maintenance at lower levels of leaf nodes performs better. For example, if you have a clock component on your page that updates periodically every second, then the maintenance of this time state should be a separate clock component that maintains the time state itself.
- To minimize the
StatefulWidget
的State
中build
The number of components that method builds. Ideally, oneStatefulWidget
There should be only one child component, and that component corresponds to oneRenderObject
. This may be hard to satisfy in reality, but it’s a guiding principle if yourStatefulWidget
You build too many components, and performance naturally degrades. This is a good time to consider using the state management plug-in for partial refreshes. - If the subcomponent tree does not change throughout its life cycle, you should consider caching the subcomponent tree for reuse. This will perform much better than rebuilding the subcomponent tree each time. The usual practice is to separate the subcomponent tree into one
Widget
And then passed as a child componentStatefulWidget
. - Use as much as possible
const
Decorates the child component constructor. That’s what we’re talking aboutconst
(Decrypt the const keyword of Flutter) Yes. In fact, usingconst
Embellishment is a form of caching. - If possible, avoid changing the hierarchy of the subcomponent tree or the component types in the subcomponent tree. For example, when returning a child component, it is possible to return a child component based on conditions or wrap a child component in
IgnorePointer
In the. This approach essentially changes the hierarchy of the subcomponent tree. The child components should be wrapped uniformly inIgnorePointer
Medium, and then passIgnorePointer
的ignoring
Property to control. This is because any action that changes the depth of the subcomponent tree requires rebuilding, rearranging, and redrawing the entire subcomponent tree. Changing the attributes of just one node will reduce the scope of the change considerably (for example, in this case, there is no need to rearrange and redraw). - Assuming that you have to change the hierarchy of the subcomponent tree, you should consider using GlobalKey to make invariant portions of the subcomponent tree within the entire
StatefulWidget
The life cycle of the. If not convenient to useGlobalKey
If so, consider using itKeyedSubtree
Component to applyGlobalKey
. - if
StatefulWidget
If some attributes are immutable, then the definition of these attributes should take precedenceWidget
And declared asfinal
Rather thanState
In, this can be reducedState
Data to be maintained.
conclusion
In fact, most of the content of this article is from official documents and videos, but I suspect that those who do Flutter development have not seen much of it. When we get into a new technology, we tend to run a Demo, and then look at the routine and start developing. Such initial development speed is indeed fast, but it is often confused when encountering problems. Therefore, there are three suggestions:
- If time permits, read the official documentation, surrounding applications, and documentation from the beginning. This will save a lot of development costs and time — especially if performance bottlenecks require refactoring.
- If time does not allow, go back to the official documentation of Flutter and read it several times. The documentation of Flutter is very thorough and has many accompanying videos that should solve most of your confusion.
- More input. Although there are not many players of Flutter in China, there are still many foreign players. Keep browsing foreign blogs and official websites, which can save you a lot of detours.
I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder. If you feel you have something to gain, please give three pairs of love as follows:
👍🏻 : a praise to encourage!
🌟 : Collect articles, easy to look back!
💬 : Comment exchange, mutual progress!