1

I am confused on why do we have to put a function in setState to update variables. I could instead update the variables and call setState. I modified the code from https://flutter.dev/docs/development/ui/widgets-intro

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

Instead, I thought of doing this

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    _counter++;
    setState(() {
    });
  }

This still works, now I was thinking why make setState() have a function as a parameter, instead setState could not have any parameters like setState(); and we would just call it after we update our variables.

Diablo
  • 13
  • 3

4 Answers4

0

it can not run properly in the big project. So answer of your problem is like this, if you dont use setState(() => {});, your screen can not render if you change any value of variable. So if you want to effect when you change value of variable in the screen, you have to use setState(() => {});.

SefaUn
  • 824
  • 1
  • 7
  • 23
  • I have passed an empty function inside setState in the second part, which works, i.e. the UI gets updated. Also, my question is not how to update the UI. – Diablo Apr 10 '21 at 17:15
0

You'll find the answer in the official docs:

The provided callback is immediately called synchronously. [...] If you just change the state directly without calling setState, the framework might not schedule a build and the user interface for this subtree might not be updated to reflect the new state.

I think the problem is that UI and state object will not be in sync for a short period of time if you call setState((){}); after changing the state.

0

I think that the original question is an excellent one and I believe that the existing answers miss the point. As diablo says, the question is not how to update the UI, but why the Flutter team chose to creat a method that requires a function as an argument. I think that they were wrong to do so and here is why...

First, consider other occasions where an anonymous function can be passed as an argument. An excellent example is the onPressed: attribute of a Button. Here we pass a function to the Button to be saved for later (and conditional) execution. Another classic example is the map() method in an Iterable where you pass an anonymous function that will be executed repeatedly. By contrast, the setState() method always evaluates the passed-in anonymous function exactly once (unless it detects an error earlier). Thus, the typical use case for an anonymous function is lacking.

Second, as to the suggestion that the UI "will not be in sync for a short period of time", that doesn't really work for me. If you read the implementation of setState() you will see that it first executes the passed-in function and then calls markNeedsBuild() which schedules the UI rebuild for later. So even if you use setState() as designed, there will be a short period of time when the state and UI are not in sync. Since there will always be a delay, the passed-in anonymous function hardly improves that situation.

The only reason I see for the current implementation is that it will trigger an error before making the state change if the setState() method is called in the constructor or after the widget is gone. In the modified code the error checking is done after the state change, which accomplishes what seems to me to be essentially the same thing.

I suppose that a possible argument is that you might forget to call stateHasChanged() after the state change, but it seems just as likely that you would forget to wrap the state change in setState().

I believe that the existing implementation is needlessly complicated. I suggest the following addition to State:

extension on State {
  void stateHasChanged() => setState(() {});
}

With this you can simply change the state and then notify the State instance that the state has changed. Which of the following is easier to understand?

  void _increment() {
    setState(() {
      _counter++;
    });
  }

or

  void _increment() {
    _counter++;
    stateHasChanged();
  }

The second code snippet is shorter and carries less cognitive load. What's not to like?

James Foster
  • 2,070
  • 10
  • 15
0

Yes, setState is basically this:

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

I simplified this one a little bit, but the earliest versions of the method look quite similar to the one above:

void setState(Function fn()) {
    fn();
    scheduleBuild();
  }
}

So why do we need this? The answer is simple: because it directs the developer to the right direction.

At the beginning, there was no setState method available.

According to Ian Hickson:

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. We found people understand much more easily that if you didn't change any state, you don't need to call it; if you did change some state, you call it and change the state during the call.

(see github issue here)

After this, they added several assertions to for example, make sure the function is not async etc. The approach may confuse you at first if you are an experienced developer, but the main reason is still as simple as "because it works better in practice for most users."

Gökhan Mete ERTÜRK
  • 3,378
  • 2
  • 19
  • 23