Skip to content

Conversation

@LongCatIsLooong
Copy link
Contributor

Byproduct of #105335

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.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label Jun 6, 2022
Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Member

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)

Copy link
Contributor Author

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:

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.

Copy link
Member

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.

Copy link
Member

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?

try {
element.rebuild();
assert(() {
_debugElementWasRebuilt(element);
Copy link
Member

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?

Copy link
Member

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?

Copy link
Contributor Author

@LongCatIsLooong LongCatIsLooong Jun 6, 2022

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.

Copy link
Member

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.

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.

Copy link
Contributor Author

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?

Copy link
Member

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?

Right.

Copy link
Contributor Author

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.

Copy link
Member

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.

Add a test for this?

Copy link
Contributor Author

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?

Copy link
Member

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. : )

@LongCatIsLooong LongCatIsLooong requested a review from Hixie June 6, 2022 16:41
@goderbauer
Copy link
Member

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?

Copy link
Member

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.

Copy link
Member

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) {
Copy link
Member

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;
Copy link
Member

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) {
Copy link
Member

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?

@LongCatIsLooong LongCatIsLooong changed the title Fix _debugElementWasRebuilt global key asserts & handle global key reparenting for SlottedMultiChildRenderObjectWidgetMixin Fix _debugElementWasRebuilt global key asserts Jul 1, 2022
@goderbauer
Copy link
Member

Looking at this some more: The most obvious place to call _debugElementWasRebuilt would be in Element.rebuild right after we called Element.performRebuild instead of calling it from all these places (including the already existing ones). From what I can tell, that would fix the test case included in this PR (not sure if other things would break, though). I am wondering why we didn't just call it from there when this was first introduced...

@LongCatIsLooong
Copy link
Contributor Author

RenderObjectElement doesn't call rebuild in its update I think?

@goderbauer
Copy link
Member

RenderObjectElement doesn't call rebuild in its update I think?

You're right. That's probably the reason why we currently call _debugElementWasRebuilt after calling Element.update in updateChild.

I just experimented with this. If I only call _debugElementWasRebuilt from Element.update and Element.rebuild all tests (including the one from this PR) seem to pass. That actually seems to me the cleanest places to call this if we don't come up with a reason why that would be bad... WDYT about this approach?

@LongCatIsLooong
Copy link
Contributor Author

LongCatIsLooong commented Jul 15, 2022

LayoutBuilder would be an exception to this, it rebuilds itself in a layout callback and doesn't call either of this on itself.

_child = updateChild(_child, built, null);

I think we currently account for that by doing this:

_debugElementWasRebuilt(context);

But that doesn't seem to be the right place to do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants