Skip to content
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

RenderConstrainedBox takes constraints when measuring intrinsic sizes #162809

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

lohnn
Copy link

@lohnn lohnn commented Feb 6, 2025

RenderConstrainedBox (such as used in SizedBox and ConstrainedBox) now takes constraints max sizes into consideration when measuring intrinsic sizes.

Fixes #162808.

Pre-launch Checklist

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

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label Feb 6, 2025
@justinmc
Copy link
Contributor

CC @victorsanni from triage.

@LongCatIsLooong
Copy link
Contributor

Previous fix deemed too breaking: #137874

@lohnn
Copy link
Author

lohnn commented Feb 26, 2025

So, just to get this straight:
This PR might break a bunch of internal Google code?
And I have to also handle infinity?

@LongCatIsLooong
Copy link
Contributor

So, just to get this straight: This PR might break a bunch of internal Google code?

See this comment. I'm curious what the internal failures looked like.

And I have to also handle infinity?

To me _additionalConstraints.constrainHeight(height), makes more sense.

@lohnn
Copy link
Author

lohnn commented Feb 27, 2025

See this comment. I'm curious what the internal failures looked like.

That is exactly the comment I am referring to. Would really like to know too.

To me _additionalConstraints.constrainHeight(height), makes more sense.

I believe I tried that and got to the conclusion I didn't want to clamp the height and width, but rather just constrain max size.
Can't remember why right now... It will probably come back to me in due time.
But what I can say is that the test added in #72095 is passing with my max implementation, and fails using _additionalConstraints.constrainX.

@LongCatIsLooong
Copy link
Contributor

To me _additionalConstraints.constrainHeight(height), makes more sense.

I looked into the internal test failures from the other PR, now I'm convinced that using min is correct here since _additionalConstraints is ignored if it doesn't satisfy the incoming constraints (_additionalConstraints.enforce(constraints)).

Looks like this change still breaks internal golden tests but most of these changes look like improvements so far. There are a couple of golden changes that I need to further investigate.

final double width = super.computeMinIntrinsicWidth(height);

final double width = super.computeMinIntrinsicWidth(
min(_additionalConstraints.maxHeight, height),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this could be a bit confusing to future readers, could you add a comment regarding why using min here instead of also taking _additionalConstraints.minHeight into account?

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 not addressed yet.

Copy link
Author

Choose a reason for hiding this comment

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

I'm hopeful the comment is clear enough, and not too long.

@LongCatIsLooong
Copy link
Contributor

Hmm there's a legit internal failure. The problem is that, if the parent that has a set of tight constraints, a child RenderConstainedBox is a no-op.

For example, in a render tree like this:

RenderBox(   // A RenderBox that gives tight width constraints to its child. In the failing test it was a Column with `CrossAxisAlignment.stretch`.
  constraints: BoxConstraints.tight(width: x),
  child: RenderConstrainedBox(
    additionalConstraints: BoxConstraints(minWidth: 200, maxWidth: 250),
    child: ...
  ),
);

The RenderConstrainedBox here has absolutely no effect on the layout of this render tree, no matter what the value of x is, so in this case _additionalConstraints should be completely ignored in the intrinsics calculation.
@goderbauer @chunhtai I wonder if we should use computeDrySize to calculate the intrinsic height of children in a RenderFlex, when CrossAxisAlignment is .stretch?

@chunhtai
Copy link
Contributor

chunhtai commented Mar 3, 2025

I wonder if we should use computeDrySize to calculate the intrinsic height of children in a RenderFlex, when CrossAxisAlignment is .stretch

I think the current behavior is correct. If using intrinsic size, it is not stretching the children. The doc also mentioned the child need to fill the space
https://api.flutter.dev/flutter/rendering/CrossAxisAlignment.html

Do you have a link to the error?

@lohnn
Copy link
Author

lohnn commented Mar 8, 2025

To clarify: Are you confirming my PR implementation is correct, or the current production code?
And is @LongCatIsLooong expected to take the next step?
I'll add clarifying comments in the meantime.

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

the code LGTM except the open comment

final double width = super.computeMinIntrinsicWidth(height);

final double width = super.computeMinIntrinsicWidth(
min(_additionalConstraints.maxHeight, height),
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 not addressed yet.

@chunhtai
Copy link
Contributor

would also need to rebase to pass the ci

@lohnn lohnn requested a review from chunhtai March 12, 2025 19:24
@chunhtai
Copy link
Contributor

@LongCatIsLooong
Copy link
Contributor

I spoke with @goderbauer offline he pointed out in the getMaxIntrinsicHeight documentation it says:

The width argument may give a specific width to assume

So the incoming width should be interpreted as a set of tight width constraints. If that interpretation is true, the additionalConstraints should be ignored in the implementation here because it would be overruled by the incoming tight width constraints.

But at the same time there are places that the incoming height is interpreted as a maxWidth constraint, for example the RenderIntrinsicHeight implementation.
also in the same API docs:

The given width can be infinite, meaning that the intrinsic height in an unconstrained environment is being requested

which seems to imply that width should be interpreted as a set of loose width constraints [0, width].

@LongCatIsLooong
Copy link
Contributor

So if the first interpretation is true, then the current RenderConstrainedBox doesn't have to be changed. We'll need to fix RenderIntrinsicHeight and everywhere else that's assuming the incoming width means [0, width] instead of [width, width].

@chunhtai
Copy link
Contributor

So the incoming width should be interpreted as a set of tight width constraints.

Is this true? The doc doesn't suggest that.I thought the getIntrinsicHight is more of what is the height if you are given this width. the renderobject itself is free to impose additional constraint on top of the width. in fact, if we don't consider the additional constraint, won't we return a different height when we actually layout the renderobject with the given width?

@lohnn
Copy link
Author

lohnn commented Mar 13, 2025

This is also what I thought, and this is also causing real layout bugs where intrinsic (expected) sizes are not matching the actual size.

@lohnn lohnn requested a review from LongCatIsLooong March 18, 2025 12:57
@LongCatIsLooong
Copy link
Contributor

I totally agree that this is a real layout but, but applying this patch will unfortunately introduce real layout bugs too, which you can repro by just changing the CrossAxisAlignment to .stretch in your original example:

CustomScrollView(
      slivers: [
        SliverFillRemaining(
          hasScrollBody: false,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: const [
              SizedBox(
                width: 300,
                child: Text(
                  'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec commodo arcu nec varius ultricies. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque porta auctor.',
                ),
              ),
            ],
          ),
        ),
      ],
    );

Of course it doesn't make a lot of sense when .stretch and SizedBox(width: 300) are used side-by-side like that in the code, but the SizedBox can be introduced by a reusable Widget that tries to establish a preferred width.

@lohnn
Copy link
Author

lohnn commented Mar 21, 2025

All right, I now see how we trade from one problem to another in this case.
Any ideas on how to propagate the CrossAxisAlignment constraints?

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.

RenderConstrainedBox does not take constraints when measuring intrinsic sizes
4 participants