Dynamic height of a column's child - flutter

Let's say I have this Column:
Column(
children: [
Flexible(
flex: 2,
child: Card(
child: Text("Card")
)
),
Flexible(
flex: 3,
child: Column(
children: [
// Buttons
]
)
)
]
)
What I want to achieve is that the Card always has a flex of 2, and only gets smaller if the screen (or parent) is too small to render both the card and the buttons. However, with the code above, the card gets scaled down to its minimum size to contain the text, although there is space at the end of the screen.
Replacing the card's Flexible widget with an Expanded widget would fix this, but as I already said, it will never get smaller then, which will end up in a bottom overflow if the screen gets smaller (or the buttons get bigger).
Is there any way to achieve what I want? Or is this just a limitation of Flutter's rendering system?
Edit: I want to avoid making the screen scrollable.

As you probably already know, Flexible allows its child to expand, but it does not force to do so. Viceversa, we use Expanded to force a child to occupy the remaining column/row.
Here's your options:
Use a Flexible Widget, and exploit its fit property: set it to FlexFit.tight to force it to fill the space (default is FlexFit.loose: the child can be at most as large as the available space, but is allowed to be smaller);
Use an Expanded Widget, since I sense that you want to occupy all the available space, a priori;
Mix these two approaches (Flexible on your Card, Expanded on your Buttons, or vice-versa)
In both cases, if some smaller screens are giving you trouble, consider making some elements scrollable: I don't know how big your Card is, but you could wrap it inside a SingleChildScrollView. Or, if you want try something else, you could make the whole screen scrollable with the same approach (you have to pay attention to that Column, though, as there is infinite height...)

Related

How to create a widget that takes no space in MainAxisAlignment.spaceBetween?

If you have a Row in Flutter and set it to mainAxisAlignment: MainAxisAlignment.spaceBetween, all Children gets evenly spaced by the distances between each other.
However, if you are conditionally displaying Widgets in this Row ((boolean) ? Display : Container()), the empty Container actually counts as a Widget that takes space with this particular mainAxisAlignment, and you will see empty space instead of Flutter just ignoring it completely and realigning as if it does not exist.
How do I keep using MainAxisAlignment.spaceBetween and use a conditional empty Widget at the same time?
You shouldn't use
(boolean) ? Display : Container()
but simply
if (boolean) Display

Flutter performance: Padding on every widget vs padding on top level listview

Scenario 1: A ListView has 10 children widgets each with a symmetric horizontal padding of 20.0.
return ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Widget1),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Widget2),
// ...8 more like that...
],);
Scenario 2: A ListView has 10 children widgets without any padding. Rather a symmetric horizontal padding of 20.0 is applied to ListView itself.
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: ListView(
children: <Widget>[
Widget1,
Widget2,
// ...8 more like that...
],),);
Will there be more overhead (in UI thread) in Scenario 1? Or will it stay almost the same.
PS: Consider that each widget is different and ListView.builder is not an option.
As per my view you should use the padding property of list view:-
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
children: <Widget>[
Widget1,
Widget2,
// ...8 more like that...
],
);
In first scenario, suppose you have 10 children then 10*2=20 widgets will be rendered as padding is present on them so multiply by 2. Whereas in second case only 11 will be rendered. But the example i have shared will render only 10 widgets.
Ps:-Have ignored the count of listview widget(If wanna consider then simply add 1 count to all situations).
All answers are good here but lake one thing to tell.
// better one first
Scenario 2: Obviously lesser work done means more performant the code is. Here we have a direct rendering of 2 + 10 = 12 widgets so whenever the widgets are rendered 12 components are rendered by the flutter's Skia engine.
Now if you deep dive into state management or updating of widgets it means the tree building again from the root meaning on every successive run of build method you render only 12 components.
Whereas
Scenario 1: The problem here with performance is that it is rendering 1 + ( 2 * 10 ) = 21 widgets in a single run of the build method. So now if we see from the point where we need to rebuild the tree it has to do more work in removing 9 more widgets than Scenario 2 and again building 9 more widgets.
PS. You could also optimize some of the static widgets that don't require to be rebuilt again and again with the const keyword.
One more thing I would like to add this is not a heavy performance increment, what I mean to say is you may not be able to verify the boost with dev tools as well.
In general, the difference is so trivial that it should not be the deciding factor for you. Instead, you should determine which method makes more sense to your business logic, or improves your code readability, or both.
Strictly speaking, directly adding a padding to the ListView is slightly more performant, because slightly fewer calculations is needed. Also, it's worth noting that Padding widget, won't actually "draw an invisible container, draw some invisible gap, and draw its child in the middle" - this is NOT how it works. In reality it's far more efficient: In short, widgets are laid out in one-pass O(n) operation, by traversing the widget tree just once. When going down, it passes parent's constraints, and when coming up, it passes children's sizes. In the case of a Padding widget, it simply modifies the parent's constraint when passing down, so there is really almost no overhead to worry about.
It's also worth noting that ListView has padding property, so directly using that, is a 3rd option that you have not mentioned. Be aware that ListView's padding property does not behave the same as wrapping it with Padding widget either, and you might want to worry about SafeArea too. You can easily figure these out with some experiments. Again, all 3 methods have trivial performance cost, you should choose the one that makes the most sense to your business logic.
first scenario is better since your widget tree is smaller

