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