Skip to content

Explain asynchronous causes in the setState() called after dispose() error#187294

Merged
auto-submit[bot] merged 3 commits into
flutter:masterfrom
fahaddoc:improve-setstate-after-dispose-async
Jul 7, 2026
Merged

Explain asynchronous causes in the setState() called after dispose() error#187294
auto-submit[bot] merged 3 commits into
flutter:masterfrom
fahaddoc:improve-setstate-after-dispose-async

Conversation

@fahaddoc

Copy link
Copy Markdown
Contributor

Description

The setState() called after dispose() assertion currently lists only a timer or an animation callback as the situations that trigger it. In real apps the most common cause is an asynchronous gap: code awaits a Future (a network request, Future.delayed, etc.), the widget is removed from the tree while the work is in flight (for example, the route is popped), and the continuation then calls setState() on the now-defunct State.

This PR extends the existing ErrorDescription and the first ErrorHint to:

  • name the asynchronous-gap cause alongside timers / animation callbacks, and
  • point at the canonical fix — guarding setState() with a mounted check after an await (the pattern enforced by the use_build_context_synchronously lint).

No behavior changes — this only makes the diagnostic more actionable, especially for beginners (the concern raised in the issue).

Before

… This error can occur when code calls setState() from a timer or an animation callback.

After

… This error can occur when code calls setState() from a timer, from an animation callback, or after an asynchronous operation (such as an awaited network request or other Future) completes once the widget has already been removed from the tree.

…and the first hint now adds that checking mounted is "especially important after an await."

I deliberately did not embed a documentation URL in the message (suggested in the issue), since the framework error messages avoid inline links and the existing mounted guidance already states the remedy.

Related Issues

Fixes #177615

Tests

Added packages/flutter/test/widgets/framework_test.dartsetState() after dispose() error explains asynchronous causes, which asserts the thrown FlutterError message mentions the asynchronous cause and the await / mounted guidance. The existing Defunct setState throws exception test continues to pass; flutter analyze reports no issues and dart format reports no changes.

Pre-launch Checklist

@github-actions github-actions Bot added the framework flutter/packages/flutter repository. See also f: labels. label May 29, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the setState() error message in framework.dart to explain that the error can occur after asynchronous operations complete when the widget is no longer in the tree, and highlights checking the mounted property after an await. It also adds a test in framework_test.dart to verify this error message. Feedback suggests simplifying the phrasing of the error message to improve readability.

Comment on lines +1169 to +1171
'setState() from a timer, from an animation callback, or after an '
'asynchronous operation (such as an awaited network request or other '
'Future) completes once the widget has already been removed from the tree.',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The phrase "completes once the widget has already been removed" is slightly wordy and ambiguous. Simplifying this to "completes after the widget has been removed" makes the error message more direct and easier to read, which aligns with the Flutter philosophy of optimizing for readability and making error messages highly useful.

            'setState() from a timer, from an animation callback, or after an '\n            'asynchronous operation (such as an awaited network request or other '\n            'Future) completes after the widget has been removed from the tree.',
References
  1. Optimize for readability: Code is read more often than it is written. Error messages should be useful: Every error message is an opportunity to make someone love our product. (link)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 18f5595 — simplified to "...completes after the widget has been removed from the tree."

@Piinks
Piinks self-requested a review June 2, 2026 22:22
Piinks
Piinks previously approved these changes Jun 3, 2026

@Piinks Piinks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @fahaddoc thank you for contributing. This change LGTM

justinmc
justinmc previously approved these changes Jun 3, 2026

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a nit.

Comment on lines +1178 to +1180
'tree; this is especially important after an "await", since the widget '
'may have been removed from the tree while the asynchronous work was in '
'progress.',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick here: I think it's not "especially" true after an await. That case is on equal footing with all of the others that are mentioned. IMO this addition here is not necessary, and the information here is already covered in the ErorrDescription.

