Caching child widgets vs. recreating them

Viewed 805

Im unsure about wether caching a widget instance and reusing it in the build() method makes a significant difference.

Suppose we have two widget classes:

class ParentWidget extends StatelessWidget {
  ParentWidget({Key key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container( // or some other widgets that define the ui
      child: ChildWidget(/*...*/),
    );
  }
}

class ChildWidget extends StatelessWidget {
  ChildWidget({Key key}) : super(key: key); // no constant

  @override
  Widget build(BuildContext context) {
    /*
     * returns some Widget
     */
  }
}

From my understanding, everytime Flutter calls build() on the ParentWidget, a new ChildWidget is created and attached to the same Element in the element tree. Except if const ChildWidget is available (e.g. ChildWidget has a const constructor).

However, we can cache the child like so:

class ParentWidget extends StatelessWidget {
  final Widget child;
  ParentWidget({Key key})
      : child = ChildWidget(/*...*/),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      // or some other widgets that define the ui
      child: child, // <-- use child instead of ChildWidget()
    );
  }
}

From my understanding, the same widget will be used and no modification of the element tree has to be done by flutter. But I am unsure in which cases that matters.

Is there a significant difference between caching a widget and creating a new Widget with the same configuration? If so, is there a rule on when to cache widgets instead of reconstructing them?

2 Answers

In the documentation of StatefulWidget, it is recommended to cache a widget if the subtree represented by that widget does not change:

If a subtree does not change, cache the widget that represents that subtree and re-use it each time it can be used. It is massively more efficient for a widget to be re-used than for a new (but identically-configured) widget to be created. Factoring out the stateful part into a widget that takes a child argument is a common way of doing this.

I therefore conclude that whenever a Widget changes State, subtrees that do not change based on the state should be cached and reused.

However, the example declared in the question is not a situation in which choosing to cache the widget is a good option, as only the root of a not-changing subtree should be cached by the parent of that subtree. But as ParentWidget is itself a StatelessWidget, it will be part of a not-changing subtree each time ChildWidget is. Therefore, some stateful Parent of ParentWidget should be responsible for caching.

There can be a difference, if for example the child widget allocates resources or has a large subtree of children.

The official flutter api documentation does recommend caching in certain situations. But generally, it should not be the first thing to do, when trying to optimise your views.

Read the documentation on Stateful and Stateless widgets, in particular the "Performance Considerations" section has a lot of relevant further information. For example:

  • If a subtree does not change, cache the widget that represents that subtree and re-use it each time it can be used.
  • Use const widgets where possible. (This is equivalent to caching a widget and re-using it.)
Related