# Flutter: “setState() called after dispose()”

> Flutter says setState() called after dispose(): a Future, timer or stream finished after the screen closed. Cancel it in dispose, or check mounted.

Canonical: https://app369.com/fix/flutter-setstate-called-after-dispose/
Last updated: 2026-11-08

The console shows setState() called after dispose(), with lifecycle state: defunct, not mounted. A screen started some work, such as an HTTP call, a Timer or a stream listener, and the user left before it finished. When the work completed, it called setState on a State object that Flutter had already removed.

**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**

1. 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**](https://api.flutter.dev/flutter/widgets/State/setState.html) 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);
}
```
2. 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**](https://api.flutter.dev/flutter/widgets/State/setState.html) 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();
}
```
3. 03 ### **A stream listener that was never cancelled**
  > “Subclasses should override this method to release any resources retained by this object” [**Flutter, State.dispose**](https://api.flutter.dev/flutter/widgets/State/dispose.html) 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();
}
```
4. 04 ### **A callback passed to another object**
  > “It is an error to call setState unless mounted is true.” [**Flutter, State.mounted**](https://api.flutter.dev/flutter/widgets/State/mounted.html) 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 November 8, 2026: [Flutter API: State.setState](https://api.flutter.dev/flutter/widgets/State/setState.html); [Flutter API: State.mounted](https://api.flutter.dev/flutter/widgets/State/mounted.html); [Flutter API: State.dispose](https://api.flutter.dev/flutter/widgets/State/dispose.html).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A 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**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**Flutter “Looking up a deactivated widget’s ancestor is unsafe”**](https://app369.com/fix/flutter-looking-up-a-deactivated-widgets-ancestor-is-unsafe/) · [**Flutter “setState() called during build”**](https://app369.com/fix/flutter-setstate-called-during-build/) · [**Flutter LateInitializationError**](https://app369.com/fix/flutter-lateinitializationerror-field-has-not-been-initialized/) · [**Flutter “Scaffold.of() called with a context”**](https://app369.com/fix/flutter-scaffold-of-called-with-a-context/)

Questions

## **More on this error**

<details>

<summary>**Is wrapping setState in if (mounted) enough? **+</summary>

It stops the error, but the timer, stream or request keeps running in the background. Cancel what you can in dispose(), and use the mounted check for one-off Futures you can’t cancel.

</details>

<details>

<summary>**Does it crash the app for users? **+</summary>

No. The check is a debug-mode assertion, so release builds don’t show it. The work it points at still runs after the user left and can hold memory, so fix it rather than ignore it.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. 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.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)