Are you worried that readers might not get the point that they should check mounted after await?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — await isn't special here, it's on equal footing with the timer and animation-callback cases, and the asynchronous cause is already spelled out in the ErrorDescription above. My intent was just to nudge people toward the mounted-after-await check, but since the description already covers it, the extra emphasis in the hint is redundant. I've dropped that addition (fb7904d) so the hint stays consistent across all the causes. Thanks for the catch!

@fahaddoc
fahaddoc dismissed stale reviews from justinmc and Piinks via fb7904d June 4, 2026 06:14
@justinmc
justinmc self-requested a review June 30, 2026 22:14
fahaddoc and others added 3 commits July 6, 2026 15:57
…error

The setState() called after dispose() error listed only timers and animation
callbacks as triggers. The most common real-world cause is an asynchronous gap:
an awaited Future completes after the widget was removed from the tree (for
example, after the route was popped). Extend the ErrorDescription and ErrorHint
to name that cause and point at the mounted-after-await fix (the pattern the
use_build_context_synchronously lint enforces). No behavior change.

Fixes flutter#177615
Use 'completes after the widget has been removed from the tree' instead of
'completes once the widget has already been removed from the tree' for a more
direct error message, per review feedback.
@Piinks
Piinks force-pushed the improve-setstate-after-dispose-async branch from ac99ee7 to 6ceb2ab Compare July 6, 2026 20:57

@Piinks Piinks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM again :)

