flutterperformancewidget

Is building widgets in functions efficient?


I was told by someone who seemed quite good at flutter that building widgets inside functions then passing them back to the build function was inefficient as it didn't allow flutter to skip the build process for widgets that haven't changed. for example:

/// bad

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF7F7F7),
      appBar: _buildAppBar(),
      body: _buildContent(),
    );
  }

  PreferredSizeWidget _buildAppBar() {
    return AppBar(...);
  }

  Widget _buildContent() {
    return Stack(
      children: [
        SafeArea(
          child: Column(
            children: [
              Expanded(
                child: ...,
              ),
              ...
            ],
          ),
        ),
      ],
    );
  }
  ...
/// good

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF7F7F7),
      appBar: AppBar(...),
      body: Stack(
        children: [
          SafeArea(
            child: Column(
              children: [
                Expanded(
                  child: ...,
                ),
                ...,
              ],
            ),
          ),
        ],
      ),
    );
  }

Is this true? I believed them until I came across the very example shown above in the dart documentation at https://docs.flutter.dev/cookbook/effects/drag-a-widget. Now my whole world is upside down and I just don't know what to believe anymore.

Does breaking the build functions into smaller functions hurt performance by not allowing the framework to cache or detect unchanged widgets properly?

EDIT: for what it may or may not be worth I took this question to chatGPT. It basically said if you want to improve performance and you have widgets that don't need to be rebuilt, ever, you should set them as constants or variables, instead of rebuilding them in the widget tree or functions.


Solution

  • The Flutter team has an explainer video on exactly this question.

    There are 3 trees that Flutter uses for building the UI:

    If a part of the widget tree changes, the other trees are updated only for that specific part. However, when you use helper methods instead of new widgets, Flutter can't see which parts of the widget tree changed inside the helper method. Thus, it always redraws all UI elements from that helper method — making it a lot less efficient (depending on what's in your method).


    As for the UI drag example you bring up above, DragTarget uses a builder — which is actually OK, as long as DragTarget is in the build method. There's lots of builders you can use inside the build method without worrying about performance impact.