Chapter 2 · Practice 5 of 5
Dispose every controller, subscription and focus node you create
Why it matters
Controllers such as TextEditingController, AnimationController, ScrollController and FocusNode, plus StreamSubscriptions, hold resources and listeners. If a State creates one, that State must dispose of it in dispose().
Forgetting this leaks memory and keeps tickers or streams running after the screen is gone. A common symptom is "setState() called after dispose()" errors, which usually means a stream or future callback outlived its widget.
For async work started by a widget, check mounted before using context or calling setState() after an await.
Do
- Pair every controller created in
initState()with a call indispose() - Check
mountedafter anawaitbefore touchingcontext
Avoid
- Creating controllers inside
build()
Example
Future<void> _save() async {
await repository.save(_form);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Saved')),
);
}