Why does setState take a closure?

When Flutter had a “markNeedsBuild” function, developers ended up just sort of calling it at random times. When the syntax switched to setState(() { ... }), developers were much more likely to use the API correctly. They are functionally equivalent from the machine’s point of view, but they seem to evoke different code from developers.

If you follow the convention of only mutating member variables inside a setState closure, you’ll avoid a situation you’re refactoring some code and accidentally remove the call to setState, or call setState unnecessarily. And if your State is unmounted, Flutter can fail an assertion so you know something is wrong as soon as you begin trying to mutate members, instead of at the end.

Eventually there will probably be an analyzer warning enforcing that setState is always called when mutating members of a State, so any member variable mutation that happens outside of initState or a setState callback will be flagged as suspect.

If you’re just getting started with state in Flutter, check out the Flutter widgets tour. I’ve found that a lot of cases where I was calling setState can be handled more elegantly with FutureBuilder, StreamBuilder, AnimatedWidget, or AnimatedBuilder, so don’t forget to consider those alternatives if you find yourself calling setState a lot.

Adam Barth and Yaroslav Volovich contributed to this question/answer.

Leave a Comment