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:
Dialog box gets poped off
setState gets called, stateful widget gets rebuild
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 pop
is asynchronous considering the behavior but Future
is not returned so i can't use asynchronous function or then
to 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:
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.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 ->
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);
Now
await
on theshowDialogue()
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.