How Does Flutter InheritedWidget Work - by Manabie Tech-Product Blog - Manabie - Medium
How Does Flutter InheritedWidget Work - by Manabie Tech-Product Blog - Manabie - Medium
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.
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.
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(
1. How does the CounterValue access the current counter value stored in the
MyInheritedWidget?
MyInheritedWidget myInheritedWidget =
context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
2. How does Flutter find the closest ancestor MyInheritedWidget instance from a
widget’s BuildContext?
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;
}
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.
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.