foreword
We can MediaQuery.of(context)
get some device and system related information through methods, such as the height of the status bar, whether it is currently in dark mode, etc. It is quite convenient to use, but we must also pay attention to possible page rebuild problems. This article will introduce a typical example, and go deep into the source code to discuss the cause of the rebuild, and finally introduce several ways to avoid the rebuild.
typical example
Take the courier checking scenario in the courier app as an example. The homepage uses MediaQuery.of(context).padding.top
the height of the status bar, and the user clicks the "Check courier" button to jump to the courier checking interface. On the courier checking interface, the user can enter the tracking number to perform query operations.
When the build method on the home page is called, it will output the logs we added in advance. We found that when the keyboard of the check express interface pops up, the build method of the home page is called multiple times:
The build code of the main interface is as follows:
Source Code Exploration
Since it is because the main interface is used in the build method MediaQuery.of(context)
, which leads to the rebuild operation when the keyboard pops up/hides, let's look at the next MediaQuery
class first.
MediaQuery
It inherits from InheritedWidget
and does not have a rewriting createElement
method itself. From the perspective of the flutter three trees, the corresponding one Element
is InheritedElement
. There are two attributes, data and child, we can get some device/system related attributes from data.
There are also two more important methods:
fromWindow(key : Key, child : Widget)
This method returns the object directly _MediaQueryFromWindow
, which will be described in detail later.
of(context : BuildContext)
The method calls dependOnInheritedWidgetOfExactType, and then we analyze the calling process behind it in detail.
MediaQuery.of(context) call process
The input parameter is context
, the main interface in this example is StatelessWidget
, so here context
is StatelessElement
. The overall calling process is as follows:
dependOnInheritedWidgetOfExactType
_inheritedWidgets
Query whether there is MediaQuery
a type from the list InheritedElement
. From the perspective of the three trees, it is to search upwards from the current node to find the nearest MediaQuery
control. If found, call dependOnInheritedElement
the method (under normal circumstances, it must be found, and will be described in detail below).
dependOnInheritedElement
This method is responsible for saving the found InheritedElement
(that is, MediaQuery
the corresponding Element
) and calling InheritedElement#updateDependencies
the method.
updateDependencies
setDependencies
StatelessElement
The last two methods are very simple, and their function is to store the corresponding ones of the homepage in MediaQuery
the corresponding ones InheritedElement#_dependents
.
After studying MediaQuery.of(context)
the principle behind it, we can know: by calling the of method, the main interface corresponds to the binding relationship established, Element
and the reference of the main interface is stored correspondingly .MediaQuery
MediaQuery
InheritedElement
Element
Rebuild starting point
When introducing dependOnInheritedWidgetOfExactType
the method, we mentioned: Searching from the current node to the parent node, under normal circumstances, it is sure to find MediaQuery
the control. This is because WidgetsApp
a root is automatically created for us in MediaQuery
.
In main
the method, whatever is used CupertinoApp
or MaterialApp
will be created internally at the end WidgetsApp
. Let's look directly at _WidgetsAppState#build
a code snippet in the method:
will be checked first widget.useInheritedMediaQuery
, this property defaults to false
. If you don't set the property when you create MaterialApp
/ , or set the property to null, but can't find it , then the method will be called here.CupertinoApp
useInheritedMediaQuery
MediaQueryData
MediaQuery.fromWindow
When introduced above MediaQuery#fromWindow
, we knew it would create _MediaQueryFromWindow
controls.
_MediaQueryFromWindow
There are not many codes, and all the codes related to this article are posted. You can take a look at them yourself. The code is shown in the figure above.
build
The control is created in the method MediaQuery
, and the method is implemented didChangeMetrics
. When the phone rotates and the keyboard pops up/hides, this method will be called, and it will didChangeMetrics
be called internally setSate
, causing build
the method to be called again.
Through the principle of flutter's three trees, we can know that the above-mentioned "build method is called again" involves the MediaQueryFromWindow
corresponding Element
method updateChild
. Let's briefly look at updateChild
the internal processing rules:
For MediaQueryFromWindow, a new MediaQuery Widget is created every time. According to the source code of Element#updateChild (not the focus of this article, we will not analyze its source code in detail), it will eventually call the update method of the Element corresponding to MediaQuery.
After a series of jumps, the following two core methods will eventually be called:
The method introduced above MediaQuery.of(context)
will eventually put the input parameters Context
into _dependents
the variable, and this will be traversed here map
, and each method will be called Context
, didChangeDependecies
and didChangeDependecies
this will Context
be set to a dirty state. When the next frame comes, it will be redrawn and this Context
method build
will be called.
So, the case is solved, and the reason why the express homepage will be rebuilt when the keyboard pops up/hides is found!
The overall rebuild call process is as follows. If you are interested, you can combine this call flow chart to see the source code:
Ways to avoid rebuild
After studying the source code, the solution becomes very simple.
- The custom
useInheritedMediaQuery
attribute is true, and the outermost layerMediaQuery
isWidgetsApp
used for creationMediaQuery
, instead of using_MediaQueryFromWindow
the control that monitors the size change of the application.
- Avoid using the method on the page
MediaQuery.of(context)
, you can use the corresponding alternative method, for example, in this example, you can use the following code instead, pay attention to the unit conversion.
- If you must use
MediaQuery.of(context)
the method, you can useBuilder
the control package, and the input parameters of the of method can be passed in hereBuilder
,context
so that the rebuild is onlyBuilder
the widget subtree under the control package.
Summarize
When the app interface becomes more complex, we have to consider optimizing interface performance. The examples introduced in this article are very common in development. If you don’t understand the mechanism of MediaQuery.of, it may cause a large number of interfaces that use this method to redraw, resulting in page freezes and frame rate drops. We analyzed the source code logic behind it in detail and introduced the solution, hoping to provide some help for your tuning work.
Author: JD Logistics Shen Mingliang
Source: JD Cloud Developer Community