StatelessWidget vs a function returning Widgets in terms of performance

3,562

First of all, I'd like to note that a package is available to make a StatelessWidget from a function: functional_widget


The gain is performance is not necessarily true. It depends on how you use your widgets, mostly how you use them to manage your state.

By default, classes may degrade performances when opposed to functions in an application that doesn't utilize their power.

The real question is: What is their power?

Simple: Classes can update independently from each other. Functions cannot

It is possible for classes to partially update the widget tree.

Consider a widget that rebuilds every frame and returns its child:

class InfiniteLoop extends StatefulWidget {
  const InfiniteLoop({Key key, this.child}) : super(key: key);
  final Widget child;
  @override
  _InfiniteLoopState createState() => _InfiniteLoopState();
}

class _InfiniteLoopState extends State<InfiniteLoop> {
  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {}));

    return widget.child;
  }
}

Now if we wrap our whole application in that widget, what happens?

void main() => runApp(InfiniteLoop(child: MyApp()));

Nothing

Sure, you'll have one widget that rebuilds often in your tree. But in reality, the build method of MyApp will be called only once.

That's because Flutter is able to abort the tree rebuild when the instance of a widget doesn't change.


Classes can abuse of this optimization.

Using classes it is possible to cleverly split the rebuilding of your widget tree into independent parts.

It's not reasonable to list all the potential optimization factors that a class allow, as there are too many.

The following example is a widget that takes an int and formats it into a Text. The catch is, this widget will rebuild only if the int passed change:

class Counter extends StatelessWidget {
  const Counter({Key key, this.value}) : super(key: key);

  final int value;

  @override
  Widget build(BuildContext context) {
    return Text(value.toString());
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) || (other is Counter && other.value == value);

  @override
  int get hashCode => value.hashCode;
}

This works because Flutter uses the == operator to know if a widget should update or not (hence why const constructor is a good optimization factor).

This is not the only solution, but it's a good example of something that functions can't do.

Share:
3,562
robertohuertasm
Author by

robertohuertasm

I look back at when I began this professional journey and so many things have happened in all those 16 years. After intensively working with the .NET framework while building websites for FIFA, playing around with Mobility and digging into JavaScript's most inner intricacies I'm still amazed of what the world of software development is bringing into the table. There's always something new to try and new ways to solve problems in a more elegant and productive fashion. Most of my past experiences have been as an employee in a product company but I've also tasted the wildness of being a consultant. Being in such a position forces you to be prepared for any eventuality that may arise so you have to be in touch with a huge variety of technologies, mastering what you already know and keeping an eye on what's new and hot. In July 2016 I had the wonderful opportunity to start working with two colleagues in a brand new company. Just the three of us facing new challenges and learning paths. Some may think it was a scary scenario but I enjoyed it a lot! Eventually, the team grew, and we used cutting edge technologies and best practices in order to achieve our goals. No hierarchy but true consensus is what led our team. We discussed everything and heard all the opinions of our team members before making a decision, be it technical or based on a different matter. No timetables, remote work was welcome, it was easy to make family life compatible with my professional duties. Undoubtedly, for me, Valudio has been the greatest professional experience so far. In May 2018 I accepted a new challenge at Telefónica Alpha with the promise of being able to change people's lives and help them have a healthier life. In November 2020, the company became Koa Health! We're closer to make this promise a reality! Finally, in the last years, my fondness for Rust and WebAssembly has grown and I'm looking for opportunities to put all this into play whenever possible. Cheers!

Updated on December 09, 2022

Comments

  • robertohuertasm
    robertohuertasm over 1 year

    Is there any difference, performance wise, in using a StatelessWidget vs a function returning a Widget?

    I'm well aware of at least the differences pointed out in this flutter's repo issue which don't have a relationship with performance.

    The fact is that I have some colleagues claiming that functional widgets are worst in terms of performance but after reading a little bit about the subject I cannot find any conclusive piece of documentation that can give credit to that assertion so any kind of clarification regarding this matter will be very welcome!

    As far as I can see the only difference between them would be in the case of using a const Widget, which seems that would avoid the rebuilding phase.