-
Notifications
You must be signed in to change notification settings - Fork 29.8k
ConstraintsTransformBox #77994
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
ConstraintsTransformBox #77994
Conversation
| /// * [RenderSizedOverflowBox], a render object that is a specific size but | ||
| /// passes its original constraints through to its child, which it allows to | ||
| /// overflow. | ||
| class RenderUnconstrainedBox extends RenderConstraintsTransformBox { |
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.
Should this class be deprecated? No one is using it internally and the superclass is more generalized.
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.
I'd be OK with deprecating this and redirecting people to RenderConstraintsTransformBox.
| /// the console, and black and yellow striped areas will appear where the | ||
| /// overflow occurs. | ||
| /// | ||
| /// This [RenderBox] allows the child to selectively enforce some of its |
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.
what do you mean by "selectively enforcing some of its intrinsic sizing rules"?
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.
reworded.
|
|
||
| /// Renders a box, imposing no constraints on its child, allowing the child to | ||
| /// render at its "natural" size. | ||
| /// A [RenderBox] that applies an arbitrary transform to its [constraints] |
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.
Might be worthwhile to document somewhere in here what this does if child is null.
| constraints: constraints, | ||
| layoutChild: ChildLayoutHelper.layoutChild, | ||
| ); | ||
| assert(constraintsTransform != 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 this in the constructor and setter instead of here?
| /// * [RenderSizedOverflowBox], a render object that is a specific size but | ||
| /// passes its original constraints through to its child, which it allows to | ||
| /// overflow. | ||
| class RenderUnconstrainedBox extends RenderConstraintsTransformBox { |
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.
I'd be OK with deprecating this and redirecting people to RenderConstraintsTransformBox.
| /// | ||
| /// The `alignment`, `clipBehavior` and `constraintsTransform` arguments must | ||
| /// not be null. | ||
| const ConstraintsTransformBox.unconstrained({ |
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.
Isn't this just duplicating behavior of the UnconstrainedBox widget? Can we just link to that one in the docs?
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 other two 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.
I'll expose the static functions instead of named constructors.
| /// * [AlignmentDirectional] for [Directionality]-aware alignments. | ||
| final AlignmentGeometry alignment; | ||
|
|
||
| /// @{template flutter.widgets.constraintsTransform} |
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.
nit: @ goes inside the {
| /// | ||
| /// The function must return a [BoxConstraints] that is | ||
| /// [BoxConstraints.isNormalized]. | ||
| /// @{endtemplate} |
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 [debugTransformType] argument adds a debug label to this widget. | ||
| /// | ||
| /// The `alignment`, `clipBehavior` and `constraintsTransform` arguments must |
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.
Given the type String, presumably debugTransformType also cannot be null? Maybe assert that as well?
| } | ||
|
|
||
| @override | ||
| RenderConstraintsTransformBox createRenderObject(BuildContext context) { |
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 could also just be implemented as a stateless widget now which builds a ConstraintsTransformBox?
justinmc
left a comment
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.
I left a few questions and ideas as I try to understand this at a high level. I owe you a more decisive review.
| required AlignmentGeometry alignment, | ||
| required TextDirection? textDirection, | ||
| Axis? constrainedAxis, | ||
| required BoxConstraintsTransform constraintsTransform, |
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.
Just an idea, let me know what you think:
Make this not required. If it's null, then it just uses the constraints it has as-is.
Maybe that's kind of pointless, but I was thinking it would allow you to get rid of _unmodified in the main PR: https://github.com/flutter/flutter/pull/64615/files#diff-f5f93c879cef9a102adbf148583de9f3d9a05f9678fdcad7bde106f62200bb34R2606
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.
I'll expose the static functions ('unconstrained' etc,.), instead of the named constructors then.
| /// ConstraintsTransformBox( | ||
| /// constraintsTransform: (BoxConstraints constraints) => constraints.copyWith(maxHeight: double.infinity), | ||
| /// child: const Card(child: Text('Hello World!')), | ||
| /// ) |
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 this also be achieved with the code below, or am I misunderstanding?
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return ConstrainedBox(
constraints: constraints.copyWith(maxHeight: double.infinity),
child: const Card(child: Text('Hello World!')),
);
},
)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.
ConstrainedBox is a bit different from ConstraintsTransformBox, as it does not replace the original constraints but adds additional constraints to the original constraints. In this example, if the parent is a SizedBox(height: 50) then in the above example the Card can be as high as it wants but with ConstrainedBox the maxium height is 50. I'll update the example to be more specific.
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.
I really thought that my example here was the same as ConstraintsTransformBox, but I tried it and they are not the same.
So here's a simple example not using anything new:
Container(
height: 100,
color: Colors.red,
clipBehavior: Clip.none,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: double.infinity),
child: Container(
height: 200,
color: Colors.blue.withOpacity(0.5),
),
),
),Both Containers are 100px high there. There is no overflow. I would have expected the blue Container to extend outside of the red Container, but I guess I'm wrong about how Flutter does this :)
Now here's a similar thing using ConstraintsTransformBox:
Container(
height: 100,
color: Colors.yellow.withOpacity(0.5),
child: ConstraintsTransformBox(
alignment:Alignment.topLeft,
constraintsTransform: (BoxConstraints constraints) => constraints.copyWith(maxHeight: double.infinity),
child: Container(
height: 200,
color: Colors.blue.withOpacity(0.5),
),
),
),The inner blue Container is able to extend outside of its parent, with an overflow warning.
So I think this makes sense and I understand how this widget produces a unique behavior that doesn't seem to be easily doable with existing widgets 👍
| super(key: key, child: child); | ||
|
|
||
| /// Creates a widget that imposes no constraints on its child, allowing it to | ||
| /// render at its "natural" size. If the child overflows the parent's |
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.
What's the difference between "natural" as you use it here and "intrinsic" as in intrinsicHeight/Width?
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.
Also just out of curiosity, what if the child's intrinsic width or height is larger than the device's screen? Will it actually be able to render that big or will it be constrained by the screen size? (This would be larger than the parent in most normal cases, so it would also give an overflow warning, but I'm curious what would happen.)
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.
Ah I'm using them interchangeably. I'll replace intrinsic with natrual.
I think the child will be rendered as if there was infinite space (given it's not clipped) and will be aligned per its alignment. But yeah if it ends up being larger than the RenderView you'll get a warning (this is a debugging widget in some sense).
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.
I tried this out and for this app:
MaterialApp(
home: Scaffold(
body: ConstraintsTransformBox(
constraintsTransform: myConstraintsTransform,
child: Container(
color: Colors.red,
),
),
),
);When myConstraintsTransform is...
(BoxConstraints constraints) => constraints,the whole screen is red.(BoxConstraints constraints) => constraints.copyWith(maxHeight: 10000),the whole screen is red, and there is an overflow warning.(BoxConstraints constraints) => constraints.copyWith(maxHeight: double.infinity),the whole screen is empty, no warning.
Should there be an error for that last case with infinite height? Or actually maybe that is the normal behavior when you make the constraints infinite in Flutter.
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.
Container has a LimitedBox which only kicks in when the size is unconstrained. I think what happened was when the maxHeight is unconstrained the LimitedBox will try to become as small as possible so the ColoredBox wrapped inside the LimitedBox is zero-sized (case 3).
2c21e64 to
27c7d1f
Compare
| /// overflow. | ||
| /// | ||
| @Deprecated( | ||
| 'Use RenderConstraintsTransformBox instead. ' |
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.
Mention the deprecation and what to use instead in the doc above?
| /// When [child] is null, this widget becomes as small as possible and never | ||
| /// overflows | ||
| /// | ||
| /// This widget can be used to ensure some of [child]'s intrinsic dimensions are |
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.
intrinsic = natural, as used below?
| /// overflows | ||
| /// | ||
| /// This widget can be used to ensure some of [child]'s intrinsic dimensions are | ||
| /// honored, during development. For instance, if [child] requires a minimum |
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.
What do you mean by "during development"? Should this widget not be used in production?
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.
changed to "This widget can be used to ensure some of [child]'s natrual dimensions are honored, and get an early warning otherwise during development. "
| /// | ||
| /// The [ConstraintsTransformBox] becomes a proxy widget that has no effect on | ||
| /// layout if [constraintsTransform] is set to this. | ||
| static BoxConstraints unmodified(BoxConstraints constraints) => const BoxConstraints(); |
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.
Should this return the argument "constraints"? Otherwise its not matching the documentation of being an identity function...
|
|
||
| final String? debugTransformLabel = _debugTransformLabel.isNotEmpty | ||
| ? _debugTransformLabel | ||
| : _knownTransforms[constraintsTransform]; |
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.
nit: name this _debugKnownTransforms to message that it's only supposed to be used within debug* methods?
| clipBehavior: clipBehavior, | ||
| ); | ||
|
|
||
| static BoxConstraints _fail(BoxConstraints incoming) { |
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 kinda odd. How about defining UnconstrainedBox as a Stateless widget that builds a ConstraintsTransformBox as a child (composition over inheritance)?
| /// positioning, and sizing widgets. | ||
| /// * [OverflowBox], a widget that imposes different constraints on its child | ||
| /// than it gets from its parent, possibly allowing the child to overflow | ||
| /// the parent. |
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.
Also link to ConstraintsTransformBox here?
| /// | ||
| /// {@tool snippet} | ||
| /// In the following snippet, the [Card] is guaranteed to be at least as tall as | ||
| /// its "natrual" height. Unlike [UnconstrainedBox], it can become taller if its |
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.
can -> will, it doesn't have a choice, right?
cb57f43 to
71c173e
Compare
| /// The text direction to use when interpreting the [alignment] if it is an | ||
| /// [AlignmentDirectional]. | ||
| final TextDirection? textDirection; | ||
|
|
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.
keep these white spaces between properties?
| expect( | ||
| const UnconstrainedBox(constrainedAxis: Axis.vertical,).toString(), | ||
| equals('UnconstrainedBox(alignment: Alignment.center, constrainedAxis: vertical)'), | ||
|
|
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.
odd extra whitespace?
goderbauer
left a comment
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
| /// * [Alignment] for non-[Directionality]-aware alignments. | ||
| /// * [AlignmentDirectional] for [Directionality]-aware alignments. | ||
| final AlignmentGeometry alignment; | ||
|
|
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.
nit: keep this whitespace?
| /// be retained, and if set to [Axis.horizontal], then horizontal constraints | ||
| /// will be retained. | ||
| final Axis? constrainedAxis; | ||
|
|
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.
nit: keep this whietspace?
justinmc
left a comment
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 pull request is not suitable for automatic merging in its current state.
|
|
This pull request is not suitable for automatic merging in its current state.
|
|
Where can I read about the reason for the deprecations in this PR? |
|
@Hixie I thought by definition this is not a breaking change (no customer tests were broken) so doesn't warrant a design doc? |
|
Just curious why we deprecated it, not suggesting a design doc or anything. :-) |
|
@Hixie gotcha. With the PR the |
|
Ah, cool. |


The safer part of Reland #63639.
The remaining part of #64615 is a bit riskier, with 31 internal test failures.
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.