build method

  1. @override
Widget build(
  1. BuildContext context
)
override

Describes the part of the user interface represented by this widget.

The framework calls this method in a number of different situations. For example:

This method can potentially be called in every frame and should not have any side effects beyond building a widget.

The framework replaces the subtree below this widget with the widget returned by this method, either by updating the existing subtree or by removing the subtree and inflating a new subtree, depending on whether the widget returned by this method can update the root of the existing subtree, as determined by calling Widget.canUpdate.

Typically implementations return a newly created constellation of widgets that are configured with information from this widget's constructor, the given BuildContext, and the internal state of this State object.

The given BuildContext contains information about the location in the tree at which this widget is being built. For example, the context provides the set of inherited widgets for this location in the tree. The BuildContext argument is always the same as the context property of this State object and will remain the same for the lifetime of this object. The BuildContext argument is provided redundantly here so that this method matches the signature for a WidgetBuilder.

Design discussion

Why is the build method on State, and not StatefulWidget?

Putting a Widget build(BuildContext context) method on State rather than putting a Widget build(BuildContext context, State state) method on StatefulWidget gives developers more flexibility when subclassing StatefulWidget.

For example, AnimatedWidget is a subclass of StatefulWidget that introduces an abstract Widget build(BuildContext context) method for its subclasses to implement. If StatefulWidget already had a build method that took a State argument, AnimatedWidget would be forced to provide its State object to subclasses even though its State object is an internal implementation detail of AnimatedWidget.

Conceptually, StatelessWidget could also be implemented as a subclass of StatefulWidget in a similar manner. If the build method were on StatefulWidget rather than State, that would not be possible anymore.

Putting the build function on State rather than StatefulWidget also helps avoid a category of bugs related to closures implicitly capturing this. If you defined a closure in a build function on a StatefulWidget, that closure would implicitly capture this, which is the current widget instance, and would have the (immutable) fields of that instance in scope:

// (this is not valid Flutter code)
class MyButton extends StatefulWidgetX {
  MyButton({super.key, required this.color});

  final Color color;

  @override
  Widget build(BuildContext context, State state) {
    return SpecialWidget(
      handler: () { print('color: $color'); },
    );
  }
}

For example, suppose the parent builds MyButton with color being blue, the $color in the print function refers to blue, as expected. Now, suppose the parent rebuilds MyButton with green. The closure created by the first build still implicitly refers to the original widget and the $color still prints blue even through the widget has been updated to green; should that closure outlive its widget, it would print outdated information.

In contrast, with the build function on the State object, closures created during build implicitly capture the State instance instead of the widget instance:

class MyButton extends StatefulWidget {
  const MyButton({super.key, this.color = Colors.teal});

  final Color color;
  // ...
}

class MyButtonState extends State<MyButton> {
  // ...
  @override
  Widget build(BuildContext context) {
    return SpecialWidget(
      handler: () { print('color: ${widget.color}'); },
    );
  }
}

Now when the parent rebuilds MyButton with green, the closure created by the first build still refers to State object, which is preserved across rebuilds, but the framework has updated that State object's widget property to refer to the new MyButton instance and ${widget.color} prints green, as expected.

See also:

  • StatefulWidget, which contains the discussion on performance considerations.

Implementation

