DEV3 Technologies home

DEV3 Developer Forum

How do I fix "setState() called after dispose()" in Flutter?

Flutter helpSolved0 views
dev3 ·

Navigating back while a request is running logs:

setState() called after dispose(): _MyPageState#1234(lifecycle state: defunct, not mounted)

It happens after an await in a method. How do I prevent it properly?

#flutter#state#async

0

1 reply

dev3 · Accepted answer

Cause

The async call finishes after the user left the screen, and the code then calls setState on a State that was already disposed.

Fix 1: guard with mounted

Future<void> _load() async {
  final data = await api.fetch();
  if (!mounted) return;
  setState(() => _data = data);
}

Check mounted after every await that precedes setState or context usage.

Fix 2: cancel what you started

Timers, stream subscriptions and animation controllers must be cancelled in dispose:

late final StreamSubscription _sub;
Timer? _timer;

@override
void initState() {
  super.initState();
  _sub = stream.listen((v) { if (mounted) setState(() => _v = v); });
  _timer = Timer.periodic(const Duration(seconds: 5), (_) => _poll());
}

@override
void dispose() {
  _sub.cancel();
  _timer?.cancel();
  super.dispose();
}

For HTTP calls, Dio supports CancelToken which you cancel in dispose.

Fix 3: do not keep the logic in the widget

Move the loading to a Riverpod provider, Bloc or ChangeNotifier owned above the page. The widget only watches state, so there is nothing to call setState on, and results are not lost when the user comes back.

Pitfalls

  • The message is not harmful by itself in release builds, but it signals a leak (a stream or timer that keeps running and holding the State alive).
  • Do not create controllers (TextEditingController, AnimationController) without disposing them.
  • initState must not be async; call an async method from it instead.
0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.