0% found this document useful (0 votes)
68 views10 pages

How Does Flutter InheritedWidget Work - by Manabie Tech-Product Blog - Manabie - Medium

InheritedWidgets allow passing data from ancestor widgets to descendant widgets deep in the widget tree. The data is passed by building a map of ancestor InheritedWidget instances in each widget's BuildContext. When an InheritedWidget rebuilds, it loops through its dependent widgets and rebuilds those where updateShouldNotify returns true. In the example, CounterValue is rebuilt when the counter changes, while DummyContainer is not as updateShouldNotify returns false.

Uploaded by

s. k. hui
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
68 views10 pages

How Does Flutter InheritedWidget Work - by Manabie Tech-Product Blog - Manabie - Medium

InheritedWidgets allow passing data from ancestor widgets to descendant widgets deep in the widget tree. The data is passed by building a map of ancestor InheritedWidget instances in each widget's BuildContext. When an InheritedWidget rebuilds, it loops through its dependent widgets and rebuilds those where updateShouldNotify returns true. In the example, CounterValue is rebuilt when the counter changes, while DummyContainer is not as updateShouldNotify returns false.

Uploaded by

s. k. hui
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

How does Flutter InheritedWidget work?

Manabie Tech-Product Blog


Jan 10, 2020 · 3 min read

I. What is InheritedWidget for?

To pass data from an ancestor widget to descendant ones, which are possibly deep
down the widget tree.

InheritedWidget is immutable and its attributes are final; therefore, Flutter needs to
rebuild InheritedWidget if we want to refresh it with new attributes.

II. How does InheritedWidget work?

Let’s figure out how Flutter InheritedWidget works by using the example code below.
Figure 1: The UI of the example code
Figure 2: The widget tree of the example code

1. The MyContainer widget can be clicked to increase the counter value in the
CounterValue by 1.

2. The CounterLabel widget is a sibling of the CounterValue widget.

3. The CounterValue should be automatically refreshed with a new counter value


whenever the MyContainer is clicked, but the CounterLabel & the
DummyContainer should not be rebuilt.

