1. Introduction to Builder component

This article looks at a component that only has 10 lines of code: Builder. Here is the complete code. It’s very simple, but you can still learn a lot from it.

typedef WidgetBuilder = Widget Function(BuildContext context);

class Builder extends StatelessWidget {
  const Builder({
    Key key,
    @required this.builder,
  }) : assert(builder ! =null),
       super(key: key);

  final WidgetBuilder builder;

  @override
  Widget build(BuildContext context) => builder(context);
}
Copy the code

You can see that a number of components in the Flutter source code rely on the Builder component for implementation. What can this little thing do? Through this article, you will gain a fuller understanding of Flutter and even Flutter.


2. What is the value of the Builder component

It can be seen from the source code that Builder is an inherited component from Stateless, which needs to implement the Build abstract method to build the Widget object by BuildContext object. Builder# Build simply uses the Builder function passed in by the build and passes the current BuildContext as a callback. That is, you leave the logic that builds the component out to the outside world, and you just call the context object back out.

typedef WidgetBuilder = Widget Function(BuildContext context);
Copy the code

So the only value of the Builder component is to call back to the current context and let the user build the component from that context. It feels like it’s completely unscripted, so what’s the use of that? At this point, all the focus should be on BuildContext, and listen to me.


3. Learn about the BuildContext object

To get a better sense of BuildContext, let’s do some debugging. The breakpoint is as follows, MyApp is the top component of the code layer, and we can see what the context object is called back to in the build method.

It can be seen from the following debugging results:

[1]. Its type is StatelessElement, that is, it is essentially an Element. [2]. It holds a Widget object, _widget, of type MyApp, the current component. [3]. It has a father _parent properties, type of RenderOnjectToWidgetElement. [4]. It has a child _child attribute, which is null.

The Element tree is actually right here. The root element is RenderOnjectToWidgetElement type object, its parent is null, and hold MyApp StatelessElement here is the second element.


BuildContext is an abstract class, which means it cannot construct objects directly. On the Flutter framework layer, it has only one implementation class, —- Element, so the relationship between the two should be clear. In the use of Flutter, every BuildContext object you see is essentially an Element object. To put it more figuratively, Element is like an engineer’s TV, and BuildContext is like a normal user’s TV. Engineers need to complete the internal functions and logic of this object; For users, they just need to press the switch and click the button. They don’t need to care about the implementation details. They just need to have the abstract cognition of the TV. It can be said that the TV in users’ cognition is not the real TV, but the abstract function of the TV. We just know what it can do. All the displays and buttons are the functional interfaces provided by the inside. For Element objects, BuildContext is the functional interface exposed to the user. The users are us who use the Flutter framework. We don’t need to know what’s going on inside the TV (Element), we just need to know how to use it (BuildContext). That’s why BuildContext is abstracted instead of using Element directly.


4. What BuildContext does

So what’s the use of BuildContext? Here’s all of BuildContext’s information. You can see that all but four of its member variables are abstract methods. It is important to note that BuildContext does not have a tree structure, which means that it is just an abstraction. The internal structure and logic are left entirely to the implementation classes, and the abstraction is only responsible for exposing the interface functionality needed by the user. There are a lot of ways to do it, and if you take a quick peek, you can see that they’re basically looking for things.

We will often use navigator.of (context).push to jump to routes. Navigator.of(context) is a static method that returns NavigatorState, and the routing methods are defined in NavigatorState. The purpose of BuildContext here is to get the related state class XXXState. The core method is findAncestorStateOfType, which gets the State object corresponding to the first upper-level component of a type.

static NavigatorState of(
  BuildContext context, {
  bool rootNavigator = false, {})// Handles the case where the input context is a navigator element.
  NavigatorState? navigator;
  if (context is StatefulElement && context.state is NavigatorState) {
      navigator = context.state as NavigatorState;
  }
  if (rootNavigator) {
    navigator = context.findRootAncestorStateOfType<NavigatorState>() ?? navigator;
  } else {
    navigator = navigator ?? context.findAncestorStateOfType<NavigatorState>();
  }
  returnnavigator! ; }Copy the code

There is also an InheritedWidget like MediaQuery, which uses.of(context) to get specific data. For example, mediaQuery.of (context) can retrieve MediaQueryData to retrieve screen size, device resolution, etc. The core method BuildContext dependOnInheritedWidgetOfExactType here.

