Flutter Renderboxes passing down Infinity height? - dart

I'm getting confused with flutter's render boxes.
Here's what I understand: The layouting algorithm starts at the root and traverses down the widget tree, passing down constraints to the children. Flex boxes, in bounded constraints in their scroll direction, try to match their parent constraints.
Consider the following code:
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
print("Parent");
print(constraints.maxWidth);
print(constraints.maxHeight);
return Column(children: [
LayoutBuilder(builder: (context, constraints) {
print("Child");
print(constraints.maxWidth);
print(constraints.maxHeight);
return SizedBox(
height: 12,
);
})
]);
});
}
This prints
flutter: Parent
flutter: 320.0
flutter: 568.0
flutter: Child
flutter: 320.0
flutter: Infinity
How does the height constraints suddenly become Infinity?
Shouldn't it match its parent and be 568.0?

LayoutBuilder parent size is important, if size of parent isn't clear, constraints sizes will be infinity. for example if you wrap your widget with scaffold, LayoutBuilder provides exact size.

For sure it’s confusing. The Flex part of this doc helps. Once the Column is in the tree the Column tries to be “as big as possible”. Why “possible” is Infinity and not 568 in this case, I don’t know and it’s seems unnecessarily painful and complicated (unlike the rest of Flutter).
https://flutter.dev/docs/development/ui/layout/box-constraints

Related

Turning a list into sublist of a given size

I have a list of X widgets and want to show them in a ListView of Rows. I know that I can fit N widget in each Row.
I want to do:
var MyListOfRows = ListView(
children: intoSublists(widgetList,subListSize).map(
(sublist)=> Row(
children: sublist,
)
).toList()
);
I thought about implementing intoSublists with complex modulo magic but that seems to leave complicated code and I'm hoping that there's a easy way to implement this is dart. Is there?
(This is part of a component that I pass a minimum height and width together with a list of Widgets. The component is supposed to display the Widgets as big as possible and if there are too many Widgets because of the minimum dimensions display a scrollable list)
You are reinventing the wheel. Instead of a ListView and a bunch of Rows, use a SingleChildScrollView and a Wrap, and let the Wrap determine how many widgets can fit in a particular "row" based on the actual size of the screen.
var myList = SingleChildScrollView(
child: Wrap(
children: widgetList,
),
);
EDIT: If you want the widgets to stretch to fill the row (and all the widgets are going to be the same size), you can also use a GridView:
var myGrid = GridView(
crossAxisCount: subListSize, // How many widgets you want to be across
children: widgetList,
);

Why Flutter Container does not respects its width and height constraints when it is inside other Container

