-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Fix _debugElementWasRebuilt global key asserts
#105430
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
Fix _debugElementWasRebuilt global key asserts
#105430
Conversation
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.
Element.updateChild is also evidence that this is being rebuilt.
Instead of doing _debugElementWasRebuilt(context) in buildScope just for LayoutBuilder this feels slightly better?
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.
To me this feels out of place here. We should detect before this place that the element was rebuild because it may potentially not have any children.
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.
(same with the one below)
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.
because it may potentially not have any children.
that case is already taken care of here:
flutter/packages/flutter/lib/src/widgets/framework.dart
Lines 3562 to 3565 in 2aa348b
| assert(() { | |
| child.owner!._debugElementWasRebuilt(child); | |
| return true; | |
| }()); |
But the above line doesn't call _debugElementWasRebuilt on the root element Element that initiated the subtree update, like in LayoutBuilder:
| _child = updateChild(_child, built, null); |
With _debugElementWasRebuilt's current implementation it's OK for it to be called on the same Element multiple times in a frame.
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.
Another reason why this feels out of place here: technically you don't have to go through updateChild. You can just call the methods called from here directly and bypass updateChild completely.
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.
Hm, but I guess if you do that you miss also some of the other debug key-locig at the end of this method?
8cbca03 to
fd52cff
Compare
…bal key reparenting
fd52cff to
f894cc4
Compare
| try { | ||
| element.rebuild(); | ||
| assert(() { | ||
| _debugElementWasRebuilt(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.
This is duplication with the updateChild's 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.
Let _debugElementWasRebuilt returns true will simpler the assertion?
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.
rebuild doesn't necessarily call updateChild. The widget may not have a child.
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.
rebuilddoesn't necessarily callupdateChild. The widget may not have a child.
I mean if we check here, the new check you added to updateChild by this change is unnecessary.
But the check above when callback is non-null should retain.
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.
Could you elaborate? You mean the 2 _debugElementWasRebuilt calls at the end of updateChild are not necessary?
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.
Could you elaborate? You mean the 2
_debugElementWasRebuiltcalls at the end ofupdateChildare not necessary?
Right.
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.
LayoutBuilder doesn't call rebuild when it rebuilds in performLayout, nor does it parent call update on the layout builder so those are still needed. I don't think we want to call _debugElementWasRebuilt(context) in invokeLayoutCallback since it's not guaranteed that context will be rebuilt.
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.
LayoutBuilder doesn't call rebuild when it rebuilds in
performLayout, nor does it parent callupdateon the layout builder so those are still needed. I don't think we want to call_debugElementWasRebuilt(context)ininvokeLayoutCallbacksince it's not guaranteed thatcontextwill be rebuilt.
Add a test for this?
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 covered by existing tests I think. I can add one that specifically verifies that _debugElementWasRebuilt(context) is no longer being called and there's no false negatives because we removed _debugElementWasRebuilt(context) but that one feels a little bit too specific to be useful?
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.
We can add a test that failed #105492 to prevent regression by accident. : )
|
It looks like this is fixing two distinct issues? And there is no dependency between the two fixes? Maybe break this up into two separate PRs to make potential breakages easier to debug and roll-backs more targeted? |
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.
To me this feels out of place here. We should detect before this place that the element was rebuild because it may potentially not have any children.
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.
(same with the one below)
| List<S>? _debugPreviousSlots; | ||
|
|
||
| void _updateChildren() { | ||
| void _updateChildren(SlottedMultiChildRenderObjectWidgetMixin<S> newWidget) { |
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 newly added parameter appears unused?
| }(), '${widget.runtimeType}.slots must not change.'); | ||
| assert(slottedMultiChildRenderObjectWidgetMixin.slots.toSet().length == slottedMultiChildRenderObjectWidgetMixin.slots.length, 'slots must be unique'); | ||
|
|
||
| final Map<GlobalKey, S> globalKeyedSlots = _globalKeyToSlot; |
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.
Maybe rename this to oldGlobalKedSlots to avoid confusion?
| _updateChild(slottedMultiChildRenderObjectWidgetMixin.childForSlot(slot), slot); | ||
| final Widget? widget = slottedMultiChildRenderObjectWidgetMixin.childForSlot(slot); | ||
| final Key? newWidgetKey = widget?.key; | ||
| if (widget != null && newWidgetKey is GlobalKey) { |
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 now has me wondering: Don't we need to do this for all keys, not just globalkeys? If a keyed widget changes slots, it should be matched with the element from the previous slot it was in?
_debugElementWasRebuilt global key asserts & handle global key reparenting for SlottedMultiChildRenderObjectWidgetMixin _debugElementWasRebuilt global key asserts
|
Looking at this some more: The most obvious place to call |
|
|
You're right. That's probably the reason why we currently call I just experimented with this. If I only call |
|
I think we currently account for that by doing this:
But that doesn't seem to be the right place to do that. |
Byproduct of #105335
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.