88

The documentation is very confusing and vague. Here is what it states:

Builder class

A platonic widget that calls a closure to obtain its child widget.

Here are my questions:

  1. What do they mean by "platonic"?
  2. What do they mean by "closure"?
  3. What exactly is the purpose of this class?
Walter M
  • 4,993
  • 5
  • 22
  • 29

3 Answers3

215

After long hours of extensive hair-pulling research on the internet, I collected small snippets and combined them to put a cohesive and clear explanation of what the Builder Class does.

Terminology:

According to the official flutter documentation, the Builder Class is defined as:

A platonic widget that calls a closure to obtain its child widget.

Platonic means the simplest possible thing of that kind. The term closure is just another name for a lambda function.

Purpose:

This is going to be a lengthy explanation, but please bare with me:

In the Flutter framework, every widget has a build method that accepts a BuildContext parameter:

Widget build ( BuildContext context ) { ... }

We have to remember that the context object is passed to the widget's build function automatically by the framework. Since the framework takes care of that automatically, there is no reason for any widget to have a constructor or function (aside from build) that would need to accept a context parameter.

Hence, if you were trying to pass a specific context object to a child, you won't be able to. You cannot call build() and pass your own context object manually. I mean, you can, but you would be calling the build function twice:

  1. Your manual call.
  2. The automatic call by the framework.

So, how can we pass a specific context object? This is where the Builder class comes in. The purpose of the Builder class is simply to build and return child widgets. How is that different from any other widget? Aha! The Builder class allows you to pass a specific context object down to its children. The Builder class is basically your own build function that you setup.

Why would I need to pass a specific context object? Lets take a look at an example:

Lets say that we want to add a new SnackBar widget to its new Scaffold parent widget that is being returned:

 @override
  Widget 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'),
                ),
              );
        }));
  }

This code above does not work. The Scaffold.of(context) function will not find the Scaffold because:

  1. The Scaffold widget has not been created yet.
  2. The context object that was passed to the build function refers to the parent widget, which is not a Scaffold widget.

So, how do we give the child SnackBar widget access to the parent Scaffold widget? We use a Builder class to pass the context of the Scaffold widget:

 @override
  Widget 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: (BuildContext context) {
        return new FloatingActionButton(onPressed: () {
          Scaffold.of(context).showSnackBar(
                new SnackBar(
                  backgroundColor: Colors.blue,
                  content: new Text('SnackBar'),
                ),
              );
        });
      }),
    );
  }

Remember, the Builder class constructor:

Builder({Key key, @required WidgetBuilder builder })

creates a widget by delegating its build to the callback function passed through its constructor.

So, in the code:

new Builder(builder: (BuildContext context){ ... });

we provided a closure that:

  1. Contains a BuildContext context parameter
  2. Builds and returns child widget(s) based on that context passed.

Basically, you provided your own build function. The BuildContext context parameter in this closure is the Scaffold's context! Baboom!

That is basically it. The Flutter documentation does not provide a thorough explanation of this at all. I feel like I would have an easier time deciphering ancient hieroglyphs than decoding the Flutter documentation.

SUMMARY: For anyone still having a hard time grasping this concept, let me explain in a more concise form. The Builder function simply allows you to attain and use the context object of the widget that the Builder widget is in. In the example above, it is the new Scaffold() widget. Remember, the only context object available to use is that of the parent widget (above Scaffold) since the current widget (Scaffold) has not been created yet. I hope that helps those were still scratching their heads.

Walter M
  • 4,993
  • 5
  • 22
  • 29
  • 1
    Hi, I think this sentence may not be correct `The context object that was passed to the build function refers to the parent widget, which is not a Scaffold widget.`. I think the context should refer to the current widget instead of the parent widget. – sgon00 May 24 '19 at 18:27
  • 1
    Or in short, Builder is used to creating something like ’inline’ widget that you might not want to create a separate class for it. – Thành Chung Bùi Jul 18 '19 at 23:31
  • 18
    Thank you for doing the research and posting such a clear explanation. This is super helpful! All Flutter documents about the Builder is "A platonic widget that calls a closure to obtain its child widget." Near useless! – Ray Li Jul 31 '19 at 16:01
  • So there it goes my understanding of BuildContext.. I think i thought it would be something like one for the application lol.. – Daniel Vilela Nov 04 '19 at 17:47
  • Well done buddy! Thanks a lot, I was going crazy :) – daveoncode Feb 11 '20 at 09:35
  • @Walter M : I am too scratched became bald then hit your answer LoL :-). Amazing effort buddy, really appreciate it. Two beers from me to you :-). – Priyanka Mishra May 15 '21 at 16:25
  • Just realized how scary the word "platonic" is after this read. – John Wang Jun 03 '21 at 10:17
  • 3
    I think the choice of words here might be misleading. I wouldn't say you use it to "pass a specific context", that might be understood as "any context you want", when in fact it's controlled by the framework and always one context deeper than what's around it. – Quacke Jun 29 '21 at 21:12
  • Deciphering ancient hieroglyphs than decoding the Flutter documentation. LOL – Davoud Aug 30 '21 at 13:25
