-
Notifications
You must be signed in to change notification settings - Fork 28.6k
Do not rebuild Routes when a new opaque Route is pushed on top #48900
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
Conversation
@Hixie Even though I had to update framework tests for this change I would like to handle it as a non-breaking change. Can you grant an exception? The changes to the tests were to update error messages and error-message-like output. In one instance I had to change a type due to the changed internals of _Theatre. |
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.
FWIW, I agree changing the tests shouldn't be considered breaking here.
I may be missing something, but the skipCount seems sketchy to me. I think we shoudl at least be asserting that the children we think are offstage are in fact offstage, and vice-versa.
Is there ever a case where an opaque route should still layout other routes? Could we have a route that doesn't cover the whole view?
children: onstageChildren.reversed.toList(growable: false), | ||
), | ||
offstage: offstageChildren, | ||
skipCount: children.length - onstageCount, |
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 seems like it would fail if the logic above added children in an order like [offstage, onstage, offstage, offstage] for some reason.
If we think that's invalid, we should probably assert it. If it's actually valid we need different logic here.
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.
Doing something like [offstage, onstage, offstage, offstage] is not supported by the overlay. The top x entries are always onstage, everything else is either completely ignored or - if maintainState is true - rendered offstage.
Not sure what we should assert here. The assert would basically duplicate the algorithm above.
@override | ||
void debugVisitOnstageChildren(ElementVisitor visitor) { | ||
assert(children.length >= widget.skipCount); | ||
children.skip(widget.skipCount).forEach(visitor); |
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.
Similar concern - I think we at least want an assert here that all the widgets we're skipping are in fact offstage.
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 the other way around, though: The fact that we are skipping them makes them offstage. There's no other marker that marks them as offstage that we could assert on.
int get skipCount => _skipCount; | ||
int _skipCount; | ||
set skipCount(int value) { | ||
assert(value != null); |
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.
assert that the children we're skipping are offstage?
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 answer as above: The fact that we are skipping them makes them offstage. Not the other way around.
); | ||
if (isHit) | ||
return true; | ||
child = childParentData.previousSibling; |
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.
assert the previous sibling is onstage?
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.
The definition of the (private) _Theatre widget is: "give me a list of children and a number n. I'll not layout/render the first n children (effectively putting them offstage)." The number n defines what is going to be offstage. There's no other mechanism to tell if a child should be offstage or not... |
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
I haven't reviewed the test of the patch (I'm on my phone) but the test changes call be considered exempt from the breaking change policy as they are only testing internals. |
Wow I'm so used to seeing posts like that linked to a today bug due to a regression. Nice! |
If we were to reland this PR, make sure to address the issue mentioned in #49318, where the order between multiple dependencies are not consistent. They might change due to an irrelevant change, or be different between platforms. |
Hello, is this released? |
Also known as: Don't rebuild OverlayEntries when one changes opaqueness.
Description
Prior to this change we were re-arranging the tree when an OverlayEntry went from onstage to offstage (due to opaqueness changes) and vice versa:
flutter/packages/flutter/lib/src/widgets/overlay.dart
Lines 461 to 467 in 4f9b6cf
Since the OverlayEntries have a global key their corresponding element was deactivated at the old location inside the Stack and re-activated at the new location outside the Stack. This caused all StatefulWidgets within that subtree to rebuild because their ancestor had changed.
This change avoids the rebuild by not re-arranging the tree. Instead, both onstage and offstage OverlayEntries are children of the _Theatre, so they don't have to be moved around in the tree. The RenderObject of the _Theatre implements a special version of a Stack that just ignores the offstage children during rendering and layout. (Since the offstage children are always all at the beginning of the child list, this is implemented by providing an integer skipCount indicating the number of children to skip.)
The unnecessary rebuilds were most commonly felt when pushing new Routes because Navigator uses an Overlay internally.
Related Issues
Fixes #45797.
Tests
I added the following tests:
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze --flutter-repo
) does not report any problems on my PR.Breaking Change
Did any tests fail when you ran them? Please read Handling breaking changes.