-
Notifications
You must be signed in to change notification settings - Fork 29.1k
Add error handling for Element
lifecycle user callbacks
#173148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add error handling for Element
lifecycle user callbacks
#173148
Conversation
@@ -2059,7 +2059,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge | |||
assert(child._parent == this); | |||
assert(child.attached == attached); | |||
assert(child.parentData != null); | |||
if (!(_isRelayoutBoundary ?? true)) { | |||
if (!(child._isRelayoutBoundary ?? true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated change. This is a typo from #169958
There was a problem hiding this comment.
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 introduces robust error handling for Element
lifecycle callbacks, specifically for deactivate
, to prevent framework state corruption when user-provided code throws an exception. The use of try...finally
blocks to ensure critical framework logic executes is a solid approach. The refactoring of Element.deactivate
into a public hook and a private implementation is also a clean and effective pattern. The new tests are valuable and cover the intended failure scenarios well. I have one minor suggestion in a test file to remove a redundant line.
@@ -5757,6 +5777,10 @@ abstract class ComponentElement extends Element { | |||
], | |||
), | |||
); | |||
// _Make sure the old child subtree are deactivated and disposed. | |||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't enough. If you inflate a widget during the process and a descendant Element fails to activate, the current pattern in framework.dart is to replace the entire subtree rooted at the performRebuild
call with an ErrorWidget. So some inflated elements are still active (since the inflation process was partially successful) even if they never get incorporated into the tree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this only a best effort thing? should we have a catch or finally to clean up the reset of tree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I guess if activation fails we should also build the ErrorWidget
to replace the subtree that actually failed to re-activate instead of that subtree that we're trying to inflate. Updating.
Edit: I ended up just deactivating the whole tree that will be replaced by this ErrorWidget
.
try { | ||
element.deactivate(); | ||
} catch (_) { | ||
element._ensureDeactivated(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may potentially be called twice if the crash is in _ensureDeactivated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't think this is an issue though, but may make stack trace a bit messy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried removing the "ensure" part out of deactivate
but one of our tests is calling deactivate
directly so I couldnt. See #173148 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I think this is fine
@@ -5757,6 +5777,10 @@ abstract class ComponentElement extends Element { | |||
], | |||
), | |||
); | |||
// _Make sure the old child subtree are deactivated and disposed. | |||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this only a best effort thing? should we have a catch or finally to clean up the reset of tree?
return newChild; | ||
} | ||
} catch (_) { | ||
// Attempt to do some clean-up if activation or mount fails to leave tree in a reasonable state. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous implementation only tries to do that if _activateWithparent
fails.
@@ -4708,11 +4721,21 @@ abstract class Element extends DiagnosticableTree implements BuildContext { | |||
/// Implementations of this method should end with a call to the inherited | |||
/// method, as in `super.deactivate()`. | |||
@mustCallSuper | |||
@visibleForOverriding |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the annotation because I was trying to move _ensureDeactivated
out, but since nothing is failing I'll keep the annotation (same as activate).
/// build phase. | ||
inactive, | ||
|
||
/// The [Element] encountered a unrecoverable error while being rebuilt when it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The [Element] encountered a unrecoverable error while being rebuilt when it | |
/// The [Element] encountered an unrecoverable error while being rebuilt when it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are great docs btw, thanks for adding them :)
/// This indicates the [Element]'s subtree is in an inconsistent state and must | ||
/// not be re-incorporated into the tree again. | ||
/// | ||
/// When a unrecoverable error is encountered, the framework calls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// When a unrecoverable error is encountered, the framework calls | |
/// When an unrecoverable error is encountered, the framework calls |
I introduced a new lifecycle state for Elements that failed to mount / activate, because while we still need to try to deactivate such Elements, we may not need to dispose them or rethrow exceptions encountered during deactivation like other Elements, so the new state is used by the framework to decide which callpath to take. I decided not to dispose these Elements because they might have never called |
element._ensureDeactivated(); | ||
// Do not rethrow: | ||
// The subtree has already thrown a different error and the framework is | ||
// cleaning up on a best-effort basis. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "best-effort basis" here may sound a bit scary, as the failing widget subtree may end up with different states with assertions enabled / disabled (and the framework don't rethrow). But I think the goal is to make sure the Elements are in the correct state as far as the framework is concerned when something fails, to reduce the noise in the crash report. Maybe we should only call _ensureDeactivated
instead so we don't invoke user deactivate callbacks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
widget subtree may end up with different states with assertions enabled / disabled
won't they all become failed at the end?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh by "end up with different states" I didn't mean ElementLifecycleState
. I was just a bit worried that in debug mode some calls could have been skipped because of an assertion fired in the deactivate implementation so it returned early (and the failures are silenced), but in production these calls will actually happen which can do more damages. But now I think about it if the developer saw that bug in debug mode they should just fix that unhandled exception.
Also I tried _ensureDeactivated
it ended up throwing a brunch of other errors during unmount in some tests so I'll keep the current implementation.
} | ||
} | ||
|
||
static void _deactivateFailedSubtreeRecursively(Element element) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be call during production as well? I think so, but just want to double check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
/// This is one of the two final stages of the element lifecycle and is not | ||
/// reversible. Reaching this state typically means that a widget implementation | ||
/// is throwing unhandled exceptions that need to be properly handled. | ||
failed, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
failedToActivate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly happen ad the end of this? do they just hang there without getting disposed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes because we don't know what to do with them, they will be stuck in a limbo. Some may have failed before initState was called so these Elements are not disposable, and others may have not gone through proper deactivation. The decision to not dispose them is pretty arbitrary, but since these elements are in inconsistent states I don't know if one option will be better than other, as they will be equally bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I prefer the current name since it could also be fail to deactivate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a code path to put element in this state when they fail to deactivate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, in _InactiveElements._deactivateRecursively
, Element._deactivateFailedSubtreeRecursively(element);
is called if the element fails to deactivate (and the deactivation exception will be rethrown).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotcha, thanks
try { | ||
_deactivateRecursively(element); | ||
} finally { | ||
_elements.add(element); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the element failed to deactivate do we still want to add it to the elements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point I restructured the deactivate flow, now I don't think an element
will be added to the list if it fails to deactivate.
return updatedChild!; | ||
} else { | ||
newChild.mount(this, newSlot); | ||
assert(newChild._lifecycleState == _ElementLifecycle.active); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't be _ElementLifecycle.failed
and go to catch case right? maybe we should assert that in catch case. I don't think that is possible, but probably should be safe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With asserts enabled, the new code would still rethrow the assert like it should if the resulting lifecycle state is not active? (maybe I'm not understanding the question correctly)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the lifecycle is anything other than active, this will go to the catch and go through the _deactivateFailedChildSilently
. I was just thinking that in the catch body before it goes o _deactivateFailedChildSilently, can we add an assert(newChild._lifecycleState != _ElementLifecycle.failed)
to make sure an element doesn't somhow get _deactivateFailedChildSilently
twice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh you mean adding assert(newChild._lifecycleState != _ElementLifecycle.failed)
to the catch clause below? But if that assert fires if will prevent the "real" exception from being rethrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably fine for something to get _deactivateFailedChildSilently
'd twice (but it seems unlikely)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah this is fine.
assert(newChild._lifecycleState == _ElementLifecycle.active); | ||
return newChild; | ||
} | ||
} catch (_) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we use (_)
in our repo? i thought this is against style guide
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using wildcard in pattern matching is fine? Here I'd consider the _
to be a wildcard pattern instead of anonymous argument since the catch
clause is pretty pattern-match-y.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I looked at dart's syntax again:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
It doesn't look like that _
is being used as a wildcard identifier, as the pattern matching is expressed in the on
clause. So the (_)
here probably is an anonymous identifier / argument.
But I think I just copied that from the existing implementation:
flutter/packages/flutter/lib/src/widgets/framework.dart
Lines 4529 to 4536 in 1c0ee96
} catch (_) { | |
// Attempt to do some clean-up if activation fails to leave tree in a reasonable state. | |
try { | |
deactivateChild(newChild); | |
} catch (_) { | |
// Clean-up failed. Only surface original exception. | |
} | |
rethrow; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried replacing _
with an identifier there's an "emply_catch_block" lint that doesn't like that change. Also the style guide says:
Provide full type information and names even for parameters that are otherwise unused. This makes it easier for people reading the code to tell what is actually going on (e.g. what is being ignored).
But catch (e)
isn't any more informative than catch (_)
I think.
element._ensureDeactivated(); | ||
// Do not rethrow: | ||
// The subtree has already thrown a different error and the framework is | ||
// cleaning up on a best-effort basis. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
widget subtree may end up with different states with assertions enabled / disabled
won't they all become failed at the end?
_deactivateRecursively(element); | ||
// This element is only added to _elements if the whole subtree is | ||
// successfully deactivated. | ||
_elements.add(element); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a descendant fails to deactivate, the root won't be marked as .failed
here (it's still .inactive
). The caller (of Element.deactivateChild
) may catch the exception and marked the entire subtree as failed like inflateWidget
does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the caller doesn't do that it should still be fine. The element will get stuck in the inactive state forever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking at the updateChild, it only mark the newChild to failed, instead of existing child (which got deactivated). or did I miss something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry can you point me to the line that this is happening? IIRC updateChild
doesn't have any try ... catch
blocks in it so it simply rethrows everything. If a descendant element of the existing child fails to deactivate the updateChild
method is going to return immediately, so it won't even get a chance to call inflateWidget
to create the new child as far as I can tell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nvm, this is answered in other thread
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replied to some of the thread. I think the code looks mostly fine, just some questions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…3148) This is for flutter#172289, but since this fix is speculative so I'll wait for the confirmation from the original reporters before closing the issue. As a bonus this fixes flutter#65655 The framework Element rebuild logic relies heavily on `Element._lifecycleState` being correct. When a user-supplied lifecycle callback (e.g., `State.deactivate`) fails the framework currently only ensures that every `Element` in the tree has the right lifecycle state, so an out-of-tree `Element` that is supposed to be disposed may still have an `active` state and continue being rebuilt by the BuildScope (because it's in the dirty list). See the comments in flutter#172289 Also related: flutter#100777 Internal: b/425298525 b/431537277 b/300829376 b/415724119 b/283614822 # TODO (in a different PR) The original issue could also be caused by incorrect `Element.updateChild` calls. If an `Element` subclass calls `Element.updateChild` to add child but forgets to update its child list accordingly (such that `visitChildren` misses that child), you'll get a child Element that thinks it's a child of the parent but the parent doesn't recognize the child so won't take that child into account during reparenting or unmounting. This is a programmer error that we should try to catch in the framework. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [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 [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [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/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
flutter/flutter@f4334d2...52af7a5 2025-08-15 engine-flutter-autoroll@skia.org Roll Packages from 09533b7 to 5c52c55 (6 revisions) (flutter/flutter#173854) 2025-08-15 engine-flutter-autoroll@skia.org Roll Skia from 46ec77ae3954 to 5654ac32ede0 (1 revision) (flutter/flutter#173848) 2025-08-15 engine-flutter-autoroll@skia.org Roll Skia from 162f47d6b6bd to 46ec77ae3954 (2 revisions) (flutter/flutter#173833) 2025-08-15 engine-flutter-autoroll@skia.org Roll Dart SDK from c7faab270f27 to cc008dc8e7aa (2 revisions) (flutter/flutter#173826) 2025-08-15 engine-flutter-autoroll@skia.org Roll Skia from ad5d04000101 to 162f47d6b6bd (5 revisions) (flutter/flutter#173822) 2025-08-15 jason-simmons@users.noreply.github.com Update the RBE configuration for the recent Clang update (flutter/flutter#173803) 2025-08-15 matanlurey@users.noreply.github.com Stop writing legacy `FLUTTER_ROOT/version` file (by default?) (flutter/flutter#172793) 2025-08-15 matanlurey@users.noreply.github.com Remove `luci_flags.parallel_download_builds` and friends (flutter/flutter#173799) 2025-08-14 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Predictive back route transitions by default (#165832)" (flutter/flutter#173809) 2025-08-14 engine-flutter-autoroll@skia.org Roll Skia from dca5f05fee87 to ad5d04000101 (8 revisions) (flutter/flutter#173798) 2025-08-14 mdebbar@google.com [web] Cleanup usages of deprecated `routeUpdated` message (flutter/flutter#173782) 2025-08-14 ahmedsameha1@gmail.com Make sure that DataTable, DataColumn, DataRow, and DataCell don't crash in 0x0 environment (flutter/flutter#173515) 2025-08-14 ahmedsameha1@gmail.com Make sure that a TableRowInkWell doesn't crash in 0x0 environment (flutter/flutter#173627) 2025-08-14 ahmedsameha1@gmail.com Make sure that a DatePickerDialog doesn't crash in 0x0 environment (flutter/flutter#173677) 2025-08-14 robert.ancell@canonical.com Return result of setting OpenGL contexts back to Flutter (flutter/flutter#173757) 2025-08-14 matanlurey@users.noreply.github.com Read `bin/cache/flutter.version.json` instead of `version` for `flutter_gallery` (flutter/flutter#173797) 2025-08-14 jmccandless@google.com Predictive back route transitions by default (flutter/flutter#165832) 2025-08-14 houssemeddinefadhli81@gmail.com feat: add onLongPressUp callback to InkWell widget (flutter/flutter#173221) 2025-08-14 engine-flutter-autoroll@skia.org Roll Dart SDK from 214a7f829913 to c7faab270f27 (1 revision) (flutter/flutter#173792) 2025-08-14 31859944+LongCatIsLooong@users.noreply.github.com Add error handling for `Element` lifecycle user callbacks (flutter/flutter#173148) 2025-08-14 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from I1TfNmsqTp7t3rO8e... to zWRpLglb48zC1vZLv... (flutter/flutter#173784) 2025-08-14 jhy03261997@gmail.com [Range slider] Tap on active range, the thumb closest to the mouse cursor should move to the cursor position. (flutter/flutter#173725) 2025-08-14 engine-flutter-autoroll@skia.org Roll Packages from 6cb9113 to 09533b7 (4 revisions) (flutter/flutter#173789) 2025-08-14 ttankkeo112@gmail.com Implements the Android native stretch effect as a fragment shader (Impeller-only). (flutter/flutter#169293) 2025-08-14 matanlurey@users.noreply.github.com Sync `CHANGELOG.md` (3.35 -> `master`) (flutter/flutter#173790) 2025-08-14 victorsanniay@gmail.com [VPAT][A11y] Announce Autocomplete search results status (flutter/flutter#173480) 2025-08-14 bruno.leroux@gmail.com Fix InputDecorator label padding (flutter/flutter#173344) 2025-08-14 edwinzn9@gmail.com Fix default minimumSize in dropdownMenu when maximumSize is null (flutter/flutter#169438) 2025-08-14 matanlurey@users.noreply.github.com Thread sub-builders for every engine-uploading builder (flutter/flutter#173742) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC louisehsu@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…3148) This is for flutter#172289, but since this fix is speculative so I'll wait for the confirmation from the original reporters before closing the issue. As a bonus this fixes flutter#65655 The framework Element rebuild logic relies heavily on `Element._lifecycleState` being correct. When a user-supplied lifecycle callback (e.g., `State.deactivate`) fails the framework currently only ensures that every `Element` in the tree has the right lifecycle state, so an out-of-tree `Element` that is supposed to be disposed may still have an `active` state and continue being rebuilt by the BuildScope (because it's in the dirty list). See the comments in flutter#172289 Also related: flutter#100777 Internal: b/425298525 b/431537277 b/300829376 b/415724119 b/283614822 # TODO (in a different PR) The original issue could also be caused by incorrect `Element.updateChild` calls. If an `Element` subclass calls `Element.updateChild` to add child but forgets to update its child list accordingly (such that `visitChildren` misses that child), you'll get a child Element that thinks it's a child of the parent but the parent doesn't recognize the child so won't take that child into account during reparenting or unmounting. This is a programmer error that we should try to catch in the framework. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [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 [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [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/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…r#9832) flutter/flutter@f4334d2...52af7a5 2025-08-15 engine-flutter-autoroll@skia.org Roll Packages from 09533b7 to 5c52c55 (6 revisions) (flutter/flutter#173854) 2025-08-15 engine-flutter-autoroll@skia.org Roll Skia from 46ec77ae3954 to 5654ac32ede0 (1 revision) (flutter/flutter#173848) 2025-08-15 engine-flutter-autoroll@skia.org Roll Skia from 162f47d6b6bd to 46ec77ae3954 (2 revisions) (flutter/flutter#173833) 2025-08-15 engine-flutter-autoroll@skia.org Roll Dart SDK from c7faab270f27 to cc008dc8e7aa (2 revisions) (flutter/flutter#173826) 2025-08-15 engine-flutter-autoroll@skia.org Roll Skia from ad5d04000101 to 162f47d6b6bd (5 revisions) (flutter/flutter#173822) 2025-08-15 jason-simmons@users.noreply.github.com Update the RBE configuration for the recent Clang update (flutter/flutter#173803) 2025-08-15 matanlurey@users.noreply.github.com Stop writing legacy `FLUTTER_ROOT/version` file (by default?) (flutter/flutter#172793) 2025-08-15 matanlurey@users.noreply.github.com Remove `luci_flags.parallel_download_builds` and friends (flutter/flutter#173799) 2025-08-14 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Predictive back route transitions by default (#165832)" (flutter/flutter#173809) 2025-08-14 engine-flutter-autoroll@skia.org Roll Skia from dca5f05fee87 to ad5d04000101 (8 revisions) (flutter/flutter#173798) 2025-08-14 mdebbar@google.com [web] Cleanup usages of deprecated `routeUpdated` message (flutter/flutter#173782) 2025-08-14 ahmedsameha1@gmail.com Make sure that DataTable, DataColumn, DataRow, and DataCell don't crash in 0x0 environment (flutter/flutter#173515) 2025-08-14 ahmedsameha1@gmail.com Make sure that a TableRowInkWell doesn't crash in 0x0 environment (flutter/flutter#173627) 2025-08-14 ahmedsameha1@gmail.com Make sure that a DatePickerDialog doesn't crash in 0x0 environment (flutter/flutter#173677) 2025-08-14 robert.ancell@canonical.com Return result of setting OpenGL contexts back to Flutter (flutter/flutter#173757) 2025-08-14 matanlurey@users.noreply.github.com Read `bin/cache/flutter.version.json` instead of `version` for `flutter_gallery` (flutter/flutter#173797) 2025-08-14 jmccandless@google.com Predictive back route transitions by default (flutter/flutter#165832) 2025-08-14 houssemeddinefadhli81@gmail.com feat: add onLongPressUp callback to InkWell widget (flutter/flutter#173221) 2025-08-14 engine-flutter-autoroll@skia.org Roll Dart SDK from 214a7f829913 to c7faab270f27 (1 revision) (flutter/flutter#173792) 2025-08-14 31859944+LongCatIsLooong@users.noreply.github.com Add error handling for `Element` lifecycle user callbacks (flutter/flutter#173148) 2025-08-14 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from I1TfNmsqTp7t3rO8e... to zWRpLglb48zC1vZLv... (flutter/flutter#173784) 2025-08-14 jhy03261997@gmail.com [Range slider] Tap on active range, the thumb closest to the mouse cursor should move to the cursor position. (flutter/flutter#173725) 2025-08-14 engine-flutter-autoroll@skia.org Roll Packages from 6cb9113 to 09533b7 (4 revisions) (flutter/flutter#173789) 2025-08-14 ttankkeo112@gmail.com Implements the Android native stretch effect as a fragment shader (Impeller-only). (flutter/flutter#169293) 2025-08-14 matanlurey@users.noreply.github.com Sync `CHANGELOG.md` (3.35 -> `master`) (flutter/flutter#173790) 2025-08-14 victorsanniay@gmail.com [VPAT][A11y] Announce Autocomplete search results status (flutter/flutter#173480) 2025-08-14 bruno.leroux@gmail.com Fix InputDecorator label padding (flutter/flutter#173344) 2025-08-14 edwinzn9@gmail.com Fix default minimumSize in dropdownMenu when maximumSize is null (flutter/flutter#169438) 2025-08-14 matanlurey@users.noreply.github.com Thread sub-builders for every engine-uploading builder (flutter/flutter#173742) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC louisehsu@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
This is for #172289, but since this fix is speculative so I'll wait for the confirmation from the original reporters before closing the issue.
As a bonus this fixes #65655
The framework Element rebuild logic relies heavily on
Element._lifecycleState
being correct. When a user-supplied lifecycle callback (e.g.,State.deactivate
) fails the framework currently only ensures that everyElement
in the tree has the right lifecycle state, so an out-of-treeElement
that is supposed to be disposed may still have anactive
state and continue being rebuilt by the BuildScope (because it's in the dirty list). See the comments in #172289Also related:
#100777
Internal:
b/425298525 b/431537277
b/300829376 b/415724119 b/283614822
TODO (in a different PR)
The original issue could also be caused by incorrect
Element.updateChild
calls. If anElement
subclass callsElement.updateChild
to add child but forgets to update its child list accordingly (such thatvisitChildren
misses that child), you'll get a child Element that thinks it's a child of the parent but the parent doesn't recognize the child so won't take that child into account during reparenting or unmounting. This is a programmer error that we should try to catch in the framework.Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assist
bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.