I’m working on a Flutter project where I’m accessing MediaQuery
properties (e.g., MediaQuery.viewInsetsOf(context).bottom)
inside a widget, let’s call it Widget2.
Widget2 is a child of Widget1, and I noticed that whenever the value of MediaQuery.viewInsetsOf(context).bottom
changes (like when the keyboard is shown or hidden), it triggers the build method of Widget2 but not its parent, Widget1. In other words, only Widget2 rebuilds when the property value changes, not the widgets above it in the tree.
My Question:
-
How does Flutter manage to identify which widget to rebuild when a
MediaQuery
property changes? -
Why is only the widget directly accessing the
MediaQuery
value rebuilt and not its parent or ancestor widgets?
Curious how this works under the hood. Any insights into how Flutter optimizes the rebuild process in such cases would be helpful!
Additional Information:
- Flutter Version: 3.24.3
- Sample Code:
class Widget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Widget2();
}
}
class Widget2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
double bottomPadding = MediaQuery.viewInsetsOf(context).bottom;
return Container(
padding: EdgeInsets.only(bottom: bottomPadding),
child: Text("Hello World"),
);
}
}
2
Answers
Flutter’s approach on the reconstruction of widgets takes this form that within the body, widgets that depend on the observation or sensitiveness on ‘MediaQuery’ for example are the only widgets going for example. Let’s see how this goes in detail.
Dependency Tracking:
When a widget needs a certain property of the MediaQuery (such as in Widget2, MediaQuery.viewInsetsOf(context).bottom), Flutter records this as a MediaQuery dependency that specific child widgets will need to build based on their view configuration. The MediaQuery widget (which wraps the app) gives information about the present media state such as screen size, orientation, padding, etc. Thus, since Widget2 directly utilizes this set of information, every time the data related to MediaQuery changes, Flutter queues up Widget2 for a rebuild.
Widget-Specific Rebuilds:
Flutter optimizes the process of rebuilding the widgets. When they are changed, the systems involving the rebuild involve only the views that depend directly on the concrete inherent views closely related to those widgets being changed. As for you, since Widget2 is the only one that depended on the value MediaQuery.viewInsetsOf(context).bottom, it was the only widget that got rebuilt. Widget1 does not access or depend on the MediaQuery member, hence it is not rebuilt.
Efficient Rendering:
This is part of the rendering system of Flutter. In other words, unnecessary reconstruction of widgets is limited. Instead of propagating through the widget tree, only that part of the widget tree that needs to change is changed any way. Since Widget1 does not need to know about the bottom inset, it remains intact
How Flutter Tracks Dependencies
That’s the mechanism taken by Flutter, wherein every widget is permitted to register dependencies on specific values, such as
MediaQuery
,Theme
, or any inherited widget. Whenever any of those dependencies have a change in the value they provide, it knows which widgets depend on those values and rebuilds only the affected widgets.When a widget references something like
MediaQuery.of(context)
, it is declared as dependent on that value. When such a value changes (as when the screen size or device orientation does change), Flutter will automatically schedule that specific widget for rebuilding.Custom Dependency Tracking Example
You can use an
InheritedWidget
to do something similar in Flutter.InheritedWidget
allows widgets deeper down in your tree to depend on a given value, so when that value is updated, only the dependent widgets are recreated.Here’s a simple example of how you might do that:
Step 1: Create a custom
InheritedWidget
Step 3: Create a Widget That Depends on the Inherited Value
How This Works
InheritedWidget
:CounterInheritedWidget
is anInheritedWidget
that provides thecounter
value to its descendants. It only rebuilds its dependent widgets (likeCounterDisplay
) when the counter changes.Dependency Registration: Whenever
CounterDisplay
callsCounterInheritedWidget.of(context)
, Flutter automatically goes and registersCounterDisplay
as dependent on theCounterInheritedWidget
. This means ifcounter
changes, onlyCounterDisplay
will rebuild.Optimized Rebuilds: If widgets don’t depend on the
InheritedWidget
as in the example, theText
widget won’t rebuild whencounter
changes, so the application is more efficientConclusion
This can be done by using
InheritedWidget
and with Flutter’s built-in method ofdependOnInheritedWidgetOfExactType
. This way, the application can keep track of dependencies and only rebuild the widgets that are required to change when the values change, as it is made to do in the case ofMediaQuery
,Theme
, and other inherited widgets in Flutter.This approach helps to achieve performance optimization by not rebuilding unnecessary widgets. And this is what makes the app more responsive.