Short answer
Cancel the work in dispose(): call cancel() on Timers and StreamSubscriptions, and dispose controllers. For a Future you can’t cancel, check if (!mounted) return; after the await and before setState. Flutter’s docs prefer cancelling over the mounted check, because cancelled work stops using CPU.
What called setState too late, and the fix
01
A Future that finished after the user left
“It is an error to call this method after the framework calls dispose.” Flutter, State.setState
An await on an HTTP call or a database read takes a second; the user taps back; the call returns and runs setState. Check mounted right after each await that is followed by setState.
Future<void> _load() async { final orders = await api.fetchOrders(); if (!mounted) return; setState(() => _orders = orders); }02
A Timer or periodic Timer still running
“it is better practice to cancel whatever work might trigger the setState rather than merely checking for mounted before calling setState” Flutter, State.setState
Timer.periodic keeps firing after the screen is gone. Keep the Timer in a field and cancel it in dispose().
late final Timer _timer; @override void initState() { super.initState(); _timer = Timer.periodic(const Duration(seconds: 1), (_) => setState(() => _seconds++)); } @override void dispose() { _timer.cancel(); super.dispose(); }03
A stream listener that was never cancelled
“Subclasses should override this method to release any resources retained by this object” Flutter, State.dispose
stream.listen in initState returns a StreamSubscription. Without cancel(), it calls setState on every event after the screen closes, and keeps the State in memory. Keep the subscription and cancel it in dispose(), or use a StreamBuilder, which handles it for you.
late final StreamSubscription<User?> _sub; @override void initState() { super.initState(); _sub = auth.authStateChanges().listen((user) => setState(() => _user = user)); } @override void dispose() { _sub.cancel(); super.dispose(); }04
A callback passed to another object
“It is an error to call setState unless mounted is true.” Flutter, State.mounted
An AnimationController, an animation or controller listener, or a callback stored by a service can outlive the screen. Remove listeners in dispose() with removeListener, and dispose every AnimationController and other controller you created. Code that runs from a global service should check mounted before touching the State.
Checked against the vendor’s docs on : Flutter API: State.setState; Flutter API: State.mounted; Flutter API: State.dispose.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.See scope and start
Related: Flutter “Looking up a deactivated widget’s ancestor is unsafe” · Flutter “setState() called during build” · Flutter LateInitializationError · Flutter “Scaffold.of() called with a context”