1 import 'package:flutter/material.dart';
2
3 void main() => runApp(MyApp());
4
5 class MyApp extends StatelessWidget {
6 // This widget is the root of your application.
7 @override
8 Widget build(BuildContext context) {
9 return MaterialApp(
10 title: 'InheritedWidget playground',
11 theme: ThemeData(
12 primarySwatch: Colors.blue,
13 ),
14 home: Scaffold(
15 body: MyStatefulWidget(
16 child: MyContainer(
17 child: DummyContainer(
18 child: Column(
19 crossAxisAlignment: CrossAxisAlignment.center,
20 mainAxisAlignment: MainAxisAlignment.center,
21 children: <Widget>[
22 CounterLabel(),
23 CounterValue(),
24 ],
25 ),
26 ),
27 ),
28 ),
29 ),
30 );
31 }
32 }
33
34 class MyStatefulWidget extends StatefulWidget {
35 final Widget child;
36
37 const MyStatefulWidget({Key key, @required this.child}) : super(key: key);
38
39 static MyStatefulWidgetState of(BuildContext context) {
40 return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>().data;
41 }
42
43 @override
44 State<StatefulWidget> createState() {
45 return MyStatefulWidgetState();
46 }
47 }
48
49 class MyStatefulWidgetState extends State<MyStatefulWidget> {
50 int _counterValue = 0;
_ ;
51
52 int get counterValue => _counterValue;
53
54 void addCounterBy1() {
55 setState(() {
56 _counterValue += 1;
57 });
58 }
59
60 @override
61 Widget build(BuildContext context) {
62 return MyInheritedWidget(
63 child: widget.child,
64 data: this,
65 );
66 }
67 }
68
69 class MyInheritedWidget extends InheritedWidget {
70 final MyStatefulWidgetState data;
71
72 MyInheritedWidget({
73 Key key,
74 @required Widget child,
75 @required this.data,
76 }) : super(key: key, child: child);
77
78 @override
79 bool updateShouldNotify(InheritedWidget oldWidget) {
80 return true;
81 }
82 }
83
84 class MyContainer extends StatelessWidget {
85 final Widget child;
86
87 MyContainer({
88 Key key,
89 @required this.child,
90 }) : super(key: key);
91
92 void onPressed(BuildContext context) {
93 MyStatefulWidget.of(context).addCounterBy1();
94 }
95
96 @override
97 Widget build(BuildContext context) {
(
98 return Center(
99 child: Container(
100 width: 200,
101 height: 200,
102 child: RaisedButton(
103 color: Colors.red,
104 onPressed: (){
105 onPressed(context);
106 },
107 child: child,
108 ),
109 ),
110 );
111 }
112 }
113
114 class DummyContainer extends StatelessWidget {
115 final Widget child;
116
117 const DummyContainer({Key key, this.child}) : super(key: key);
118
119 @override
120 Widget build(BuildContext context) {
121 return child;
122 }
123 }
124
125 class CounterLabel extends StatelessWidget {
126 @override
127 Widget build(BuildContext context) {
128 return Text(
129 "Counter",
130 style: TextStyle(
131 color: Colors.white,
132 fontSize: 20,
133 ),
134 );
135 }
136 }
137
138 class CounterValue extends StatefulWidget {
139 @override
140 State<StatefulWidget> createState() {
141 return CounterValueState();
142 }
143 }
144
145 class CounterValueState extends State<CounterValue> {
146 int counterValue;
147 double fontSize;
148
149 @override
150 void didChangeDependencies() {
151 super.didChangeDependencies();
152 MyStatefulWidgetState data = MyStatefulWidget.of(context);
153 counterValue = data.counterValue;
154 fontSize = 50.0 + counterValue;
155 }
156
157 @override
158 Widget build(BuildContext context) {
159 return Text(
160 "$counterValue",
161 style: TextStyle(

Snippet 1: The example code

1. How does the CounterValue access the current counter value stored in the
MyInheritedWidget?

MyInheritedWidget myInheritedWidget =
context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();

The dependOnInheritedWidgetOfExactType method enables a descendant widget to


access the closest ancestor MyInheritedWidget instance enclosed in its BuildContext.

2. How does Flutter find the closest ancestor MyInheritedWidget instance from a
widget’s BuildContext?

T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>


({Object aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null
: _inheritedWidgets[T];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
Every widget keeps a map _inheritedWidgets, which stores all ancestor InheritedWidget
instances indexed by their type.

3. How is this _inheritedWidgets map constructed for each widget’s BuildContext


in a widget tree?

When an element is added to the widget tree, Flutter calls the mount method, which
then invokes the _updateInheritance method, which copies the _inheritedWidgets map
from the parent.

void _updateInheritance() {
assert(_active);
_inheritedWidgets = _parent?._inheritedWidgets;
}

The InheritedElement class overrides _updateInheritance to copy the _inheritedWidgets


from the parent and then assigns itself to _inheritedWidgets with the key which is its
runtime type.

void _updateInheritance() {
assert(_active);
final Map<Type, InheritedElement> incomingWidgets =
_parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type,
InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
_inheritedWidgets[widget.runtimeType] = this;
}

By running two above code snippets recursively when mounting a widget tree, every
widget’s _inheritedWidgets stores all ancestor InheritedWidget instances.

4. In the above example, when the instance of MyInheritedWidget is rebuilt, the


MyContainer and the CounterValue instances are rebuilt while the
DummyContainer and CounterValue instances are not rebuilt. How does it work?

The body of the method dependOnInheritedWidgetOfExactType calls


dependOnInheritedElement.
InheritedWidget dependOnInheritedElement(InheritedElement ancestor,
{ Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}

The line, ancestor.updateDependencies(this, aspect), registers MyContainer and


CounterValue as MyInheritedWidget’s dependencies. When the MyInheritedWidget is
rebuilt, Flutter loops through MyInheritedWidget’s dependencies and decides whether
to rebuild these registered widgets by calling the updateShouldNotify method. If the
method returns true, Flutter rebuilds all registered widgets. Otherwise, Flutter does
not.

void notifyClients(InheritedWidget oldWidget) {


assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies.contains(this));
notifyDependent(oldWidget, dependent);
}
}

void notifyDependent(covariant InheritedWidget oldWidget, Element


dependent) {
dependent.didChangeDependencies();
}

By calling didChangeDependencies on a dependent, the dependent is rebuilt.

The example code can be found at this GitHub link. If we miss out any important
information or some details are incorrect, please help to comment.

Flutter Inheritedwidget Edtech Technology Engineering


About Write Help Legal

Get the Medium app

You might also like