Home > Back-end >  Problem with using `Navigator.pop` and setState in same method
Problem with using `Navigator.pop` and setState in same method

Time:01-03

I have stateful widget that is basically list of tasks. In that widget i also have a button that causes Dialog box to appear.

Dialog box is AlertDialog that consists of text field that has controller inside of it and Save button. Button calls state's method that does few things:

  • dialog box to pop so to get back to list of tasks
  • causes setState of stateful widget with value from controller
  • controller to clear

Method looks like this:

  void saveNewTask() {
    Navigator.of(context).pop();
    setState(() {
      toDoList.add([_controller.text, false]);
    });
    _controller.clear();
    
  }

This method is passed all the way down to Save button, and Dialog box is of course seperate widget so Navigator gets the right context.

Considering the order in saveNewTask i would assume the order would go like this:

  1. Dialog box gets poped off

  2. setState gets called, stateful widget gets rebuild

  3. controller is cleared.

but in slow motion i can see this operations don't happen in this order. Controller is cleared and widget is rebuilt before navigator pops off dialog box.

Before cliking the Save Button : -

After clicking the Save Button : -

(and after the second picture dialog box gets removed)

Digging through source code i found out that pop causes another setState of navigator's state that removes the Dialog box. I thought that maybe popis asynchronous considering the behavior but Future is not returned so i can't use asynchronous function or thento enforce the order. setState is of course not asynchronous so i don't have a clue what's happening here??

Any clue is greatly appreciated.

CodePudding user response:

The procedure you're following is not the actual way to do it. Reasons:

  1. AlertDialogue and the other dialogues you show are not a part of your main screen widget. It's a totally different widget. So you can't actually setState() of your widget from here.

  2. Now you're trying to set the state of the dialogue widget which is already being removed when you say Navigator.pop();. Which is not possible. Cz that widget doesn't exist anymore.

In normal situations that controller would get destroyed too with the alert widget. But it's a good practice to manually destroy it.

So, to do it the right way these are the steps you should follow ->

  1. First return the controller value to the main widget by passing it to the Navigator.pop(); function. In your case, just pass it for the save button value. Like this:

    Navigator.pop(context, controller.text);
    
  2. Now await on the showDialogue() method. And receive the value to the main widget. Something like this.

    String? data = await showDialog(
    context: context, 
    builder: (context) => AlertDialog());
    

Now in the main widget check if the dialogue returns a string or not. If it does, then set the state with the data you just received. And if it doesn't. In your case, when the cancel button is clicked. It will return null. Then don't do anything.

And finally, if you want to clear the controller for some reason. Then clear it before popping. And if you want to dispose it. Just dispose it inside the dispose method of your stateful widget.

  • Related