Fix: setState() called after dispose() in Flutter
Why you get 'setState() called after dispose()' and three proven ways to fix it — with code examples for async operations, streams, and timers.
Fix: setState() called after dispose()
setState() called after dispose(): _MyWidgetState#12345
This error means you're calling setState() on a widget that's already been removed from the widget tree. It's a lifecycle timing issue, and it's very common with async operations.
Why It Happens
When a widget is disposed (user navigated away, parent rebuilt without it), the State object is marked as unmounted. But if you have a pending Future, Stream, or Timer, its callback will still fire — and call setState() on a dead widget.
Fix 1: Check mounted Before setState
The simplest fix:
Future<void> _loadData() async {
final result = await api.fetchData();
if (!mounted) return; // Widget was disposed during the await
setState(() {
_data = result;
});
}Fix 2: Cancel Subscriptions in dispose()
For streams and timers:
class _MyWidgetState extends State<MyWidget> {
StreamSubscription? _subscription;
Timer? _timer;
@override
void initState() {
super.initState();
_subscription = stream.listen((data) {
setState(() => _items = data);
});
_timer = Timer.periodic(Duration(seconds: 5), (_) {
setState(() => _counter++);
});
}
@override
void dispose() {
_subscription?.cancel();
_timer?.cancel();
super.dispose();
}
}Fix 3: Use a Cancelable Completer
For complex async chains:
class _MyWidgetState extends State<MyWidget> {
CancelableOperation<Data>? _operation;
void _fetchData() {
_operation?.cancel();
_operation = CancelableOperation.fromFuture(
api.fetchData(),
);
_operation!.value.then((data) {
if (mounted) setState(() => _data = data);
});
}
@override
void dispose() {
_operation?.cancel();
super.dispose();
}
}InkPal Auto-Fix
InkPal detects this pattern and adds the mounted guard automatically:
inkpal.self_heal()
// Detects: setState after dispose pattern
// Fix: Adds mounted check before every setState in async context
Prevention Rules
- Every
awaitin a State method needs amountedcheck after it - Every
StreamSubscriptionneeds cancellation indispose() - Every
Timerneeds cancellation indispose() - Consider using
StatefulWidgetalternatives (Riverpod, BLoC) that handle this automatically
Look up any Flutter error at /errors — free, instant, no signup required.