# How people calling markNeedsBuild "like a good luck charm" gave us setState

Source: https://iiro.dev/set-state/
Published: 2021-03-29

The embarrassing story of what I thought setState was, and what it totally wasn't.

---

Here's a somewhat embarrassing assumption I made about `setState` when I started to learn Flutter
[almost 4 years ago](https://www.codemate.com/considering-flutter/).

We all know `setState` from the counter example:


```dart
class _MyWidgetState extends State<MyWidget> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter = _counter + 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text("The current value is $_counter!");
  }
}
```


Here's how _I thought_ it worked:
1. There's one stateful field called `_counter`.
2. We also have a `Text` widget that displays the value of `_counter`.
3. Every time we want to update `_counter`, we also have to wrap it in an anonymous function and pass it to `setState`.
Otherwise, the framework doesn't know what was updated.

Four months into my Flutter journey, I found out that **assumption number three was not true**.

We do have to call `setState` when updating `_counter`, but there's absolutely no need to do it in an anonymous callback.

This:


```dart
setState(() {
  _counter = _counter + 1;
});
```


is exactly the same as this:


```dart
_counter = _counter + 1;
setState(() {});
```


The Flutter framework **does not** magically inspect code inside the anonymous function and then do some
diffing to see what changed. 

Whenever you call `setState`, the widget rebuilds - even if nothing changed and the callback passed to `setState` was empty.

What I had initially assumed about `setState` was totally made up in my own head.

## A peek behind the curtains

Let's see how the code behind `setState` looks like:


```dart
@protected
void setState(VoidCallback fn) {
  assert(fn != null);
  assert(() {
    if (_debugLifecycleState == _StateLifecycle.defunct) {
      throw FlutterError.fromParts([/* ... */]);
    }
    if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
      throw FlutterError.fromParts([/* ... */]);
    }
    return true;
  }());
  final dynamic result = fn() as dynamic;
  assert(() {
    if (result is Future) {
      throw FlutterError.fromParts([/* ... */]);
    }
    return true;
  }());
  _element.markNeedsBuild();
}
```


Let's remove the assertions:


```dart
@protected
void setState(VoidCallback fn) {
  final dynamic result = fn() as dynamic;
  _element.markNeedsBuild();
}
```


We can actually boil it down even more:


```dart
@protected
void setState(VoidCallback fn) {
  fn();
  _element.markNeedsBuild();
}
```


All that `setState` does is calling the provided callback and then the associated element gets scheduled for a new build.

No magical field diffing here.

## So why do we have setState then?

Because of UX studies ran by the Flutter team.

They do [a lot of it](https://www.youtube.com/watch?v=Zx732oQN_Rs).

Back in 2017 and 2018, there was a GitHub issue label called _"first hour"_. The Flutter team ran developer UX
studies and observed how Flutter newcomers would use Flutter when left on their own for an hour.
Those studies would shape future API decisions for Flutter.

Apparently, the `setState` thing is one of these findings.

From the [GitHub issue](https://github.com/flutter/flutter/issues/12296) that made me realize I had been living in a lie:

> We used to just have a markNeedsBuild method but we found people called it like a good luck charm -- any time they 
> weren't sure if they needed to call it, they'd call it.
> 
> We changed to a method that takes a (synchronously-invoked) callback, and suddenly people had much less trouble with it.

There's also [a StackOverflow answer](https://stackoverflow.com/a/44379367) from Collin Jackson from the Flutter team back then:

> 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.

So the whole `setState` API is just a mental trick.
And it seems to work - apparently it lead into people building their widgets less often.

I never had problems knowing when to call `setState`, but I can definitely see myself wondering _"who's Mark and what
is he building?"_

Mystery solved.

## Conclusion

What does all of this mean?

Should we now convert all our `setState` calls to something like this?


```dart
_counter = _counter + 1;
setState(() {});
```


Probably not.

Even though there's no difference, calling `setState` with an anonymous callback feels right.

At this point, the anonymous callback is an established convention and anything else just feels weird.
