BuildContext
on Friday, 24th of July, 2020
Every Flutter widget has an @override build()
method with the argument of BuildContext
.
class CartItemWidget extends StatelessWidget {
Widget build(BuildContext context) {
// ...
why BuildContext()
Simply explain is that the BuildContext
is:
- the location of the
Widget
in the widget tree. - a widget of widgets, like nested.wrap
<div <div> .html>
- parent objects in qt and alike
- In Flutter, everything is a Widget, down to the final
build.call()
- Until finally a widget is returned with its "stuff," row dimentions et all
Important concept to understand is that
1 - Every Widget
has its own build()
and its context
.
2 - The BuildContext is the parent of the widget returned by the build()
method._
In other words, the buildContext
of the Widget that calls build()
is not the
same as the build context of the widget returned by build()
.
class _MyHomePageState extends State<MyHomePage> {
_MyHomePageState() {
print(context.hashCode);
// prints 2011
}
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(),
floatingActionButton:
new FloatingActionButton(onPressed: () => print(context.hashCode)),
// prints 63
);
}
}
If you did this exact experiment, the print statements would be different, but those are the actual numbers that I got.
But what does this actually mean?
This is the one big gotcha;-):
- It is easy to reference the wrong
build()
- and its
context
- This can cause unexpected situations, specifically when using the
of()
method.
The 'of()' Method
In Flutter, as everthing are widgets, looking up and down the widget tree, in some cases, to reference other Widgets. This is required for some functionality.
In particular, widgets that want to use the State of inherited
widgets need
to be able to reference those inherited widgets. This usually comes in the form
of the of
method.
For example:
build(context) {
return new Text('Hello, World',
style: new TextStyle(color: Theme.of(context).primaryColor),
);
}
Widget
Under the hood, that of
method is looking up the tree for the next parent
widget that is of type Theme
, and grabbing that primary color property. The
framework can find the correct Theme
object because it knows the tree in
relation to this build context
.
The Gotcha
With the scaffold, Flutter does give us a nice way to solve following problem...
When creating some widgets, such as a snackbar, you have to grab the nearest Scaffold context so that Flutter knows how to paint the snackbar, since Scaffold is the widget that actually gives us access to displaying snackbars.
Consider this:
(hint: it doesn't work)
build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(),
/// Scaffold doesn't exist in this context here
/// because the context thats passed into 'build'
/// refers to the Widget 'above' this one in the tree,
/// and the Scaffold doesn't exist above this exact build method
///
/// This will throw an error:
/// 'Scaffold.of() called with a context that does not contain a Scaffold.'
floatingActionButton: new FloatingActionButton(onPressed: () {
Scaffold.of(context).showSnackBar(
new SnackBar(
content: new Text('SnackBar'),
),
);
}));
}
Widget
Builder Methods
Builder
is a widget that takes a closure and uses it to build its child
widgets. In laymans, you can use it to pass the context from a build
method
directly to children being returned in that build
method.
Using the example above:
build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(),
/// Builders let you pass context
/// from your *current* build method
/// Directly to children returned in this build method
///
/// The 'builder' property accepts a callback
/// which can be treated exactly as a 'build' method on any
/// widget
floatingActionButton: new Builder(builder: (context) {
return new FloatingActionButton(onPressed: () {
Scaffold.of(context).showSnackBar(
new SnackBar(
backgroundColor: Colors.blue,
content: new Text('SnackBar'),
),
);
});
}),
);
}
Widget
You could also solve this by simply making your build methods smaller, and returning a Scaffold in a 'higher' widget. When in doubt, stick with smaller return methods.
- next: The Widget tree