28

It basically converts a function that builds a widget into a widget.

Wo where you need to pass a widget but only have a function that returns a widget, you can use the Builder widget.

bool bar;

Widget createFooOrBarWidget() {
  if(bar) {
    return BarWidget();
  } 
  return FooWidget();
}

Widget build(BuildContext context) =>
  Container(child: Builder((context) => createFooOrBarWidget()));

you could also use

Widget build(BuildContext context) =>
  Container(child: createFooOrBarWidget());

but the former delays the creation of the Foo or Bar widget until build is actually called.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Hmm.. I think I have it. Can you confirm a fact for me: does every widget have a build method? – Walter M Aug 31 '18 at 03:57
  • Yes, there can't be a widget without `build`. Flutter calls `build` on each widget when it (re)builds the view. – Günter Zöchbauer Aug 31 '18 at 04:08
  • 2
    Okay. With that confirmed, I will post an answer for my own question based upon your info and the 23 hour research I did to find a clear explanation of the Builder class. Give me a few minutes while I post my answer and see if I got it right. – Walter M Aug 31 '18 at 04:19
  • Sorry for the delay Gunter. I posted my answer. Can you double check for me. – Walter M Aug 31 '18 at 22:06
  • Can you please elaborate on "but the former delays the creation of the Foo or Bar widget until build is actually called." a little bit more? I loved the 2 snippets and want to learn exactly how they differ. – aytunch Aug 17 '20 at 09:28
  • Actually I can't make sense of it myself. I just started with Flutter when I aposted this answer. – Günter Zöchbauer Aug 17 '20 at 17:24
10

Simple Definition

This Builder widget is, like its name is implying; used to create a child widget with a "new context".

Technical Definition

This Builder has a builder property and this property accept WidgetBuilder typedef (WidgetBuilder typedef is a Signature for a function that creates(return) a widget with new context)

If you want to know about that WidgetBuilder typedef please use this link ➡https://api.flutter.dev/flutter/widgets/WidgetBuilder.html

usage:


1. when Scaffold widget and Scaffold.of method are in same build method.

[ At that time scaffold.of method cannot find closet Scaffold widget ,because both are in same context ,by creating new context inside build method you can solve this, that why we use Builder Widget to create widget with New BuildContext. ]

Below code shows real usage of Builder widget ,when you want use Scaffold.of and Scaffold widget in same build method

(Look these comments carefully-it will help you to understand the context)

Widget build(BuildContext context) {                       // context - 1
  return Scaffold(
    appBar: AppBar(
      title: Text('Demo')
    ),
    body: Builder(
      // Create an inner BuildContext so that the onPressed methods
      // can refer to the Scaffold with Scaffold.of().
      builder: (BuildContext context) {                    // context - 2
        return Center(
          child: RaisedButton(
            child: Text('SHOW A SNACKBAR'),
            onPressed: () {
              Scaffold.of(context).showSnackBar(SnackBar(  // here context is (context- 2)
                content: Text('Have a snack!'),
              ));
            },
          ),
        );
      },
    ),
  );
}

2. When Theme.of method and Theme widget in a same build method.

[here also purpose same as above 1]

Below code shows real usage of Builder widget ,when you want use Theme.of and Theme widget in same build method

@override
Widget build(BuildContext context) {
  return MaterialApp(
    theme: ThemeData.light(),
    body: Builder(
      // Create an inner BuildContext so that we can refer to
      // the Theme with Theme.of().
      builder: (BuildContext context) {
        return Center(
          child: Text(
            'Example',
            style: Theme.of(context).textTheme.title,
          ),
        );
      },
    ),
  );
}


EXTRA POINT

buider property (WidgetBuilder typedef) we can see on many instances

Down below that code part shows, 'how MaterialPageRoute ' use builder property to get widget for that route

Navigator.push(context, MaterialPageRoute<void>(
  builder: (BuildContext context) {                 //here
    return Scaffold(
      appBar: AppBar(title: Text('My Page')),
      body: Center(
        child: FlatButton(
          child: Text('POP'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  },
));
danronmoon
  • 3,814
  • 5
  • 34
  • 56
dilshan
  • 2,045
  • 20
  • 18