Performance improvements in the Flutter app

Tram Ho

Flutter already has an optimized mechanism for Performance rendering the app interface, but if we pay more attention, we can avoid unnecessary rebuilding. The article is based on my practical experience and research, if there is anything wrong / additional, please help me below!

First in terms of interface, most apps operate in the 60fps environment (some games require 120fps requires a higher configuration). With 60fps, the eyes can feel the displayed interface is quite ok. At 60fps, that means drawing 60 frames / 1 s => 1 frame needs 0.0167s to draw. If it takes longer, the application feels slow, one of the reasons for this is rebuilding many widgets or running heavy tasks. Here are the ways I usually use to limit this.

1. Use const. Anywhere can be used

Like caching, const constructor restricts rebuilding, in fact most widgets are not rebuild except for dc wrap widget from InhertWidget. Take the example from the sample app created when we create a new project:

When pressing floatingActionButton will run setState function, it turns to rebuild the entire state is MyHomePage, from Scaffold, AppBar, title AppBar, Center, Column, Text child in column, FloatingActionButton. But notice that Text (‘You have pushed the button this many times:’), which is a constant widget, defined before compiler, if changed to

then even if rebuild the state, the Text widget above will not be rebuild. You can check it in the Flutter Performance tab. To be able to const Constructor, the class needs to satisfy the following conditions:

  • The filed fields are final
  • The constructor in the class is const
  • the parameters passed are const. (The title in the appBar is Text (widget.title) does not meet this condition so const cannot be used).

Most widgets in Flutter are constructor const, if not constructor is const, such as Container, it can be converted to Sizedbox, Align, Padding (when not using Container only properties).

Because Flutter has a very optimized interface mechanism, as in the above example, without using const, it will rebuild the Widet, but creating a widget is a light task, even if a rebuild does not affect much, the reality of the back of the widget tree As Element tree, rebuilding the new element tree is a heavy task, but in the above case, using const or not using const does not cause the Element tree to rebuild because it checks that the child objects are unchanged widgets so we don’t rebuild. However, using const is still more optimal, in the document also says this:

https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html#performance-considerations

Use const widgets where possible. (This is equivalent to caching a widget and re-using it.)

2. Push the state down as far as possible

For example project default, when the _increment () function is called, it will run setState () rebuil d over the entire _MyHomePageState. In fact, the parts that need to be manipulated and changed are only Column and floatingActionButton, so if you split these two parts into a new statefulWidget and convert MyHomePage to a statelessWidget, you won’t be rebuilding Scaffold, AppBar, Center. converted to FlatButton / RaisedButton.

or simply use StreamBuilder.

The Wrap Text needs to change into StreamBuilder. function _icrement instead of calling setState convert to add value to streamController. With this change, each time you press the button, you just rebuild StreamBuilder and Text in StreamBuilder.
In a WidgetTree, if a part is mutable (actually Widget is immutable, Element is mutable) then create a subtree from that part, to avoid rebuilding, then rebuil the entire parent state, when setState it only rebuilds state of that subtree. .

3. Constraint to change the texture of the Widget tree.

or

Both examples above have the same logic and display, but B will be better than A. Both will rebuild Widget Tree, but B won’t be rebuild Element tree, and A will rebuild Element Tree, that the RenderObject tree will also be rebuild. Widgets only store the state, not manage the state, but only Element manage the state, so rebuilding the Element tree will be much heavier than the widget tree (essentially the element mediating the connection between Widget and RenderObject).

4. Avoid overusing BuildContext, if you have to listen to the change then limit to the smallest context possible.

Currently we often use Provider + ChangeNotifier / Bloc pattern to manage the state (in addition, there are also Redux, Mobx). Currently, I like to use Provider + StateNotifier (+ freezed).

Provider inherits from InhertWidget. The advantage of InhertWidget is to be able to access, to the nearest InhertWidet, and only when needed to notify certain Widgets that have designated listeners to rebuild when there is a change. The second feature surprised me when the state with heavy Page tasks was rebuild. The structure is as follows:

When the name is changed, it is expected that only WidgetB with child Text listens to the name from the Profile changed, but WidgetA, B, C, … are rebuild. Quite surprising, but in retrospect the problem is that using the context in Text (‘{Provider.of & ltProfile & gt (context) .name}’) is the context of the build function, which means it’s _UpdateProfileState. This means that when the Profile instance is passed from the top down, there is a change, the whole context will rebuild which means rebuilding the entire _UpdateProfileState. To fix this just wrap Text ({Provider.of

(context) .name} ‘) to the Widget Consumer from the package provider is fine. Or wrap it into Widget Builder. Both of these widgets grant their own context to the context that is listened to in the text, so they won’t be rebuilding other widgets.

Conclusion: Above are the main ways that I often use to limit rebuilding.

Share the news now

Source : Viblo