Skip to content

Conversation

@goderbauer
Copy link
Member

@goderbauer goderbauer commented Nov 16, 2020

For google3 rollers: This change requires dnfield/flutter_svg#450 and cl/343539658.

Description

Fixes #48679 by implementing the solution outlined in #48679 (comment). Going forward, authors of RenderBox subclasses will have to implement a new computeDryLayout method, which calculates nondestructively the Size that the RenderBox would like to be under the given set of constraints.

Furthermore, performResize is now implemented by calling the new computeDryLayout method and does not have to be overridden in subclasses anymore.

Last, but not least, this change adds a computeSizeForNoChild method to RenderProxyBoxMixin, which is called from performLayout and computeDryLayout when the RenderProxy has no child to determine its size based on the current constraints (previously this was done by calling performResize, which was a little odd since performResize is documented to only be called when sizedByParent is true (which it often isn't in these cases)). The default implementation just returns constraints.smallest (which is what performResize used to set the size as as well).

Related Issues

Fixes #48679

Tests

I added the following tests:

  • Added tests for the cases from the bug
  • Also: This is self-testing: In every test we check that the size computed by performLayout/performResize matches the size computed by performDryLayout.

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.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read Handling breaking changes.

@flutter-dashboard flutter-dashboard bot added a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. labels Nov 16, 2020
@google-cla google-cla bot added the cla: yes label Nov 16, 2020
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also when the layout is lazy, right? e.g. SliverList?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "Reason given:" really adds anything.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objectRuntimeType

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(yes i know this is in an assert but we have a lint now)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(also affects other places in the pr)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this mention debugCheckingIntrinsics? It seems to be the real reason this function exists...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"(which is used by [getDryLayout] behind the scenes)" sounds mysterious, maybe "(the backend implementation of getDryLayout)" or something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a weird condition

@Hixie
Copy link
Contributor

Hixie commented Nov 17, 2020

I'm worried about the amount of duplicate code that this involves. Is there any way we can use this to simplify matters? Maybe find places where the intrinsics can defer to the dry layout? Or ways the dry and wet layouts can be implemented as one method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is correct, it definitely deserves a comment. :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

layouted -> laid out

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth mentioning that these all implement ChildLayouter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this use ChildLayoutHelper?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this use ChildLayoutHelper?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth adding something about RenderBox here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dry

@Hixie
Copy link
Contributor

Hixie commented Nov 17, 2020

I'm worried about the amount of duplicate code that this involves. Is there any way we can use this to simplify matters? Maybe find places where the intrinsics can defer to the dry layout? Or ways the dry and wet layouts can be implemented as one method?

I guess it's not all that bad. You do end up reusing a bunch of code.

Where does the previous intrinsic/layout logic now defer to the new API to solve the bug?

@flutter-dashboard flutter-dashboard bot added the a: tests "flutter test", flutter_test, or one of our tests label Nov 17, 2020
@goderbauer
Copy link
Member Author

Where does the previous intrinsic/layout logic now defer to the new API to solve the bug?

It's used in three places:

@goderbauer
Copy link
Member Author

goderbauer commented Nov 17, 2020

I was able to simplify the RenderWrap code a little more by implementing its other intrinsics methods in terms of dryLayout. The new API is now used in the following places:

@Hixie
Copy link
Contributor

Hixie commented Nov 17, 2020

Nice.

@goderbauer
Copy link
Member Author

I updated this PR with the drayLayout implementations for the remaining RenderBoxes.

Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have implications for the solution we're exploring in #70855?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this TODO be removed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other intrinsics in this file are still incorrect.

@goderbauer
Copy link
Member Author

Does this have implications for the solution we're exploring in #70855?

I removed the legacy mode from here and closed the other PR since SliverFillRemaining seems to require intrinsics.

Copy link
Contributor

@gspencergoog gspencergoog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32384589-a60f0e74-c078-11e7-9bc1-e5b5287aea9d

This looks like a good change! You might want to updated the PR description to include the links to the breaking change information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name feels a little odd: If I was someone seeing it for the first time, I might wonder if there was a wet layout too, and what dry refers to.

Maybe computeDryRunLayout? computeDesiredLayout? computeAPrioriLayout?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really happy about any of these names... I added a paragraph to the docs explaining why this is dry and the other layout is wet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the paragraph, why not change the name to computeDryRunLayout? Seems more self-documenting.

/// significant optimizations. Classes that use this approach should override
/// [sizedByParent] to return true, and then override [performResize] to set the
/// [size] using nothing but the constraints, e.g.:
/// [sizedByParent] to return true, and then override [computeDryLayout] to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update the docs in sizedByParent to point the user in the right direction too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will open a PR to add a note to sizedByParent.

void performResize() {
// default behavior for subclasses that have sizedByParent = true
size = constraints.smallest;
size = computeDryLayout(constraints);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a hugely breaking change. Many (if not most) render objects that set sizedByParent to true were likely relying on the old default behavior in performResize, and now they'll get assertion errors in the default implementation of computeDryLayout.

I know this is listed as a breaking change, but was it intentional for it to break so many places?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the framework there appear to be a total of 16 RenderObject subclasses that set sizedByParent to true. Of those, only 2 used to rely on the default of performResize (constraints.smallest) and the remaining 14 had custom implementations. If I recall correctly, there also wasn't any customer_test that relied on the old default of performResize. With that, I wouldn't really call this "hugely breaking"...

I considered making the default computeDryLayout return constraints.smallest if sizedByParent was set, but ultimately decided against making that method more complicated (and harder to reason about) since the impact of the change seemed so low.

Having said all that, is there something I missed that makes this change more breaking?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. When I ran across this, it's because I hit the error in my app; I had three render objects in which I had overridden sizedByParent to return true, and in all three cases, I was relying on the old default performResize behavior.

But based on your data, it seems I may be an outlier.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side-note: once my app's done (and I write tests), I plan to add them to the customer tests 🙂

/// Computes the value returned by [getDryLayout]. Do not call this
/// function directly, instead, call [getDryLayout].
///
/// Override in subclasses that implement [performLayout] or [performResize].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or subclasses of RenderBox that set sizedByParent to true but didn't override performResize (see comment below).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will open a PR to add this.

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

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) a: tests "flutter test", flutter_test, or one of our tests f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect IntrinsicHeight calculation for RichText

5 participants