Skip to main content

Understanding BuildContext in Flutter

· 6 min read

If you have been learning Flutter, you have seen BuildContext everywhere. BuildContext is an important concept in Flutter most developers find hard to understand. Many of even seasoned developers don't fully understand what BuildContext is and what it does. Yet it's basic and must know for every Flutter developers. Today in this article we will try to understand what BuildContext is and why it is important. Before we understand BuildContext, we must understand widget and widget tree. So let's dive into that.

Widget Tree

In Flutter everything is a widget. Container, text, button, providers everything is virtually a widget. Also, a parent widgets can have one or more child widgets. Flutter applications consists of stack of widgets popularly known as the widget tree. In the widget tree we connect parent and child widgets to show their relationship.

If you have previously worked with frameworks like ReactJS, ViewJS or other component based frameworks, this is very similar. On frontend frameworks we have components, root component from where other components are sacked upon and each component is responsible for a unit of UI. So a ReactJS or ViewJS applications are a tree of components. In Flutter the components are rather called widgets but follows the similar principle. We have a root widget which is responsible for housing other widgets. So there is a parent-child relationship between widgets.

A widget that renders another widget is a parent widget and the widget that is rendered by the parent widget is called the child widget. So if we have a root widget called MyApp which is the default in Flutter projects (but you can change it) it becomes the root in our widget tree.

For example a tree could look like the following.

            MyApp
|
MaterialApp
|
Scaffold
|
--------------
| |
AppBar Row
|
------------------
| |
Text TextButton

Here, we have MyApp as the root widget that is parent to all the widgets in the tree. This renders MaterialApp which renders Scaffold that renders AppBar and Row. And finally Row renders Text and TextButton. We can go deeper and look into text and text button widgets but you get the point. So here we can say that MyApp is parent of MaterialApp and Scaffold is parent of both AppBar and Row widgets. Before we move on to understanding build context, keep in mind that every widget in a widget tree are in their own place. They have their own position, siblings, parent and child information. However the widget itself doesn't hold any of those information and that is where the BuildContext comes in. So let's dive into BuildContext.

BuildContext

From the API docs, BuildContext is a handle to the location of a widget in a widget tree. Every widget in a widget tree has their own unique BuildContext which they receive in their build method. This means that BuildContext of a widget is not same as the BuildContext of the widget returned by the widget from the build method. In our widget tree example above, the BuildContext of MyApp is different from that of MaterialApp and BuildContext of MaterialApp is not the same as the BuildContext of Scaffold. Each widget in the widget tree have their own unique BuildContext.

If we look into the source code of Flutter, for example into StatelessWidget, we can see a createElement method that creates an instance of StatelessElement passing this widget as parameter.

// StatelessWidget
StatelessElement createElement() => StatelessElement(this);

And if we look into StatelessElement, it passes it's own instance to the widgets build method.

class StatelessElement extends ComponentElement {
StatelessElement(StatelessWidget widget) : super(widget);


Widget build() => widget.build(this);
}

If we keep exploring more, we can see that ComponentElement extends Element which implements BuildContext class. So we can see that the element instance is what comes as BuildContext to every widget's build method.

So from this, as every widget in widget tree is created by build method, and the build method receives this BuildContext. This helps the build method to find which widget it is going to draw and to locate the position of the widget to be drawn in the widget tree.

Also, note that the widgets are only visible to their own BuildContext or to the BuildContext of their parent. That is wy, we can locate the parent widget from a child widget using findAncestorWidgetOfExactType helper method from BuildContext. This method is used to locate a widget of particular type in the widget tree. Going up the widget tree from whichever widget it was called, this method will try to locate the first widget that matches the type passed to the method.

For example, we have a tree MyApp-> Scaffold -> Row -> Container -> Row -> Text. And we call the following method from the Text widget.

context.findAncestorWidgetOfExactType<Row>()

This will traverse up the tree starting from Text widget and stopping when a widget of type Row is located. So, the Row between Text and Container will be returned. Next time you are using Navigator.of(context) or Scaffold.of(context) remember these are just helper method that use context.findAncestorWidgetOfExactType to find the nearest NavigatorState or ScaffoldState respectively.

One thing to be careful while using BuildContext is that we must always verify the context that we are using in the method is the same context that is needed. For example, using Theme.of(context) looks for nearest Theme. If a nearest widget X has a Theme in it's returned widget tree and it's calling Theme.of passing it's on context, then the Theme that is found will not be the Theme present in the build method but rather a Theme that was an ancestor to the widget X. That is why we could use a Builder widget and it's context as the context received there is the context of Builder hence we can get the Theme that is ancestor to Builder but is in the same widget X.

Conclusion

With this I hope you understand what BuildContext is and how crucial it is in order to understand how Flutter works. This deepens our knowledge of the framework which helps us to build robust applications confidently.