If you’ve ever received the DON'T use BuildContext across asynchronous gaps.
message in Flutter you’re in good company. I keep getting it, fixing it, then forgetting what I did when I get it next time. The solution is very simple, don’t do that in stateless widgets and always check if the widget is mounted. It is made slightly more complicated by the fact that the docs on the linter error are either wrong or out of date.
Build Context The Wrong Way
void onNextButtonTapped(BuildContext context) async {
await Future.delayed(const Duration(seconds: 1));
Navigator.of(context).pop();
}
Here’s why this is bad. This is a button handler called from the onPress
method of some button in the UI. While this method is being evaluated other methods could be called because this method is async
. If the widget also had a different button on it, say a close button, that button could call Navigator.of(context).pop()
while the onButtonTapped
method is still being evaluated. In that case the result of the Navigator
call in this method would be an error because the widget the BuildContext
depends on would already be gone.
Build Context The Right Way
void onNextButtonTapped(BuildContext context) async {
await Future.delayed(const Duration(seconds: 1));
if (mounted) {
Navigator.of(context).pop();
}
}
This works, and passes the lint check, because if the widget has been invalidated before the Navigator call is reached it won’t try to use it. Note that this only works in a StatefulWidget
, not in a StatelessWidget
. If you’re trying to solve the problem in a StatelessWidget
the solution is to convert it to a StatefulWidget
and then use the mounted
check as above. There are other suggestions in various answers on Stack Overflow suggesting that you save the Navigator
before any await
or other asynchronous calls but I don’t think that solves the underlying problem, it just means the linter won’t notice it.