0

I've been searching around and didn't quite find a suitable answer to this question: is it ok to reuse a widget in a subsequent build call? By that I mean that I create a StatefulWidget and in the build function I keep an internal reference to the returned widget. On later build (if I determine that nothing has changed since last build), I then return the last widget instead of building a new one.

I understand that one should avoid that, create simple builds and optimise invalidation process so that complex widgets don't get rebuilt too often. But under some circumstances, I find this can be a good solution.

Reading on this official doc page in particular, it is stated:

The traversal to rebuild all descendents stops when the same instance of the child widget as the previous frame is re-encountered. This technique is heavily used inside the framework for optimizing animations where the animation doesn’t affect the child subtree. See the TransitionBuilder pattern and the source code for SlideTransition, which uses this principle to avoid rebuilding its descendents when animating.

So doing something like this should be acceptable:

abstract class SmartState<T extends StatefulWidget> extends State<T> {
  Widget _lastBuild;
  T _previousWidget;

  bool shouldRebuild(T previousWidget);
  Widget doBuild(BuildContext context);

  @override
  @mustCallSuper
  Widget build(BuildContext context) {
    final T previousWidget = _previousWidget;
    _previousWidget = widget;

    if (previousWidget != null && _lastBuild != null && !shouldRebuild(previousWidget)) {
      return _lastBuild;
    }

    _lastBuild = doBuild(context);
    return _lastBuild;
  }
}

As a little background, I intend to extend this class in a list item. The list itself changes at times and is rebuilt with the new items, although most of the items remain the same, I just rearrange them, remove or add some. Also note that I had to put them in a Wrap widget because of design requirements.

So does anyone has any counter-indication or better solution than this. And most of all, I would like to know if it is considered ok or good practice to do such a reuse. Thanks!

1 Answers1

-1

You are unnecessary fighting with the framework, flutter internally takes care of rebuild by using methods like canUpdate() method. https://api.flutter.dev/flutter/widgets/Widget/canUpdate.html.

And for your solution to reduce build method calls. You are actually overcomplicating your widget instantiation. with no performance uplift, I would say even maybe worse performance(Please don't mind). As you are always throwing a new object to your SmartState class whenever a build is called until and unless those widgets have a const constructor(which helps for runtime final object) or you can see from the above link that widgets are compared on two basis keys and runtime type, Though I would say constConstructor would be more performing but dart and other languages have more support for objects rather than const variables. Most of the object disposal task is taken care by dart() itself.

So to finalize my answer you can do this and this may clear your doubt. Do this in any stateful widget and you may find your answer

  void initState() { 
    super.initState();
  print( SizedBox() == SizedBox());  //OutPut: false 
  }

A quick solution is to create a class like this.

class WidgetConstant {
  static const SizedBox _sizedBox = SizedBox(
    height: 2,
  );

  WidgetConstant._();
  static Widget sizedBox() => _sizedBox;
}

And now compare using the code below

void initState() { 
    super.initState();
  print( WidgetConstant.sizedBox() == WidgetConstant.sizedBox(););  //OutPut: true 
}

Conclusion: flutter is already optimized so enjoy using this highly performing framework .

Ruben Helsloot
  • 12,582
  • 6
  • 26
  • 49
UTKARSH Sharma
  • 698
  • 1
  • 8
  • 17
  • Thanks for your detailed answer. I understand what you're saying but I can say I really see a big improvement using my SmartState class for this use case. I know that comparing 2 newly instanciated widgets will not be equal. I think your are mistaking on what the `canUpdate` method does. It compares 2 given widgets to see if the framework can treat them as equivalent, as if the new one is a replacement to the old one. – Nicolas Dion-Bouchard Nov 05 '20 at 16:10
  • Also, in the quote from the documentation I put before, it says "when the same instance of the child widget as the previous frame is re-encountered", which means it is really the same instance, not a newly created one (widgetA == widgetB). So in my understanding, returning the previously built widget should do (and actually does) the trick. – Nicolas Dion-Bouchard Nov 05 '20 at 16:14
  • 1
    @NicolasDion-Bouchard or you can override `operator==` and `hashcode` so that it compares by your custom logic – Kirill Karmazin Jan 05 '23 at 14:57