Should i use less complex widgets for best flutter performance? - flutter

I prefer to use a Column with SizedBox to build space layout ! But my boss keep saying that i should use a Container with padding because container is less complex widget and should perform better. He always says that i should avoid stack widget because is a complex widget. But i think i remember from a flutter video that doesn't matter which widgets you choose, because flutter optimizes it when it builds the three trees ! Is that true ? should i spend time optimizing my widgets with less complex widgets as possible ?
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 24),
Text(
text,
),
],
);
Container(
padding: EdgeInsets.only(top: 24),
child: Text(
text,
),
);
In Flutter padding desing discussion they said that doesn't matter if you use container with padding or specific padding widget. But i'm still in doubt if i should use less complex widget in general.

Related

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

Dynamic height of a column's child

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...)

ButtonStyle padding different from container padding

Why is it that a ButtonStyle's padding property is not equivalent to a Container's padding property in terms of pixels?
I would expect the TextButton and Container text to be aligned when placed together here:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.all(24)),
),
onPressed: () => print('pressed!'),
child: Text('My Button'),
),
Container(
padding: EdgeInsets.all(24),
child: Text('Some other text'),
)
],
);
Both the TextButton and Container theoretically have taken the same padding amount, and yet the texts do not line up. Note the "S" from the Container text does not line up with the "M" from the TextButton, despite them both having the same amount of padding:
Note that the Column's crossAxisAlignment has been set to "start" such that both widgets should align to the left-most side.
Am I missing something about the ButtonStyle's padding property?
Update
I can confirm that iOS and Android does not have the same issue. This issue seems to be isolated to Web and Desktop (macOS/Linux). I have not tested Windows.
So it seems that this is a bug and has been tracked here on GitHub.
As per the comments, a fix was pushed to the master channel of releases in March and I presume will be available in the next 2.3.0 stable release.
For those who want to switch to the latest master to double check that it's working as it should; run:
$ flutter channel master
$ flutter upgrade
You may also need to reinstall any packages before running.

How to set the height of the Container of GridView to "auto" in Flutter

A recurring issue I have in my Flutter learning journey, is that GridView does require a parent container with a specified height.
What is the best practice to maximize the height so that it stretches out as much as needed without always incurring in overflow issues or unpleasant UI?
Here are a few methods I tried:
double.infinity - This one doesn't work properly, GridView doesn't show at all.
Container(
height: double.infinity,
child: GridView.builder(
....
)
MediaQuery.of(context).size.height - This one doesn't work properly either, overflow issues occur and just doesn't work completely when other elements are displayed (for example in a column).
Container(
height: MediaQuery.of(context).size.height,
child: GridView.builder(
....
)
fixed size - This one doesn't allow any flexibility at all and isn't ideal for responsiveness too.
Container(
height: 800,
child: GridView.builder(
....
)
Thank you.
Instead of using Container try to use Flexible widget.

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.