@override
Widget build(BuildContext context) {
  widget._state = this;
  //Set Map Size
  if (widget.getParentNode() == null &&
      (!widget.getSelected() || !widget._focusNode.hasFocus)) {
    WidgetsBinding.instance.addPostFrameCallback((c) {
      if (mounted && !widget.getSelected()) {
        RenderObject? ro = context.findRenderObject();
        if (ro != null && ro is RenderBox) {
          widget.getMindMap()?.setSize(ro.size);
        }
      }
    });
  }
  List<Widget> leftItems = [];
  if (widget.getNodeType() == NodeType.root ||
      widget.getNodeType() == NodeType.left) {
    for (IMindMapNode item in widget.getLeftItems()) {
      leftItems.add(item as Widget);
    }
  }
  List<Widget> rightItems = [];
  if (widget.getNodeType() == NodeType.root ||
      widget.getNodeType() == NodeType.right) {
    for (IMindMapNode item in widget.getRightItems()) {
      rightItems.add(item as Widget);
    }
  }
  return CustomPaint(
    painter: widget.getOffset() == null
        ? null
        : widget.getLink().getPainter(widget),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisSize: MainAxisSize.min,
      //spacing: widget.getHSpace().toDouble(),
      children: [
        //Left Items
        ...(leftItems.isEmpty || !widget.getExpanded()
            ? []
            : [
                Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.end,
                  mainAxisSize: MainAxisSize.min,
                  spacing: widget.getVSpace().toDouble(),
                  children: leftItems,
                ),
              ]),
        //LeftSpace
        ...(widget.getNodeType() != NodeType.right
            ? [
                Container(
                  constraints: BoxConstraints(
                    minWidth: widget.getHSpace().toDouble(),
                    maxWidth: widget.getHSpace().toDouble(),
                    minHeight: widget.getMindMap()?.buttonWidth ?? 16,
                  ),
                  padding: EdgeInsets.fromLTRB(3, 0, 3, 0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.end,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      ...(widget.getSelected()
                          ? (widget.getReadOnly()
                                ? (widget.getNodeType() == NodeType.left &&
                                          widget.canExpand() &&
                                          widget.getLeftItems().isNotEmpty
                                      ? [
                                          //Left Expand Button
                                          Container(
                                            constraints: BoxConstraints(
                                              maxHeight:
                                                  (widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16) +
                                                  widget
                                                          .getLinkOutOffset()
                                                          .abs() *
                                                      2,
                                            ),
                                            padding: EdgeInsets.fromLTRB(
                                              0,
                                              widget.getLinkOutOffset() > 0
                                                  ? widget.getLinkOutOffset() *
                                                        2
                                                  : 0,
                                              0,
                                              widget.getLinkOutOffset() < 0
                                                  ? widget
                                                            .getLinkOutOffset()
                                                            .abs() *
                                                        2
                                                  : 0,
                                            ),
                                            child: Container(
                                              constraints: BoxConstraints(
                                                maxWidth:
                                                    widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16,
                                                maxHeight:
                                                    widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16,
                                              ),
                                              decoration: BoxDecoration(
                                                color:
                                                    widget
                                                        .getMindMap()
                                                        ?.buttonBackground ??
                                                    Colors.white,
                                                borderRadius:
                                                    BorderRadius.circular(
                                                      widget
                                                              .getMindMap()
                                                              ?.buttonWidth ??
                                                          16,
                                                    ),
                                              ),
                                              child: IconButton(
                                                onPressed: () {
                                                  widget.setExpanded(
                                                    !widget.getExpanded(),
                                                  );
                                                },
                                                padding: EdgeInsets.zero,
                                                icon: Icon(
                                                  widget.getExpanded()
                                                      ? Icons
                                                            .remove_circle_outline
                                                      : Icons
                                                            .add_circle_outline,
                                                  size:
                                                      widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16,
                                                  color:
                                                      widget
                                                          .getMindMap()
                                                          ?.buttonColor ??
                                                      Colors.white,
                                                ),
                                              ),
                                            ),
                                          ),
                                        ]
                                      : [])
                                : [
                                    //left Add Button
                                    Container(
                                      constraints: BoxConstraints(
                                        maxWidth:
                                            (widget
                                                .getMindMap()
                                                ?.buttonWidth ??
                                            16),
                                        maxHeight:
                                            (widget
                                                .getMindMap()
                                                ?.buttonWidth ??
                                            16),
                                      ),
                                      decoration: BoxDecoration(
                                        color:
                                            (widget
                                                .getMindMap()
                                                ?.buttonBackground ??
                                            Colors.white),
                                        border: Border.all(
                                          color:
                                              (widget
                                                  .getMindMap()
                                                  ?.buttonColor ??
                                              Colors.black),
                                          width: 1,
                                        ),
                                        borderRadius: BorderRadius.circular(
                                          (widget.getMindMap()?.buttonWidth ??
                                              16),
                                        ),
                                      ),
                                      child: IconButton(
                                        onPressed: () {
                                          widget.addLeftChildNode();
                                        },
                                        padding: EdgeInsets.zero,
                                        hoverColor: Colors.green.shade200,
                                        highlightColor: Colors.green,
                                        icon: Icon(
                                          Icons.add_rounded,
                                          size:
                                              (widget
                                                      .getMindMap()
                                                      ?.buttonWidth ??
                                                  16) -
                                              4,
                                          color:
                                              (widget
                                                  .getMindMap()
                                                  ?.buttonColor ??
                                              Colors.black),
                                        ),
                                      ),
                                    ),

                                    //Sapce
                                    widget.getNodeType() == NodeType.root ||
                                            (widget
                                                    .getMindMap()
                                                    ?.getShowRecycle() ??
                                                false)
                                        ? SizedBox(width: 0, height: 0)
                                        : SizedBox(width: 4),
                                    //left Delete Button
                                    widget.getNodeType() == NodeType.root ||
                                            (widget
                                                    .getMindMap()
                                                    ?.getShowRecycle() ??
                                                false)
                                        ? SizedBox(width: 0, height: 0)
                                        : Container(
                                            constraints: BoxConstraints(
                                              maxWidth:
                                                  (widget
                                                      .getMindMap()
                                                      ?.buttonWidth ??
                                                  16),
                                              maxHeight:
                                                  (widget
                                                      .getMindMap()
                                                      ?.buttonWidth ??
                                                  16),
                                            ),
                                            decoration: BoxDecoration(
                                              color:
                                                  (widget
                                                      .getMindMap()
                                                      ?.buttonBackground ??
                                                  Colors.white),
                                              border: Border.all(
                                                color:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonColor ??
                                                    Colors.black),
                                                width: 1,
                                              ),
                                              borderRadius:
                                                  BorderRadius.circular(
                                                    (widget
                                                            .getMindMap()
                                                            ?.buttonWidth ??
                                                        16),
                                                  ),
                                            ),
                                            child: IconButton(
                                              onPressed: () {
                                                widget
                                                    .getParentNode()
                                                    ?.removeLeftItem(widget);
                                              },
                                              hoverColor: Colors.red.shade200,
                                              highlightColor: Colors.red,
                                              padding: EdgeInsets.zero,
                                              icon: Icon(
                                                Icons.close_rounded,
                                                size:
                                                    (widget
                                                            .getMindMap()
                                                            ?.buttonWidth ??
                                                        16) -
                                                    4,
                                                color:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonColor ??
                                                    Colors.black),
                                              ),
                                            ),
                                          ),
                                  ])
                          : (widget.getReadOnly()
                                ? (widget.getNodeType() == NodeType.left &&
                                          widget.canExpand() &&
                                          !widget.getExpanded() &&
                                          widget.getLeftItems().isNotEmpty
                                      ? [
                                          //Left Expand Button
                                          Container(
                                            constraints: BoxConstraints(
                                              maxHeight:
                                                  (widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16) +
                                                  widget
                                                          .getLinkOutOffset()
                                                          .abs() *
                                                      2,
                                            ),
                                            padding: EdgeInsets.fromLTRB(
                                              0,
                                              widget.getLinkOutOffset() > 0
                                                  ? widget.getLinkOutOffset() *
                                                        2
                                                  : 0,
                                              0,
                                              widget.getLinkOutOffset() < 0
                                                  ? widget
                                                            .getLinkOutOffset()
                                                            .abs() *
                                                        2
                                                  : 0,
                                            ),
                                            child: Container(
                                              constraints: BoxConstraints(
                                                maxWidth:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16),
                                                maxHeight:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16),
                                              ),
                                              decoration: BoxDecoration(
                                                color:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonBackground ??
                                                    Colors.white),
                                                borderRadius:
                                                    BorderRadius.circular(
                                                      (widget
                                                              .getMindMap()
                                                              ?.buttonWidth ??
                                                          16),
                                                    ),
                                              ),
                                              child: IconButton(
                                                onPressed: () {
                                                  widget.setExpanded(
                                                    !widget.getExpanded(),
                                                  );
                                                },
                                                padding: EdgeInsets.zero,
                                                icon: Icon(
                                                  Icons.add_circle_outline,
                                                  size:
                                                      (widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16),
                                                  color:
                                                      (widget
                                                          .getMindMap()
                                                          ?.buttonColor ??
                                                      Colors.black),
                                                ),
                                              ),
                                            ),
                                          ),
                                        ]
                                      : [])
                                : [])),
                    ],
                  ),
                ),
              ]
            : []),
        //Node
        MindMapNodeTitle(node: widget),
        //RightSpace
        ...(widget.getNodeType() != NodeType.left
            ? [
                Container(
                  constraints: BoxConstraints(
                    minWidth: widget.getHSpace().toDouble(),
                    maxWidth: widget.getHSpace().toDouble(),
                    minHeight: (widget.getMindMap()?.buttonWidth ?? 16),
                  ),
                  padding: EdgeInsets.fromLTRB(3, 0, 3, 0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.start,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      ...(widget.getSelected()
                          ? (widget.getReadOnly()
                                ? (widget.getNodeType() == NodeType.right &&
                                          widget.canExpand() &&
                                          widget.getRightItems().isNotEmpty
                                      ? [
                                          //Right Expand Button
                                          Container(
                                            constraints: BoxConstraints(
                                              maxHeight:
                                                  (widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16) +
                                                  widget
                                                          .getLinkOutOffset()
                                                          .abs() *
                                                      2,
                                            ),
                                            padding: EdgeInsets.fromLTRB(
                                              0,
                                              widget.getLinkOutOffset() > 0
                                                  ? widget.getLinkOutOffset() *
                                                        2
                                                  : 0,
                                              0,
                                              widget.getLinkOutOffset() < 0
                                                  ? widget
                                                            .getLinkOutOffset()
                                                            .abs() *
                                                        2
                                                  : 0,
                                            ),
                                            child: Container(
                                              constraints: BoxConstraints(
                                                maxWidth:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16),
                                                maxHeight:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16),
                                              ),
                                              decoration: BoxDecoration(
                                                color:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonBackground ??
                                                    Colors.white),
                                                borderRadius:
                                                    BorderRadius.circular(
                                                      (widget
                                                              .getMindMap()
                                                              ?.buttonWidth ??
                                                          16),
                                                    ),
                                              ),
                                              child: IconButton(
                                                onPressed: () {
                                                  widget.setExpanded(
                                                    !widget.getExpanded(),
                                                  );
                                                },
                                                padding: EdgeInsets.zero,
                                                icon: Icon(
                                                  widget.getExpanded()
                                                      ? Icons
                                                            .remove_circle_outline
                                                      : Icons
                                                            .add_circle_outline,
                                                  size:
                                                      (widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16),
                                                  color:
                                                      (widget
                                                          .getMindMap()
                                                          ?.buttonColor ??
                                                      Colors.black),
                                                ),
                                              ),
                                            ),
                                          ),
                                        ]
                                      : [])
                                : [
                                    //left Delete Button
                                    widget.getNodeType() == NodeType.root ||
                                            (widget
                                                    .getMindMap()
                                                    ?.getShowRecycle() ??
                                                false)
                                        ? SizedBox(width: 0, height: 0)
                                        : Container(
                                            constraints: BoxConstraints(
                                              maxWidth:
                                                  (widget
                                                      .getMindMap()
                                                      ?.buttonWidth ??
                                                  16),
                                              maxHeight:
                                                  (widget
                                                      .getMindMap()
                                                      ?.buttonWidth ??
                                                  16),
                                            ),
                                            decoration: BoxDecoration(
                                              color:
                                                  (widget
                                                      .getMindMap()
                                                      ?.buttonBackground ??
                                                  Colors.white),
                                              border: Border.all(
                                                color:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonColor ??
                                                    Colors.black),
                                                width: 1,
                                              ),
                                              borderRadius:
                                                  BorderRadius.circular(
                                                    (widget
                                                            .getMindMap()
                                                            ?.buttonWidth ??
                                                        16),
                                                  ),
                                            ),
                                            child: IconButton(
                                              onPressed: () {
                                                widget
                                                    .getParentNode()
                                                    ?.removeRightItem(widget);
                                              },
                                              hoverColor: Colors.red.shade200,
                                              highlightColor: Colors.red,
                                              padding: EdgeInsets.zero,
                                              icon: Icon(
                                                Icons.close_rounded,
                                                size:
                                                    (widget
                                                            .getMindMap()
                                                            ?.buttonWidth ??
                                                        16) -
                                                    4,
                                                color:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonColor ??
                                                    Colors.black),
                                              ),
                                            ),
                                          ), //Sapce
                                    widget.getNodeType() == NodeType.root ||
                                            (widget
                                                    .getMindMap()
                                                    ?.getShowRecycle() ??
                                                false)
                                        ? SizedBox(width: 0, height: 0)
                                        : SizedBox(width: 4),
                                    //Right add Button
                                    Container(
                                      constraints: BoxConstraints(
                                        maxWidth:
                                            (widget
                                                .getMindMap()
                                                ?.buttonWidth ??
                                            16),
                                        maxHeight:
                                            (widget
                                                .getMindMap()
                                                ?.buttonWidth ??
                                            16),
                                      ),
                                      decoration: BoxDecoration(
                                        color:
                                            (widget
                                                .getMindMap()
                                                ?.buttonBackground ??
                                            Colors.white),
                                        border: Border.all(
                                          color:
                                              (widget
                                                  .getMindMap()
                                                  ?.buttonColor ??
                                              Colors.black),
                                          width: 1,
                                        ),
                                        borderRadius: BorderRadius.circular(
                                          (widget.getMindMap()?.buttonWidth ??
                                              16),
                                        ),
                                      ),
                                      child: IconButton(
                                        onPressed: () {
                                          widget.addRightChildNode();
                                        },
                                        padding: EdgeInsets.zero,
                                        hoverColor: Colors.green.shade200,
                                        highlightColor: Colors.green,
                                        icon: Icon(
                                          Icons.add_rounded,
                                          size:
                                              (widget
                                                      .getMindMap()
                                                      ?.buttonWidth ??
                                                  16) -
                                              4,
                                          color:
                                              (widget
                                                  .getMindMap()
                                                  ?.buttonColor ??
                                              Colors.black),
                                        ),
                                      ),
                                    ),
                                  ])
                          : (widget.getReadOnly()
                                ? (widget.getNodeType() == NodeType.right &&
                                          widget.canExpand() &&
                                          !widget.getExpanded() &&
                                          widget.getRightItems().isNotEmpty
                                      ? [
                                          //Right Expand Button
                                          Container(
                                            constraints: BoxConstraints(
                                              maxHeight:
                                                  (widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16) +
                                                  widget
                                                          .getLinkOutOffset()
                                                          .abs() *
                                                      2,
                                            ),
                                            padding: EdgeInsets.fromLTRB(
                                              0,
                                              widget.getLinkOutOffset() > 0
                                                  ? widget.getLinkOutOffset() *
                                                        2
                                                  : 0,
                                              0,
                                              widget.getLinkOutOffset() < 0
                                                  ? widget
                                                            .getLinkOutOffset()
                                                            .abs() *
                                                        2
                                                  : 0,
                                            ),
                                            child: Container(
                                              constraints: BoxConstraints(
                                                maxWidth:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16),
                                                maxHeight:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonWidth ??
                                                    16),
                                              ),
                                              decoration: BoxDecoration(
                                                color:
                                                    (widget
                                                        .getMindMap()
                                                        ?.buttonBackground ??
                                                    Colors.white),
                                                borderRadius:
                                                    BorderRadius.circular(
                                                      (widget
                                                              .getMindMap()
                                                              ?.buttonWidth ??
                                                          16),
                                                    ),
                                              ),
                                              child: IconButton(
                                                onPressed: () {
                                                  widget.setExpanded(
                                                    !widget.getExpanded(),
                                                  );
                                                },
                                                padding: EdgeInsets.zero,
                                                icon: Icon(
                                                  Icons.add_circle_outline,
                                                  size:
                                                      (widget
                                                          .getMindMap()
                                                          ?.buttonWidth ??
                                                      16),
                                                  color:
                                                      (widget
                                                          .getMindMap()
                                                          ?.buttonColor ??
                                                      Colors.black),
                                                ),
                                              ),
                                            ),
                                          ),
                                        ]
                                      : [])
                                : [])),
                    ],
                  ),
                ),
              ]
            : []),
        //Right Items
        ...(rightItems.isEmpty || !widget.getExpanded()
            ? []
            : [
                Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  spacing: widget.getVSpace().toDouble(),
                  children: rightItems,
                ),
              ]),
      ],
    ),
  );
}