@Piinks Piinks added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels Jul 6, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Jul 7, 2026
Merged via the queue into flutter:master with commit 8580e15 Jul 7, 2026
95 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jul 7, 2026
Rusino pushed a commit to Rusino/flutter that referenced this pull request Jul 7, 2026
…error (flutter#187294)

## Description

The `setState() called after dispose()` assertion currently lists only
*a timer or an animation callback* as the situations that trigger it. In
real apps the most common cause is an **asynchronous gap**: code
`await`s a `Future` (a network request, `Future.delayed`, etc.), the
widget is removed from the tree while the work is in flight (for
example, the route is popped), and the continuation then calls
`setState()` on the now-defunct `State`.

This PR extends the existing `ErrorDescription` and the first
`ErrorHint` to:
- name the asynchronous-gap cause alongside timers / animation
callbacks, and
- point at the canonical fix — guarding `setState()` with a `mounted`
check after an `await` (the pattern enforced by the
`use_build_context_synchronously` lint).

No behavior changes — this only makes the diagnostic more actionable,
especially for beginners (the concern raised in the issue).

**Before**
> … This error can occur when code calls setState() from a timer or an
animation callback.

**After**
> … This error can occur when code calls setState() from a timer, from
an animation callback, or after an asynchronous operation (such as an
awaited network request or other Future) completes once the widget has
already been removed from the tree.

…and the first hint now adds that checking `mounted` is *"especially
important after an `await`."*

I deliberately did **not** embed a documentation URL in the message
(suggested in the issue), since the framework error messages avoid
inline links and the existing `mounted` guidance already states the
remedy.

## Related Issues

Fixes flutter#177615

## Tests

Added `packages/flutter/test/widgets/framework_test.dart` →
**`setState() after dispose() error explains asynchronous causes`**,
which asserts the thrown `FlutterError` message mentions the
asynchronous cause and the `await` / `mounted` guidance. The existing
`Defunct setState throws exception` test continues to pass; `flutter
analyze` reports no issues and `dart format` reports no changes.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.

[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[CLA]: https://cla.developers.google.com/
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Jul 8, 2026
…12137)

Manual roll Flutter from 6995038d96ef to 91939cc4db78 (48 revisions)

Manual roll requested by [email protected]

flutter/flutter@6995038...91939cc

2026-07-07 [email protected] Roll Skia from 80ae1deed9ed to 075fbe4778d9 (6 revisions) (flutter/flutter#189067)
2026-07-07 [email protected] Bump customer testing version for new registrants (flutter/flutter#189070)
2026-07-07 [email protected] Roll Packages from fc00ceb to 92525f5 (2 revisions) (flutter/flutter#189069)
2026-07-07 [email protected] fix android semantics integration test hangs due to postframe callback (flutter/flutter#189043)
2026-07-07 [email protected] Bump goldctl (flutter/flutter#189030)
2026-07-07 [email protected] Roll Skia from 71db7c06c5fe to 80ae1deed9ed (2 revisions) (flutter/flutter#189060)
2026-07-07 [email protected] Collect analytics on AI agent usage (flutter/flutter#187630)
2026-07-07 [email protected] Roll Skia from 125a76cc4cf3 to 71db7c06c5fe (4 revisions) (flutter/flutter#189054)
2026-07-07 [email protected] Roll Dart SDK from 70577d6f2bba to c648e3429d76 (1 revision) (flutter/flutter#189052)
2026-07-07 [email protected] Fix RenderSliverPadding setter assertion (flutter/flutter#187353)
2026-07-07 [email protected] Roll Skia from 2dde156aec3e to 125a76cc4cf3 (3 revisions) (flutter/flutter#189037)
2026-07-07 [email protected] Explain asynchronous causes in the setState() called after dispose() error (flutter/flutter#187294)
2026-07-07 [email protected] Roll Skia from 7dccd1e79a5b to 2dde156aec3e (2 revisions) (flutter/flutter#189035)
2026-07-07 [email protected] Add `STRING_CATALOG_GENERATE_SYMBOLS = YES;` (flutter/flutter#188163)
2026-07-07 [email protected] Print warnings on Intel Macs / targeting Intel Macs (flutter/flutter#188835)
2026-07-07 [email protected] Roll Dart SDK from 4c03f64b19f6 to 70577d6f2bba (1 revision) (flutter/flutter#189031)
2026-07-07 [email protected] Roll Fuchsia Linux SDK from n1Lm2Z4DZkfC3bwj5... to 7RjQJBW3m-3Jl-7jr... (flutter/flutter#189032)
2026-07-06 [email protected] Recommend `OverlayPortal.overlayChildLayoutBuilder` over Target-Follower (flutter/flutter#188894)
2026-07-06 [email protected] Sync CHANGELOG.md from stable (flutter/flutter#189025)
2026-07-06 [email protected] Rename preferredSize to size and preferredConstraints to constraints in the windowing API (flutter/flutter#189017)
2026-07-06 [email protected] Skip locked issues in no-response check (flutter/flutter#188950)
2026-07-06 [email protected] Fix potential race condtion in android_semantics_integration_test (flutter/flutter#188830)
2026-07-06 [email protected] ci: udpate wait-for-engine-build composite action to cocoon (flutter/flutter#189020)
2026-07-06 [email protected] Roll Skia from 2b55eed5500d to 7dccd1e79a5b (3 revisions) (flutter/flutter#189021)
2026-07-06 [email protected] [AGP 9] Update Flutter Template Versions (flutter/flutter#188762)
2026-07-06 [email protected] fix(engine/windows): keep tooltip position rect alive - fix use after free (flutter/flutter#188476)
2026-07-06 [email protected] [Tool] Fix null check operator crash in currentPackageConfig (flutter/flutter#188454)
2026-07-06 [email protected] [Tool] Gracefully handle WDAC/AppLocker blocks for impellerc (flutter/flutter#188452)
2026-07-06 [email protected] [flutter_tools] Throw DaemonException if app fails to start early (flutter/flutter#188921)
2026-07-06 [email protected] Roll Dart SDK from ce7a2567eb59 to 4c03f64b19f6 (2 revisions) (flutter/flutter#189015)
2026-07-06 [email protected] [flutter_tools, engine] Add --disable-service-origin-check option to disable VM service origin checks (flutter/flutter#188745)
2026-07-06 [email protected] [flutter_tools] Fix test flakiness in widget_preview_detection_test.dart (flutter/flutter#188922)
2026-07-06 [email protected] Roll pub packages (flutter/flutter#189009)
2026-07-06 [email protected] Roll Skia from c0e2b9667d91 to 2b55eed5500d (1 revision) (flutter/flutter#189006)
2026-07-06 [email protected] Roll Packages from 2fbe873 to fc00ceb (1 revision) (flutter/flutter#189007)
2026-07-06 [email protected] Roll Skia from b68567542f81 to c0e2b9667d91 (1 revision) (flutter/flutter#189004)
2026-07-06 [email protected] Fixing multiline GetBoxesForRange (flutter/flutter#188803)
2026-07-06 [email protected] Roll Skia from e15ea7a4b927 to b68567542f81 (4 revisions) (flutter/flutter#189000)
2026-07-06 [email protected] Roll Skia from 79806692425f to e15ea7a4b927 (2 revisions) (flutter/flutter#188992)
2026-07-06 [email protected] Roll Skia from 2826ae6dc649 to 79806692425f (1 revision) (flutter/flutter#188991)
2026-07-06 [email protected] Roll Skia from 7f99eee20fd6 to 2826ae6dc649 (1 revision) (flutter/flutter#188990)
2026-07-06 [email protected] Add debugPaintFocusBoxes (flutter/flutter#188288)
2026-07-05 [email protected] Roll Fuchsia Linux SDK from bkK6BLiZDRnE7AQQj... to n1Lm2Z4DZkfC3bwj5... (flutter/flutter#188989)
2026-07-05 [email protected] Roll Skia from 75a4bf6706bd to 7f99eee20fd6 (2 revisions) (flutter/flutter#188988)
...
kalyujniy pushed a commit to brickit-app/camera that referenced this pull request Jul 8, 2026
…lutter#12137)

Manual roll Flutter from 6995038d96ef to 91939cc4db78 (48 revisions)

Manual roll requested by [email protected]

flutter/flutter@6995038...91939cc

2026-07-07 [email protected] Roll Skia from 80ae1deed9ed to 075fbe4778d9 (6 revisions) (flutter/flutter#189067)
2026-07-07 [email protected] Bump customer testing version for new registrants (flutter/flutter#189070)
2026-07-07 [email protected] Roll Packages from fc00ceb to 92525f5 (2 revisions) (flutter/flutter#189069)
2026-07-07 [email protected] fix android semantics integration test hangs due to postframe callback (flutter/flutter#189043)
2026-07-07 [email protected] Bump goldctl (flutter/flutter#189030)
2026-07-07 [email protected] Roll Skia from 71db7c06c5fe to 80ae1deed9ed (2 revisions) (flutter/flutter#189060)
2026-07-07 [email protected] Collect analytics on AI agent usage (flutter/flutter#187630)
2026-07-07 [email protected] Roll Skia from 125a76cc4cf3 to 71db7c06c5fe (4 revisions) (flutter/flutter#189054)
2026-07-07 [email protected] Roll Dart SDK from 70577d6f2bba to c648e3429d76 (1 revision) (flutter/flutter#189052)
2026-07-07 [email protected] Fix RenderSliverPadding setter assertion (flutter/flutter#187353)
2026-07-07 [email protected] Roll Skia from 2dde156aec3e to 125a76cc4cf3 (3 revisions) (flutter/flutter#189037)
2026-07-07 [email protected] Explain asynchronous causes in the setState() called after dispose() error (flutter/flutter#187294)
2026-07-07 [email protected] Roll Skia from 7dccd1e79a5b to 2dde156aec3e (2 revisions) (flutter/flutter#189035)
2026-07-07 [email protected] Add `STRING_CATALOG_GENERATE_SYMBOLS = YES;` (flutter/flutter#188163)
2026-07-07 [email protected] Print warnings on Intel Macs / targeting Intel Macs (flutter/flutter#188835)
2026-07-07 [email protected] Roll Dart SDK from 4c03f64b19f6 to 70577d6f2bba (1 revision) (flutter/flutter#189031)
2026-07-07 [email protected] Roll Fuchsia Linux SDK from n1Lm2Z4DZkfC3bwj5... to 7RjQJBW3m-3Jl-7jr... (flutter/flutter#189032)
2026-07-06 [email protected] Recommend `OverlayPortal.overlayChildLayoutBuilder` over Target-Follower (flutter/flutter#188894)
2026-07-06 [email protected] Sync CHANGELOG.md from stable (flutter/flutter#189025)
2026-07-06 [email protected] Rename preferredSize to size and preferredConstraints to constraints in the windowing API (flutter/flutter#189017)
2026-07-06 [email protected] Skip locked issues in no-response check (flutter/flutter#188950)
2026-07-06 [email protected] Fix potential race condtion in android_semantics_integration_test (flutter/flutter#188830)
2026-07-06 [email protected] ci: udpate wait-for-engine-build composite action to cocoon (flutter/flutter#189020)
2026-07-06 [email protected] Roll Skia from 2b55eed5500d to 7dccd1e79a5b (3 revisions) (flutter/flutter#189021)
2026-07-06 [email protected] [AGP 9] Update Flutter Template Versions (flutter/flutter#188762)
2026-07-06 [email protected] fix(engine/windows): keep tooltip position rect alive - fix use after free (flutter/flutter#188476)
2026-07-06 [email protected] [Tool] Fix null check operator crash in currentPackageConfig (flutter/flutter#188454)
2026-07-06 [email protected] [Tool] Gracefully handle WDAC/AppLocker blocks for impellerc (flutter/flutter#188452)
2026-07-06 [email protected] [flutter_tools] Throw DaemonException if app fails to start early (flutter/flutter#188921)
2026-07-06 [email protected] Roll Dart SDK from ce7a2567eb59 to 4c03f64b19f6 (2 revisions) (flutter/flutter#189015)
2026-07-06 [email protected] [flutter_tools, engine] Add --disable-service-origin-check option to disable VM service origin checks (flutter/flutter#188745)
2026-07-06 [email protected] [flutter_tools] Fix test flakiness in widget_preview_detection_test.dart (flutter/flutter#188922)
2026-07-06 [email protected] Roll pub packages (flutter/flutter#189009)
2026-07-06 [email protected] Roll Skia from c0e2b9667d91 to 2b55eed5500d (1 revision) (flutter/flutter#189006)
2026-07-06 [email protected] Roll Packages from 2fbe873 to fc00ceb (1 revision) (flutter/flutter#189007)
2026-07-06 [email protected] Roll Skia from b68567542f81 to c0e2b9667d91 (1 revision) (flutter/flutter#189004)
2026-07-06 [email protected] Fixing multiline GetBoxesForRange (flutter/flutter#188803)
2026-07-06 [email protected] Roll Skia from e15ea7a4b927 to b68567542f81 (4 revisions) (flutter/flutter#189000)
2026-07-06 [email protected] Roll Skia from 79806692425f to e15ea7a4b927 (2 revisions) (flutter/flutter#188992)
2026-07-06 [email protected] Roll Skia from 2826ae6dc649 to 79806692425f (1 revision) (flutter/flutter#188991)
2026-07-06 [email protected] Roll Skia from 7f99eee20fd6 to 2826ae6dc649 (1 revision) (flutter/flutter#188990)
2026-07-06 [email protected] Add debugPaintFocusBoxes (flutter/flutter#188288)
2026-07-05 [email protected] Roll Fuchsia Linux SDK from bkK6BLiZDRnE7AQQj... to n1Lm2Z4DZkfC3bwj5... (flutter/flutter#188989)
2026-07-05 [email protected] Roll Skia from 75a4bf6706bd to 7f99eee20fd6 (2 revisions) (flutter/flutter#188988)
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement] Improve developer feedback when setState is called after widget disposal

4 participants