static MediaQueryData of(BuildContext context) {
  assert(context ! =null);
  assert(debugCheckHasMediaQuery(context));
  returncontext.dependOnInheritedWidgetOfExactType<MediaQuery>()! .data; }Copy the code

BuildContext can get specific objects from all of its parent nodes, and that’s enough for this article. We’ll write a section on BuildContext that discusses these interfaces in more detail.


5. The role of the Builder

Now, after BuildContext, the current context only has the root node, so using this context to do MediaQuery will get an error, because MediaQuery is contained within the MaterialApp, You can’t find the context, so to use MediaQuery, you have to move the context down after the MaterialApp build. The simplest solution is to pull the context down by pulling the component away. If you don’t pull the component away, then the Builder component is powerful.

It is important to note that the context of the callback in the Builder is not a direct child of the context, that is, it is not just moved down one level. This depends on the complexity of the component. For example, the internal implementation of MaterialApp is complicated. It can be seen that the CTX depth of the callback in Builder is 87, indicating that there are so many parent nodes on it.

So, the tree that you think is in a Flutter is completely different from the tree that is in a Flutter. At this time, some people with ulterior motives began to say: “Look, a MaterialApp component is so complex, its performance is worried, Flutter is too nested hell, it is terrible, it will get cold sooner or later”. I just want to say, don’t compare your brain to a computer, even if there are thousands of element nodes, in a tree structure, forming a tree or finding an element in the tree is just a matter of the imagination. This is the equivalent of me opening the shell of the TV and giving you a glimpse of what’s inside. Don’t use your ignorance to marvel at the complexity of the world.

Scroll down and you’ll see an element holding MediaQuery, which means that the current CTX can be traced back to the ancestor node. Mediaquery.of (CTX) can be used to retrieve media information in the context of the Builder callback.

At this point, you should have some sense of the hierarchy of context. Theme. Of, scaffold. of, navigator. of, Provider. Of, Bloc. Of. This is what all Builder components do, calling back a lower level context for use. This is essentially the same as extracting a Widget. If it’s simple and you don’t want to come up with a component to handle it, a Builder is a great way to do it. Now you know what to do when you don’t get what you want through xxx.of (context).

For example, the Scaffold under BuilderDemo# Build is intended to eject SnackBar when floatingActionButton is clicked. ShowSnackBar is triggered by ScaffoldState. The current context does not yet have a corresponding element on top of it. There are two options: 1. Pull out the component and trigger in the context of the underlying component. 2. Use Builder to call back the underlying context. Since there is little to do, there is no need to create a new component, and using Builder is easy and convenient.

import 'package:flutter/material.dart';
class BuilderDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 200,
      child: Scaffold(
        appBar: AppBar(
          title: Text('Builder'),
        ),
        floatingActionButton: Builder(
          builder: (ctx) => FloatingActionButton(
            onPressed: () {
              Scaffold.of(ctx).showSnackBar(SnackBar(content: Text('hello builder'))); }, child: Icon(Icons.add), ), ), ), ); }}Copy the code

6. The application of Builder component in the source code

In case you haven’t noticed, the MaterialApp itself has a Builder property. WidgetsApp is used internally within the MaterialApp

You can see in _WidgetsAppState#build that the Builder component is used if the Builder property is not empty. When the MaterialApp component is used, it can achieve the same effect as the Builder component through the Builder attribute, but it is also the credit of the Builder component to pursue its essence.


The Merge method in IconTheme also uses the Builder component. This is to get the context when there is no context, so there is no need to pass the context in the merge method, which is also a way to use the context out of nothing. The same usage can also be seen in the source Text. Merge, listtile. merge.


In the Provider class, you can also see a Builder property of type TransitionBuilder. In fact, their function is also given by builder, and their function is self-evident. You’ll feel a real kinship.

Is the Builder component itself difficult? A 10-line source code component is certainly not hard. What is hard is to think about its value for existence, and your interest and ability to discover something deeper. By reading this article, you should be able to add a little new knowledge about Flutter. That concludes this article. Thank you for watching.


@Zhang Fengjietele 2021.01.02 not allowed to transfer my public number: the king of programming contact me – email :[email protected] – wechat :zdl1994328 ~ END ~