BuildContext Class

Every Flutter widget has an @override build() method with the argument of BuildContext.

  1. class CartItemWidget extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. // ...

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 thesame as the build context of the widget returned by build().

  1. class _MyHomePageState extends State<MyHomePage> {
  2. _MyHomePageState() {
  3. print(context.hashCode);
  4. // prints 2011
  5. }
  6. @override
  7. Widget build(BuildContext context) {
  8. return new Scaffold(
  9. appBar: new AppBar(
  10. title: new Text(widget.title),
  11. ),
  12. body: new Container(),
  13. floatingActionButton:
  14. new FloatingActionButton(onPressed: () => print(context.hashCode)),
  15. // prints 63
  16. );
  17. }
  18. }

If you did this exact experiment, the print statements would be different, butthose 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, toreference other Widgets. This is required for some functionality.

In particular, widgets that want to use the State of inherited widgets needto be able to reference those inherited widgets. This usually comes in the formof the of method.

For example:

  1. @override
  2. Widget build(context) {
  3. return new Text('Hello, World',
  4. style: new TextStyle(color: Theme.of(context).primaryColor),
  5. );
  6. }

Under the hood, that of method is looking up the tree for the next parentwidget that is of type Theme, and grabbing that primary color property. Theframework can find the correct Theme object because it knows the tree inrelation 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 nearestScaffold context so that Flutter knows how to paint the snackbar, sinceScaffold is the widget that actually gives us access to displaying snackbars.

Consider this:

(hint: it doesn't work)

  1. @override
  2. Widget build(BuildContext context) {
  3. return new Scaffold(
  4. appBar: new AppBar(
  5. title: new Text(widget.title),
  6. ),
  7. body: new Container(),
  8. /// Scaffold doesn't exist in this context here
  9. /// because the context thats passed into 'build'
  10. /// refers to the Widget 'above' this one in the tree,
  11. /// and the Scaffold doesn't exist above this exact build method
  12. ///
  13. /// This will throw an error:
  14. /// 'Scaffold.of() called with a context that does not contain a Scaffold.'
  15. floatingActionButton: new FloatingActionButton(onPressed: () {
  16. Scaffold.of(context).showSnackBar(
  17. new SnackBar(
  18. content: new Text('SnackBar'),
  19. ),
  20. );
  21. }));
  22. }

Builder Methods

Builder is a widget that takes a closure and uses it to build its childwidgets. In laymans, you can use it to pass the context from a build methoddirectly to children being returned in that build method.

Using the example above:

  1. @override
  2. Widget build(BuildContext context) {
  3. return new Scaffold(
  4. appBar: new AppBar(
  5. title: new Text(widget.title),
  6. ),
  7. body: new Container(),
  8. /// Builders let you pass context
  9. /// from your *current* build method
  10. /// Directly to children returned in this build method
  11. ///
  12. /// The 'builder' property accepts a callback
  13. /// which can be treated exactly as a 'build' method on any
  14. /// widget
  15. floatingActionButton: new Builder(builder: (context) {
  16. return new FloatingActionButton(onPressed: () {
  17. Scaffold.of(context).showSnackBar(
  18. new SnackBar(
  19. backgroundColor: Colors.blue,
  20. content: new Text('SnackBar'),
  21. ),
  22. );
  23. });
  24. }),
  25. );
  26. }

tip: You could also solve this by simply making your build methodssmaller, and returning a Scaffold in a 'higher' widget. When in doubt, stickwith smaller return methods.