Container(
width: 200.0,
height: 200.0,
child: Container(
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red
),
),
)
I've been trying to find the answer in the Container class docs but I did not find it.
Update:
After a long time, I understood the problem.
All views inside a layout must have width, height, x position, and y position. (This applies to Android, IOS, Flutter, etc)
In my code, the inner container just has a width and height for that reason it doesn't know where to start painting.
For that reason, if the container is placed inside an Alignment widget the container gets the x position and y position and it works.
Constraints in Flutter works a bit different than usual.
Widgets themselves do not have constraints.
When you specify a width/height on a Container, you're not constraining Container. You're constraining the child of Container.
Container will then size itself based on the size of its child.
As such, parent widgets always have the last word on how their descendants should be sized.
If you want to go around this, you have to use Align widget:
Container(
width: 200.0,
height: 200.0,
child: Align(
alignment: Alignment.topLeft,
child: Container(
width: 50.0,
height: 50.0,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.red),
),
),
);
This may seem weird and limiting. But this single weirdness is the reason why Flutter's layout is so powerful and composable.
All views inside a layout must have four constraints:
Width
Height
X position
Y position
This applies to Android, IOS, Flutter, etc.
In the code, the inner container just has a width and height for that reason it doesn't know where to start painting and it gets all the available space.
But, if the inner container is placed inside another layout widget that aligns its child, for example, Center. It will get its x and y positions.
Container(
decoration : BoxDecoration(color : Colors.green),
width: 200.0,
height: 200.0,
child: Center(
child: Container(
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red
),
),
)
)
The inner container in red, the outer container in green.
Keep in mind, setting width or height of a container, it's same as setting min-width and max-width or min-height and max-height at the same time.
Your case for the inner Container according to the documentation will be:
If the widget has no child and no alignment, but a height, width, or constraints are provided, then the Container tries to be as small as possible given the combination of those constraints and the parent's constraints.
Since your outer container gives minWidth=minHeight=200 constraints to the inner container, thus the size of your inner container cannot be less than Size(200, 200).
In support of Rémi Rousselet's answer, the Align widget, same as Center widget, will expand to fit its parent's constraints, if its parent has constraints, and position its child in the specified alignment.
If the widget has an alignment, constraints, and the parent provides bounded constraints, then the Container tries to be as small as possible given the combination of those constraints and the parent’s constraints, and then positions the child within itself as per the alignment.
In this case, the outer Container has an Align widget as child (an alignment), constraints, and the parent provides bounded constraints, thus it tries to be as small as possible given the combination of its constraints and the parent’s constraints, which is Size(200, 200).
You might be wondering why does the inner Container has the size of 50*50, instead of 200*200, since its parent (outer Container) has specify the minWidth=minHeight = 200. The reason is that as mentioned before the Align widget will expand to fit its parent constraints, so in this case the Align widget will expand to fit the outer Container's constraints, i.e. it will have the size of Size(200, 200), and tell its child (the inner Container), that it can be any size it wants, but not bigger than my size, 200*200. Therefore, the information of minWidth and minHeight of the outer Container that is supposed to be pass to the inner Container is lost.
Some additonal information for the "Update" section of the question:
As metioned above,
All views inside a layout must have width, height, x position, and y position. (This applies to Android, IOS, Flutter, etc)
In my code, the inner container just has a width and height for that reason it doesn't know where to start painting.
The reason why the inner container doesn't know where to start painting is that its parent, which is the outer Container Widget, does not have a corresponding RenderObject(it doesn't implement a createRenderObject method), hence which can't performLayout alone, and here comes the big deal:
Most widgets set the offset of their children(the x, and the y position of the children) by putting the Offset data in the parentData attributes in them, and such process is done in the performLayout method, so in the nested Container usage scanarios, the inner container can't get a reasonable offset from the outer widget, and therefore setting the width and height fails working in the inner container.
Furthermore, why Align Widget can help solving this problem?
Take a glimpse of the Align source code and everything becomes clear and plain
// Align -> createRenderObject -> RenderPositionedBox -> alignChild
#protected
void alignChild() {
// ....irrelevant code
final BoxParentData childParentData = child!.parentData! as BoxParentData;
childParentData.offset = _resolvedAlignment!.alongOffset(size - child!.size as Offset);
}
And I sure believe any other Widgets similar to Align Widget that set the offset of their children can solve this problem as well

flutter: Pie chart has a weird margin around it. How can I remove it?

I am trying to use the piechart widget from the flutter_charts dependency in my flutter app but it has a margin around it taking up space inside the container widget please how can I remove the margin or padding around the pie chart.
How can I remove margin or padding from the PieChart() widget
use layoutConfig property of PieChart
layoutConfig: charts.LayoutConfig(
leftMarginSpec: charts.MarginSpec.fixedPixel(0),
topMarginSpec: charts.MarginSpec.fixedPixel(0),
rightMarginSpec: charts.MarginSpec.fixedPixel(0),
bottomMarginSpec:charts.MarginSpec.fixedPixel(0),
),
Please use transform instead of margin
Container(
transform: Matrix4.translationValues(-230.0, 0.0, 0.0),
child:new charts.PieChart()
)
I also had faced the same problem but for me that one is due to the improper ratio of defining height and width on its parent widgets. Try to use 1:1 ratio for pie chart. because it always try to align on center.

Autoresize Text to fit in Container vertically

