Check context.mounted After Every await
Your Flutter app crashes after a slow network call. One line fixes it. The pattern is always the same: a button handler awaits a request, the user navigates away while it's in flight, and when the response finally lands your code touches a BuildContext whose widget no longer exists. On a fast connection you never see it; on a 3-second cellular round trip it's a crash report.
Why the context dies
Every widget's BuildContext is only valid while that widget is mounted in the tree. An await is an async gap — between the line before it and the line after it, anything can happen: the user pops the route, the parent rebuilds without this child, the app backgrounds. If the widget was disposed during the gap, calling Navigator.of(context), ScaffoldMessenger.of(context), or setState afterwards hits a defunct element. The framework's own docs are blunt about it: accessing properties of a BuildContext or calling methods on it "is only valid while mounted is true."
The one-line guard
Since Flutter 3.7, BuildContext itself exposes a mounted property, so the fix is a single check between the await and the first use of the context:
Future<void> _onSave(BuildContext context) async {
await repository.save(item); // async gap — anything can happen here
if (!context.mounted) return; // the guard
Navigator.of(context).pop(); // now safe
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('Saved')));
}
Inside a State subclass, use the state's own mounted instead — same idea, no context parameter needed:
final data = await api.fetch();
if (!mounted) return;
setState(() => _data = data);
The guard goes after every await that precedes a context use — two awaits in a row means the first check is stale by the time the second completes.
Rules of thumb
| Situation | Guard |
|---|---|
Plain function receiving a BuildContext | if (!context.mounted) return; after each await |
Inside State<T> (has its own context) | if (!mounted) return; after each await |
| Multiple awaits before the context use | Check after the last await, not just the first |
Context captured before the await (e.g. final nav = Navigator.of(context) first) | Capturing Navigator/ScaffoldMessenger before the gap avoids the lookup, but still check mounted before UI work |
| No context use after the await | No guard needed |
Let the linter catch it for you
You don't have to rely on discipline. The Dart linter rule use_build_context_synchronously flags exactly this bug — a BuildContext referenced across an async gap without a mounted check. It ships stable and is part of the recommended flutter_lints set that new Flutter projects (2.3.0+) include by default via analysis_options.yaml:
include: package:flutter_lints/flutter.yaml
If your CI runs flutter analyze, this class of crash becomes a build failure instead of a production incident.
Power-user notes
- AI-generated code trips on this constantly. Async UI handlers are exactly where coding assistants produce plausible-but-unsafe code. Keep
use_build_context_synchronouslyenabled and treat its warnings on generated diffs as non-negotiable — it's a free hallucination detector for lifecycle bugs. return, don't nest. Prefer the early-return form (if (!context.mounted) return;) over wrapping the rest of the handler inif (context.mounted) { ... }— it keeps the happy path flat and makes the nextawaiteasier to audit.- Guarded means skipped, not retried. When the check fails, the right behavior is usually to do nothing: the screen is gone, so there's no snackbar to show. If the work still matters (e.g., persisting data), do the work first, guard only the UI part.
- Pre-3.7 codebases: only
State.mountedexisted; helpers that took a rawBuildContextcouldn't check it. If you're on an older SDK, that's a strong reason to upgrade — the one-line guard is the whole feature.
Resources
- use_build_context_synchronously — Dart linter rule
- BuildContext.mounted — Flutter API docs
- State.mounted — Flutter API docs
- flutter_lints — recommended lint set on pub.dev
- Flutter 3.7.0 release notes (mounted added to BuildContext)
Building an AI feature? Yeda AI designs, audits, and ships production LLM systems.