SIGN IN SIGN UP

Skip removed observers during app exit requests (#190343)

`WidgetsBinding.handleRequestAppExit` iterates over a snapshot of
registered observers so callbacks can add or remove observers without
causing concurrent modification.

When an earlier asynchronous observer callback causes a later
`AppLifecycleListener` to be disposed, the disposed listener remains in
that snapshot and is invoked. In debug mode this reports a
use-after-dispose assertion; in release mode it runs the stale callback.

This change checks that each observer is still registered immediately
before invoking it. It preserves snapshot iteration for reentrancy
safety while skipping observers removed during an earlier asynchronous
callback.

The regression test pauses the first exit callback, disposes a second
listener while the first callback is pending, resumes dispatch, and
verifies that the disposed listener is not called.

Fixes #189472.

## Tests

- `./bin/flutter analyze packages/flutter/lib/src/widgets/binding.dart`
- `./bin/flutter analyze
packages/flutter/test/widgets/app_lifecycle_listener_test.dart`
- `./bin/flutter test --reporter compact
packages/flutter/test/widgets/app_lifecycle_listener_test.dart
packages/flutter/test/widgets/binding_test.dart`

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making.
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[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
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Elliott Brooks <21270878+elliette@users.noreply.github.com>
Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
K
kyungilcho committed
0cb3b7e22d9b5f729eea15d5f4cbd953d32a7f06
Parent: 6ec64b3
Committed by GitHub <noreply@github.com> on 8/24/2026, 11:03:00 PM