What is the difference between using ConstrainedBox and Container widgets to give constraints to their child box? - flutter

I found that the only difference between the Container() and ConstrainedBox() widgets (if we want to constrain its child), is the more properties in the Container() widget to customize its child, but are there any other differences? and are there any performance differences and when it is efficient to use what?

Container does not do anything by itself. It is only a utility widget that delegates functionality to other widgets. This means that the contraints argument of a Container is strictly equivalent to ConstrainedBox.
If you take a look at the source code for Container, you will find the following:
if (constraints != null)
current = ConstrainedBox(constraints: constraints, child: current);
→ there is no difference.
Container(
constraints: constraints,
child: child,
)
// does strictly the same as
ConstrainedBox(
constraints: constraints,
child: child,
)

Related

Why does ListView ignore constraints

Why does a ListView completely ignore imposed constraints?
#override
Widget build(BuildContext context) {
return Padding(
child: Container(
constraints: BoxConstraints(maxWidth: 500),
child: ListView(
children: buildList(data),
),
),
);
}
The displayed ListView does not seem to be bothered at all with the maxWidth:500 of the parent container (same if replaced with a ConstrainedBox) and all elements (which are Rows) go full screen width.
Why?
The solution is to work with width of children instead, but still I'd much rather constrain the entire listView, and also if there's a constraint, shouldn't everything inside be forced not to exceed it?
EDIT: After hours of trying various things it finally works. Turns out the list view needed to a child of a Center, otherwise it would not pay any attention to SizedBoxes, ConstrainedBoxes, Containers etc. you'd put on it or its children and stretch full screen no matter what.
What are the parent widgets of the Padding(child: Container(... list view ))? I mean are you sure that a parent is not imposing its constraints on its children? Try do replace your ListView with Container(color: Colors.red, height: 100) and see if it respects the constraints. If it does not, there probably is a parent widget with infinite width constraint or something like that!
A solution can be wrap your Container with an UnconstrainedBox but I would recommend first checking my first point :D

MaxHeight causes widget to expand

I'm building an expansible card in flutter, which I wanto to animate. I want the parent Container to have a limited height while the card is collapsed, and to have whatever height is necessary for the child when it's expanded. I can reach this result using maxHeight: double.infinity:
Container(
alignment: Alignment.topCenter,
clipBehavior: Clip.hardEdge,
constraints: isExpanded
? BoxConstraints(maxHeight: double.infinity)
: BoxConstraints(maxHeight: 76),
decoration: BoxDecoration(),
child: widget.child,
),
)
But whatever maxHeight value I use other than double.infinity causes the parent to have that exact height, instead of just having the height needed by the child. I can't use double.infinity because that's not animatable. I've tried following this answer's tips, but whatever I wrap the child with, the parent always grows. I've tried changing the child, but even if I use maxHeight on the child itself, the parent always grows to the defined maxHeight.
Why does this happen? Shouldn't maxHeight only cause the parent to expand if needed? How come it works correctly with double.infinity, but doesn't with anything else?
Also, I have tried reaching this result with AnimatedSize, it sort of works. When it is expanding, it works perfectly, but when the child is collapsing, it collapses instantly, and the AnimatedSize's animation follows after.
Edit:
I see what you mean, you want to use AnimatedContainer, but in order to be animated like you want it, it requires you to specify the height. double.infinity won't do in this case, the error message you're getting is correct.
There are ways to get widget height.. I suggest you check this answer: https://stackoverflow.com/a/49650741/7248289
Also, this one might help: https://stackoverflow.com/a/54173729/7248289

"RenderStack object was given an infinite size during layout" when using Positioned inside a Stack

