Introduction
We know that there are two kinds of widgets in Flutter, namely StatelessWidget and StatefulWidget. There is a build method in StatelessWidget to create the corresponding widget. Although there is no corresponding build method in StatefulWidget, there is also the same build method in State corresponding to StatefulWidget.
This build method is the core method used to create a Widget.
Let's look at the definition of the build method:
Widget build(BuildContext context);
The build method passes in a BuildContext object and returns a Widget object, which means that this BuildContext contains all the information of the Widget to be created. This BuildContext is called the Widget's contextual build environment.
So what are the features of BuildContext? How can we use BuildContext? Let's take a look together.
The essence of BuildContext
Remember the three trees in flutter?
They are Widgets tree, Element tree and Render tree respectively. The Widgets tree and the Element tree are in one-to-one correspondence. And the RenderObjectElement in the Render tree and Element has a one-to-one correspondence.
In fact BuildContext is an Element object. how to say?
Let's first look at the definition of BuildContext:
abstract class BuildContext {
Widget get widget;
...
}
BuildContext is an abstract class, let's look at the definition of the Element class again:
abstract class Element extends DiagnosticableTree implements BuildContext {
As you can see, the Element object implements the BuildContext interface, and each BuildContext has a Widget object bound to it.
After a complex relationship transfer operation, we can know that the Element object and the Widget object are indeed in a one-to-one correspondence at the code level.
BuildContext and InheritedWidget
InheritedWidget is a widget used to pass down change information in the tree. In the child node of the tree, you can find the nearest parent InheritedWidget in the child node by calling BuildContext.dependOnInheritedWidgetOfExactType, so as to bind the current BuildContext to the widget and InheritedWidget Establish a binding relationship, so that the next time the InheritedWidget changes, the rebuild method of the widget bound by the BuildContext will be automatically triggered.
It sounds complicated, but it's actually very simple. Let's take an example. First, we need to define a Widget to inherit InheritedWidget:
class FrogColor extends InheritedWidget {
const FrogColor({
Key? key,
required this.color,
required Widget child,
}) : super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
final FrogColor? result = context.dependOnInheritedWidgetOfExactType<FrogColor>();
assert(result != null, 'No FrogColor found in context');
return result!;
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
In this method, we need to define a of method. In this method, we call the context.dependOnInheritedWidgetOfExactType method to find the FrogColor closest to the BuildContext.
Then it can be used like this:
class MyPage extends StatelessWidget {
const MyPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: FrogColor(
color: Colors.green,
child: Builder(
builder: (BuildContext innerContext) {
return Text(
'Hello Frog',
style: TextStyle(color: FrogColor.of(innerContext).color),
);
},
),
),
);
}
}
Our original intention is to hope that the style of the Text component in the child will change according to the color of the FrogColor in the parent widget. Therefore, the FrogColor.of(innerContext) method is called in the style of the subcomponent to search for the InheritedWidget and establish a binding relationship at the same time.
In BuildContext, there are two methods for finding and binding, they are:
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
The difference between the two is that the latter defines the type of lookup.
In addition to dependOn, BuildContext also provides two search methods:
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
T? findAncestorWidgetOfExactType<T extends Widget>();
T? findAncestorStateOfType<T extends State>();
T? findRootAncestorStateOfType<T extends State>();
T? findAncestorRenderObjectOfType<T extends RenderObject>();
The difference between them and depend is that they do not build dependencies, but simply search.
BuildContext hierarchy
Because each widget has a BuildContext, we must pay attention to which widget the incoming BuildContext is bound to when using it.
As shown in the code below:
class MyPage extends StatelessWidget {
const MyPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: FrogColor(
color: Colors.green,
child: Builder(
builder: (BuildContext innerContext) {
return Text(
'Hello Frog',
style: TextStyle(color: FrogColor.of(innerContext).color),
);
},
),
),
);
}
}
In the child of FrogColor, we create a new Builder and provide a new innerContext.
Why do you want to do this? Because if we don't create a sub-innnerContext, the context used is Scaffold, so FrogColor.of will not find the object it is looking for and report an error.
So when we use BuildContext, we must pay attention.
Summarize
BuildContext is the basis for building Widgets. It also provides some very useful search and binding functions. I hope it can be helpful to everyone.
For more information, please refer to http://www.flydean.com/04-flutter-buildcontext/
The most popular interpretation, the most profound dry goods, the most concise tutorials, and many tricks you don't know are waiting for you to discover!
Welcome to pay attention to my official account: "Program those things", understand technology, understand you better!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。