GridView which automatically sets row height to the tallest grid item - flutter

The title is a simplified version of what I'm trying to achieve: a GridView which automatically sets row height based on a prototype grid item that I supply. The prototype item is a tallest possible item, for example if the grid items contain text, the prototype would contain a longest possible text.
A possible workaround is a ListView in which each row is wrapped in an IntrinsicHeight widget. But:
Row height is not necessarily uniform like in a GridView.
IntrinsicHeight is inefficient per the documentation. Not sure how much though, perhaps it's negligible for simple layouts. Ideally this "compute intrinsic height" operation would only be called for the one prototype row.

For setting custom row heights depending upon the item that is fed into the grid item you can consider using this flutter_staggered_grid_view plugin.
It provides different widgets for both normal grids and sliver grids and will take care of the random size of input. So whatever size of widget you feed to the grid it'll take the size of the item

As far as I understand, you want to dynamically set size for GridView item. There are two ways, calculate it manually and set every time based on your contents, or to use SliverGridDelegateWithMaxCrossAxisExtent. So in your GridView.builder you can add following attribute:
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent:
200.0, // maximum size of item (on small screens 1 item per row, on bigger as many as can fit with 200.0 px width)
childAspectRatio: 5 / 6,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
),
It gives you access to extra tools for managing the GridView items keeping the aspect ratio, it also has maximum size that it should not exceed

Related

Flutter: Differences between SliverList and SliverFixedExtentList

What are the differences between SliverList and SliverFixedExtentList in Flutter?
According to the documentation of each widget:
SliverList: "A sliver that places multiple box children in a linear array along the main axis."
SliverFixedExtentList: "A sliver that places multiple box children with the same main axis extent in a linear array."
It seems that we should use SliverFixedExtentList over SliverList if all children have the same main axis extent. However, what does "same main axis extent" really mean?
It means all children have the same height. If you are familiar with ListView, it has itemExtent property that does the same thing.
Essentially, if you cannot guarantee all list items will have "equal size on the main axis" (e.g. equal height on a vertical scrolling list, or equal width on a horizontal scrolling list), then we cannot know the "exact size" of each item in the list, until we load it. However, if you can guarantee that they will all have the same size, for example, 100 units in height, then we don't need to load each item to measure its size.
Knowing item size before loading them is very useful when you want to jump a large distance. For example, if you want to scroll 10,000 px down, and if we know each item is fixed at 200 px tall, then we can easily jump 50 items, just load the 51th item and display it. If we don't know that (if you cannot guarantee that) then we will have to literally lay out all items in-between to see where 10,000 px will land us.

GridView with fixed size children

I need a grid that contains children of fixed sizes.
GridView expands its children, which is not what I need. The grid should simply size itself according to it's children by setting the crossAxisCount.
My current solution is to use simple columns and rows, but that's so much boilerplate code for just a common widget.
You will have to do it dynamically, and supply the value of the crossAxisCount dynamically

How to set the height of a widget dynamically in flutter?

Container(
height: min(
widget.order.products.length * 20.0 + 10,
100,
),)
widget.order.products.length => will return me the list of dynamic products.
While viewing the list of these products, I want the set the height of the container dynamic which I'm not able to do with the "min" function.
Can anyone help me.
Thanks in advance.
This can be done by using an IntrinsicHeight:
https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html
A widget that sizes its child to the child's intrinsic height.
This class is useful, for example, when unlimited height is available and you would like a child that would otherwise attempt to expand infinitely to instead size itself to a more reasonable height.
The constraints that this widget passes to its child will adhere to the parent's constraints, so if the constraints are not large enough to satisfy the child's maximum intrinsic height, then the child will get less height than it otherwise would. Likewise, if the minimum height constraint is larger than the child's maximum intrinsic height, the child will be given more height than it otherwise would.
This van be put Around things like a listView, Column etc.

Should I use absolute pixel values in my Flutter widgets, or should I scale them to the screen?