I'm creating a widget that presents a Stack inside a Container. Inside the Stack there is a dynamic Column inside a Positioned widget, which sticks the Column to the Bottom.
I am trying to wrap the Stack in a SingleChildScrollView, that if the dynamic Column will have lots of children it will be able to scroll.
But after I add the scroll view I get this error message:
RenderStack object was given an infinite size during layout.
A scrollView doesn't have height, it will use infinite height for it's children. So the stack should be wrapped in a sizedBox widh a known height in case that you want that. If you want the stack to fill the screen then remove the singleChildScrollview completely. And if you want the stack to be scrollable from the inside. Just add SingleChildScrollView inside the stack
return Scaffold(
body: Container(
color: Colors.blue[100],
child: Stack(
children: [
Positioned.fill(child: SingleChildScrollView(child: MyColumn()))
],
),
),
);
EDIT 1:
Ok the problem is the layout itself. Since you only use the stack to position the image. And you know the widht and height of the image, and the background shape fills all the items. We could
Isolate the stack only to the image, and use padding and some calculation to have the layout build in a similar way.
Then render everything else inside a normal column
My implementation https://dartpad.dev/230b14d8f0b22b45929a75c208786257
From the RenderStack documentation:
First, the non-positioned children (those with null values for top, right, bottom, and left) are laid out and initially placed in the upper-left corner of the stack. The stack is then sized to enclose all of the non-positioned children. If there are no non-positioned children, the stack becomes as large as possible.
Since all of your Stack's children are positioned, Stack will match the maximum size constraint passed down by the SingleChildScrollView. This size has infinite height, so the Stack is forced to have infinite height as well.
To fix this, you will need to find another way to accomplish what you want. One option that comes to mind involves a ConstrainedBox providing a minimum height (obtained from a LayoutBuilder), a non-positioned Column, and a bottomCenter alignment on the Stack itself. This is similar to the first code sample in the SingleChildScrollView documentation:
LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
// This ensures that the Stack fills the entire viewport,
// even if the Column is small.
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: Stack(
// Place the only non-positioned child (the column) at the bottom
alignment: Alignment.bottomCenter,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [ /* ... */ ]
)
]
)
)
);
}
)

ListTile with wrap_content width

Now when I add a ListTile inside a view (no matter its Column,ListView,...) It gets whole width, I want it to be kinda wrap_content as we have in android.
I know this is an old post but here is a solution I found for future reference:
You can use the IntrinsicWidth widget to stop the ListTile from expanding.
Container(
child: IntrinsicWidth(
child: Column(
children: [
ListTile(title: Text("Hello")),
ListTile(title: Text("There")),
],
)
)
)
As far as I can tell this is not how you are supposed to use ListTile. In the docs it even says:
ListTile requires fixed width constraints
Also, regarding IntrinsicWidth the docs say:
This class is useful, for example, when unlimited width is available and you would like a child that would otherwise attempt to expand infinitely to instead size itself to a more reasonable width. [...] This class is relatively expensive, because it adds a speculative layout pass before the final layout phase. Avoid using it where possible.
may be like this
Row(
children: <Widget>[
ListTile(),
Text("hell")
])

Flutter Container's BoxConstrains not working as expected

this is the code
return MaterialApp(
home: Container(
constraints: BoxConstraints.tight(Size(100, 100)),
decoration: BoxDecoration(color: Colors.yellow),
child: Card(child: Text('Hello World')),
),
);
what i expected is Card is 100 x 100, but it's not, it just stretched to the whole screen. why does it happened?
The Flutter team released a great article named "Understanding constraints" that's definitely worth checking out if you're unsure about how constraints work.
That being said, here's a more in-depth answer specifically about your problem:
Why do your BoxConstraints get ignored?
The Container internally creates a ConstrainedBox in order to enforce your constraints.
The ConstrainedBox's RenderObject - a RenderConstrainedBox - then tries to adhere to your constraints as much as possible.
During layout, the following code taken from the RenderConstrainedBox implementation is executed.
The members minWidth, maxWidth, minHeight and maxHeight stem from your given constraints, while constraints are the constraints provided by the Flutter framework (in your case, the screen size).
BoxConstraints(
minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth),
maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth),
minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight),
maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight)
)
Because the screen size is inflexible, the constraints are tight, meaning constraints.minWidth == constraints.maxWidth and constraints.minHeight == constraints.maxHeight.
That forces clamp to ignore your constraints (for example, 2.clamp(3,3) == 3).
So there you have it:
The constraints you give to a Container will only be respected as much as possible.
Because the whole screen needs to be filled, the Container is forced to take the tight screen constraints, thereby ignoring your constraints.
What to do about it
To make it possible for the Container to respect your constraints, you have to give it more "breathing room", i.e. don't force it to fill the screen.
A simple solution would be to wrap it in a Center, but it depends on your preferences on where the Container should be aligned.