Why does a ListView cause rendering error when used directly inside Column?

I used ListView inside Column and it causes a rendering error. When I wrapped ListView in Container or Expanded widgets, it works fine. What is the reason that causes the error?
Column(
children: <Widget>[
Expanded(
child: ListView(
children: <Widget>[
],
),
),
],
),
A ListView tries to fill all the available space given by the parent element (here column). But the column has no definite space defined.
Column's children should have a definite size. If you haven't used Expanded or shrinkWrap property of listview, it won't recognize. Hence will give you rendering error.
With Expanded, you defined that child can expand as much as it wants, giving the definite size.
Or
If you set it to shrinkwrap: true, the list will wrap its content and be as big as its children allow it to be.
So in both cases, the column will get a definite size to render.

Shrink TextFormField to fit only text

How can I shrink a TextFormField to fit only the text inside it and its associated prefix/suffix icons?
I'm trying to display a prefix (dollar) icon next to my number input. I want the field to align the the right of the screen. My TextFormField is inside a row:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('TITLE'),
Expanded(
TextFormField(
textAlign: TextAlign.end,
decoration: InputDecoration(
prefixText: '\$',
),
...
),
),
],
),
While this displays the numbers to the right, the TextFormField is expanding to fill all available space, and putting the prefix text on the left:
TITLE$_____________1000
I would like to add a spacer between the title and the form field, and let the form field occupy only the space needed to show its numbers:
TITLE<----spacer---->$1000
My attempts so far have only resulted in the spacer sharing spacing with the form field:
TITLE<--spacer-->$____1000
I want to style the prefix text differently than the field text, so I can't use an inputFormatter to add my prefix. There doesn't seem to be any way to tell the form field to draw the prefix text next to the field text. I suspect my issue with the prefix text is related to this bug: https://github.com/flutter/flutter/issues/18788
If there's a way to tell the form field to occupy the minimum amount of space needed, I can work around the prefix text bug.
I don't think you can shrink a TextFormField to fit only the text inside. Not easily anyway. I don't have the answer but maybe some pointers could also help ya.
First: you'd have to know what font you are using and calculate how long the text would be if you rendered it on the screen with the exact same font style as the input's. Once you have that, in theory, it should be possible to programmatically calculate the width of the input field.
Flutter uses RenderObject's to keep track of sizes. In fact, RenderBox is a child class of RenderObject. You can retrieve them by via GlobalKey.
If I remember correctly the API:
final gk = GlobalKey();
RenderBox box = gk.currentContext.findRenderObject();
Curious what solution you'll find. If you figure this out, please drop a response :)

Flutter Spacer vs Expanded

What is the difference between Expanded and Spacer in Flutter? Why Flutter team added Spacer when we already have Expanded?
Both Spacer and Expanded does the same job that is they fill up the remaining space in a widget like Row or Column. Before Spacer, we had to use
Expanded(child: Container())
And which is not very good. With Spacer we only have to use
Spacer()
So, using Spacer, we prevent us from writing more code and it also gives clear indication that we are just using it to fill up the remaining space. Both of them have same property of flex which defaults to 1. The only difference is we don't have child property which makes sense. In fact Spacer is
Expanded(child: SizedBox.shrink());
TL;DR
Spacer()
is equivalent to
Expanded(child: Container())
Expanded makes its child widget occupy space.
Spacer occupies space by itself.
If you look at the implementation of Spacer, you'll see that it's a wrapper around Expanded with a dummy child.
Is Spacer really necessary? Probably not. In an alternate universe, Expanded could have had an optional child argument instead.