“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 theStatefulWidgetStatebuildThe number of components that method builds. Ideally, oneStatefulWidgetThere 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 yourStatefulWidgetYou 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 oneWidgetAnd then passed as a child componentStatefulWidget.
  • Use as much as possibleconstDecorates the child component constructor. That’s what we’re talking aboutconst(Decrypt the const keyword of Flutter) Yes. In fact, usingconstEmbellishment 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 inIgnorePointerIn the. This approach essentially changes the hierarchy of the subcomponent tree. The child components should be wrapped uniformly inIgnorePointerMedium, and then passIgnorePointerignoringProperty 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 entireStatefulWidgetThe life cycle of the. If not convenient to useGlobalKeyIf so, consider using itKeyedSubtreeComponent to applyGlobalKey.
  • ifStatefulWidgetIf some attributes are immutable, then the definition of these attributes should take precedenceWidgetAnd declared asfinalRather thanStateIn, this can be reducedStateData 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!