I want to create an app, that shows sometimes shorter, somertimes longer text in a Container. This container should not change in size, after the widget is build to fit with the size of the screen. The text itself comes from a database and I don't know, how long it can be.
When I print the text in the container, sometimes it fits, sometimes there is many space above and below, when the text is short, or sometime the text overflows and is not visible.
I want, that the text size is automatically changed to fit into the container. Container with overflow
The flutter package Auto-Size-Text is not working for me, because I only could resize the text to fit the width or set the maximum line, which I don't know either.
This is my code simplified:
AutoSizeText autotext = AutoSizeText(text, style: style, textAlign: TextAlign.center, presetFontSizes: [25.0,22.0,20.0,18.0], semanticsLabel: text);
Container(
child: autotext
);
TL;DR:
Your AutoSizeText widget is probably provided with unbounded constraints.
This can be the case if it's wrapped in a Column, Row, ListView or something similar.
To counter this issue, wrap it in an Expanded widget.
In order to answer that question, we need to understand how Flutter performs the layout.
Basically, the top widget's height and width (called its "dimensions") are requested by the framework, which provides a min and max width and height (called "constraints").
In order to calculate its dimensions, the widgets ask their children what dimensions they want to have based on some constraints.
So, every widget gets told constraints and returns concrete dimensions within these constraints.
Widgets like ListView or Column don't want to influence their children's dimensions too much - they should simply be as small as possible.
In order to communicate that, they are given a maximum width/height of double.infinity, telling them that they should default to their "natural"/default dimensions. A constraint of this type is called "unbounded".
Here's a Google Tech Talk video about Flutter's rendering pipeline, where all of this is described in greater detail.
With this process in mind, let's have a look at how your widgets perform during layout:
In a comment to your post, you said there's a Column widget, so I assume there's a Column containing a Container, which in turn contains the AutoSizeText widget.
If instead of a Column, there's a Row or ListView or something similar, the process is basically the same.
The Column wants its children to be as small as possible, so it passes unbounded constraints to all of its children.
Being one of those children, the Container receives those unbounded constraints.
Because the Container itself is just a wrapper class, it simply passes these constraints on to its child, the AutoSizeText.
The AutoSizeText receives unbounded constraints.
Looking at its source code, we can see that it starts with a default text size and checks if it fits (calling _checkTextFits).
If it doesn't fit, the text size is decreased and this process is repeated until the text is small enough to fit into the given constraints.
Because in your case, the constraints are unbound, the text fits on the first try - that means the widget simply uses the default size.
Having determined the text size, it can calculate the text's dimensions and returns them up the tree.
The Container receives those dimensions.
Because it's just a wrapper class, it simply returns them to its parent in turn.
Finally, the Column receives the dimensions and sizes itself around that (probably adding the dimensions of other children).
Solution
You don't want to pass unbounded constraints to an AutoSizeText widget, because that defeats the purpose of the widget in the first place - fitting text into bounded constraints.
You can confirm this is the problem by hardcoding the Container's width and height, that should make the AutoSizeText widget adjust the text size properly.
If the Container is placed inside a Column or something similar, you can always bound the constraints by wrapping it in an Expanded.
Why the solution works
The Column checks which of its children are Expanded widgets.
It first lays out all the other widgets.
Only then, it divides the remaining space among the Expanded widgets according to their flex factors (that's a whole other topic for itself).
That means the Expandeds' children will receive bounded constraints.
If you wrap your Container in an Expanded and it's the only expanded in the Column, it should take all the remaining space, and fit the text perfectly within itself.

Flutter overflow: hidden analogue

Is there any flutter widget that prevents the children to paint outside a Container by any mean?
I have this container with a child that may get some transformations (such as scale and rotate) and therefore can be painted outside
I want to limit the children's painting to only inside the parent Container, just like a div with CSS overflow:hidden; would behave.
A sample:
return Container( // the one with overflow hidden -ish behavior
height: 300.0,
child: TheTransformingChild() // the one that can get bigger that container
)
I think it's easier to use clipBehavior: property in container
Container(
clipBehavior: Clip.hardEdge,
height: 400,
width: 400,
child :TheTransformingChild(),)
There is - what you're looking for is a combination of OverflowBox or SizedOverflowBox and ClipRect, or ClipOval, or ClipPath, or ClipRRect etc.
I'd recommend looking through the painting and layout sections of the flutter widget catalog (and the rest of it as well) as it generally does a pretty good job of showcasing the widgets you need.
An easy way is to use the Wrap component (below is your example).
return Container( // the one with overflow hidden -ish behavior
height: 300.0,
child: Wrap(
children: [
TheTransformingChild()
],
)
)
It also replaces the Column component in most cases:
Using Column
Using Wrap

Resources