I wanted to convert a
ListView into a
GridView so that it would show more information on a large screen such as Web.
ListView code looked like this:
return ListView.builder( physics: scrollable ? null : NeverScrollableScrollPhysics(), shrinkWrap: shrinkWrap, padding: EdgeInsets.only(top: 16.0), itemCount: words.length, itemBuilder: (context, index) => buildListItem(context, words[index]), );
I found that GridView used almost the same parameters, and took on top of that a gridDelegate.
The doc says that for large or infinite grids, you should use the builder constructor. Good, that's what I'm looking for. Also for gridDelegates, they list two of them:
Looking at the description for
MaxCrossAxisExtent, which says it'll divide columns if the width of the item is larger than a threshold, this is perfect for me! And here I thought I'd need a LayoutBuilder and do width/column/spacing maths.
So I tried introduced the
GridView.builder with the delegate, and ran it. It automatically created the columns as expected. However, the height of the grid items was all wrong. I thought it had to do with how my items had some Columns in them. I tried to set the Columns'
mainAxisSize to min. It did nothing.
After googling it, I realized that it was because by default, the
childAspectRatio is 1, so it will make square tiles. See two results:
Judging by these posts, it seems like Flutter doesn't provide a way to set a height besides computing an aspect ratio yourself. Some users provide some fancy maths, while one other person suggested to copy/paste reimplement their own version of a delegate like
Out of curiosity, instead of reimplementing the whole class, I wanted to see if it was possible to extend the desired class, and override the
getLayout method to use the same code, except for the height value.
As I browsed through the code, I found this (source code link):
final double childMainAxisExtent = mainAxisExtent ?? childCrossAxisExtent / childAspectRatio;
Wait, so there's a way to not use childAspectRatio? What is this mainAxisExtent? It turns out, it's an optional parameter, and it does exactly what we wanted (source code link):
/// The extent of each tile in the main axis. If provided it would define the /// logical pixels taken by each tile in the main-axis. /// /// If null, [childAspectRatio] is used instead. final double? mainAxisExtent;
With hindsight, the parameter was in the documentation all along. https://api.flutter.dev/flutter/rendering/SliverGridDelegateWithMaxCrossAxisExtent/mainAxisExtent.html
Here's the final piece of code:
return GridView.builder( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 500, mainAxisExtent: 77, ), physics: scrollable ? null : NeverScrollableScrollPhysics(), shrinkWrap: shrinkWrap, padding: EdgeInsets.only(top: 16.0), itemCount: words.length, itemBuilder: (context, index) => buildListItem(context, words[index]), );