I am coming to Flutter from a web background, where I am used to defining screen elements in terms of percentages of the height and width of the screen, or of elements that contain them.
I just completed a course.
Now that I am enthused and want to start building an app, I am a little confused, as the course only spoke of heights & widths in absolute pixel values. I can see this being problematic with different aspect rations, and especially with different orientations.
Is there a canonical approach to this? The official docs also seem to use absolute pixel values, so maybe I am missing a fundamental point.
A search suggests that I might use MediaQuery and then scale everything according to that. But, I don't see widespread use of that in code samples.
Is there a non-opinionated standard approach?
I am a little confused, as the course only spoke of heights & widths in absolute pixel values.
Actually, flutter uses density independent pixels (dp) for width/height arguments. dp actually scale with resolution, meaning 1 dp is displayed as the same PHYSICAL distance on every device. You don't have to worry about your elements being displayed at different sizes, just because the resolution of the screen they're on changes.
To be precise, flutter calls them logical pixel and:
By definition, there are roughly 38 logical pixels per centimeter, or about 96 logical pixels per inch, of the physical display.
So think about them as you would think about cm.
I am used to defining screen elements in terms of percentages of the height and width of the screen
Nonetheless, you might want to layout your widgets in a relative fashion (relative to the screen or the parent). For that purpose, flutter has different solutions:
Flexible
Expanded
Wrap
MediaQuery
LayoutBuilder
GridView
other layout options
Is there a non-opinionated standard approach?
It is a very opinionated question to begin with, but for example, Material design is a common standard for mobile-design. Flutters layout widgets are based around this approach.
But in the end, it is your design choice. For example, to achieve a responsive layout grid you could use Wrap, or you could use LayoutBuilder and determine yourself how you would like to layout rows and columns.
I would recommend you to scale widgets based on the size of the screen. This allows your application to be more flexible and adjust to various platforms and sizes such as large tablets or small phones. In order to do this, I recommend you to use the widget FractionallySizedBox which allows you to size widgets using a percentage of the screen size. For example, if you want a button widget to fill up 50 percent of a screen's width you can use the following code:
Container(
alignment: Alignment.center,
child: FractionallySizedBox(
widthFactor: 0.5,
child: FlatButton(
onTap: () {},
child: Text("PRESS HERE")
)
)
)
This code creates a button positioned in the center of the screen with a width of 50 percent of the screen size's width. You can also change the height of the button with the heightFactor field. By using this code the button widget will scale up and scale down for different screen sizes while still maintaining a size of half of the screen's width. For more resources, you should check out this video by the Flutter Team: https://youtu.be/PEsY654EGZ0 and their website on the FractionallySizedBox here: https://api.flutter.dev/flutter/widgets/FractionallySizedBox-class.html.
The FractionallySizedBox however is only one of many different approaches to making your flutter app fit to different screen sizes. Another approach is to use the AspectRatio Widget. Below is an example of this:
Container(
alignment: Alignment.center,
child: AspectRatio(
aspectRatio: 3/2
child: FlatButton(
onTap: () {},
child: Text("PRESS ME")
)
)
)
This code will create a button with a 3 to 2 ratio between its width and height. If the screen size changes the button will increase or decrease in size accordingly while again maintaining the 3 to 2 ratio. If you want more information the Flutter team also has a video on it (https://youtu.be/XcnP3_mO_Ms) along with some documentation here:(https://api.flutter.dev/flutter/widgets/AspectRatio-class.html).
Both widgets are perfectly fine and are considered standard practice to use but I personally use FractionallySizedBox more.
I hope my answer was helpful.

Flutter - How to remove margin under GridView?

I get a strange margin under my GridView:
This image is from an IOS simulator, here's how it looks on a smaller screen on Android where the margin appears to be gone or a lot smaller:
Here's the code:
Column(
children: [
GridView.count(
shrinkWrap: true,
crossAxisCount: 8,
children: tiles
),
Text('mamma')
]
)
Each element in the grid (tiles) is an EmptyTile widget:
class EmptyTile extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: bgColor,
border: Border.all(color: borderColor)
)
);
}
}
I really can't figure out what this margin is or where it comes from, whether it has something to do with shrinkWrap or something else.
How can I remove this margin?
EDIT:
As requested here's the fullscreen images without the simplified example.
IOS:
Android:
Try this. by default it has padding and you should set padding to zero.
GridView.builder(
padding: EdgeInsets.all(0),
The difference in layout you experience comes from a combination of things:
How GridView manages its size.
How widgets are placed in a Column.
GridView tiles and size
When you build a GridView, its width and height are implicitly deduced from the crossAxisCount parameter and the constraints given by its parent.
The constraints are the limits in size in which the widget is allowed to draw.
The crossAxisCount defines how many tiles should fit in one line (row or column). Depending on its scrollDirection, it will either try to fill all the available width or height. By default this direction is set to Axis.vertical, which means that the width will be filled.
So when we come back to your example, this means that the size of each tile will depend on the width of the Column containing your grid, divided by the number of tiles you set in crossAxisCount. This size will be both the width and the height of every tile in your GridView.
Once Flutter knows the size of each of your tiles, it sets them on each row, until all tiles are placed or there isn't any available space.
Column layout
Now that we know more about GridView, we need to understand how Column builds its layout.
Columns allocates space to each widget in its children, following an algorithm best describes in the docs.
tl;dr:
Your GridView will only fill its own height in the Column, leaving the rest as free space. This is why you get empty space in your Column.
Possible fix
I actually don't really see how this is a problem. GridViews are supposed to only extend to display their children, so it totally makes sense for it to stop when it completed.
The thing is, most of the times this ind of grids are not used with a finite list of children, and more likely with a growing list.
If you only want to have one line of tiles, that will extend using the available space, you could use a simple Row.
If you want multiple lines of tiles, with non-square tiles, you need to dive a little deeper into GridView.custom.
Edit after question was updated with more screenshots:
It is possible that you need to rethink your layout so that the player panels are in the same Column than the game board. You will have a much better control over the layout this way.