An in-depth look at the GridView widget

The GridView class is described as a scrollable 2-dimensional array of widgets and offers up to five constructors to create such an array. In the GridView class page, you’ll find a simple example displaying a grid layout composed of two columns. Each column has three rows of verse from the Broadway musical, Hamilton.

Image for post
Image for post
GridView Sample

Like the widgets, CustomScrollView and ListView, the GridView’s parent class is the class, ScrollView. ScrollView is essentially a widget that can scroll its content. Below is a graphic depicting the class hierarchy involved. Many of the named parameters and properties passed to the GridView widget when created goes all the way back to be used by its parent class, ScrollView.

Image for post
Image for post

Construct Your Grid

Again, there are five constructors in all to choose from when it comes to creating the GridView widget. This article will review what each constructor provides to the user as well as quickly describe the purpose of the many named parameters supplied in each. You can see in the screenshot below, for example, the four named constructors share about the first eleven parameters of the original generative constructor, GridView(), but then have a varying set of parameters for other specific purposes.

Image for post
Image for post
Image for post
Image for post
The Five Constructors of the GridView Widget

Screenshots Only. Click For Gists.

As always, I prefer using screenshots over gists to show code in my articles. I find them easier to work with, and easier to read. However, you can click/tap on them to see the code in a gist or in Github. Ironically, it’s better to read this article about mobile development on your computer than on your phone. Besides, we program mostly on our computers; not on our phones. For now.

No Moving Pictures No Social Media

Note, there will be gif files in this article demonstrating aspects of the topic at hand. However, it’s said viewing such gif files is not possible when reading this article on platforms like Instagram, Facebook, etc. Please, be aware of this and maybe read this article on medium.com

Let’s begin.

Image for post
Image for post
Other Stories by Greg Perry

Count The Grid

The example in the documentation uses one of the most common variations of the GridView widget: GridView.count. This one creates a grid layout with a fixed number of ‘child widgets’ listed along its cross axis. Note, when it comes to grid layouts, the ‘main axis’ aligns with the scroll direction (usually vertically up-and-down) while the horizontal direction defines the cross axis.

Image for post
Image for post
The grid layout’s main axis and cross axis.

And so, the named parameter, crossAxisCount, in this case is set to the integer value, 2, giving the grid layout two columns. The children (a List of Widgets) are spaced apart using the crossAxisSpacing and mainAxisSpacing properties — both assigned a ‘logical pixel amount’ of 10. A screenshot of the code that makes up this GridView example is listed below as well as a shortlist describing the remaining named parameters used to define it.

bool primary
- If true, it’s ‘Scroll Controller’ is obtained implicitly by the framework.

final EdgeInsetsGeometry padding;
- The amount of space to surround the whole List of Widgets.

final double crossAxisSpacing;
- The number of logical pixels between each child listed along the cross axis.

final double mainAxisSpacing;
- The number of logical pixels between each child listed along the main axis.

Image for post
Image for post
GridView Sample

Your Primary Behaviour

The default setting for the parameter, primary, is true. You may want to turn to a previous article, Decode ListView, for further information on this parameter and its association with the ‘Scroll Controller.’ After all, the ListView and GridView widget share a common parent class, ScrollView.

The gif files running below demonstrate if we ‘comment out’ the primary parameter set to false making its default value of true be selected. This results in the scrolling behaviour of displaying the ‘over-scroll indication glow’ typically seen, in this case, on Android phones. It would seem, since the simple example had its ‘children widgets’ all displayed on one screen, there was no need for scrolling and so the primary parameter was set to false.

Remove Some Padding

As you see in the screenshots below, it’s quickly apparent what happens if the named parameter, padding, is commented out in the simple example. There was a ‘border’ of some 20 logical pixels surrounding the little green boxes. Note, ‘Logical pixels’ are roughly the same visual size across devices while ‘Physical pixels’ are the size of the actual hardware pixels on the device. Logical pixels is a means to be device-independent you see.

With No Spacing

The space between the little green blocks in the simple example is provided by the named parameters, crossAxisSpacing, and mainAxisSpacing. As you see, simply removing those parameters will remove, as a consequence, the spaces between those green blocks that make up the List of widgets in the GridView.

Going Horizontal

As hinted earlier, the default ‘Scroll Direction’ for GridView widgets is vertical. Setting it, however, to a horizontal direction will now allow for scrolling in the example, but it’s ‘back and forth’ as you can see by the gif file displayed below.

Put It In Reverse

It’s pretty apparent in the simple example of what happens if you set the named parameter, reverse, to true. As you see below, the List of Widgets is simply listed in the ‘opposite’ direction along the main axis. The default value, of course, is set to false.

The Physics Of Scrolling

The next parameter we’ll look at is called, physics. It determines how the scrollable list will behave when the user reaches the start or the end of the scroll and how it behaves when the user ‘let’s go’, and it continues scrolling for a time. It takes in a class object of type, ScrollPhysics. Our simple example doesn’t have enough to allow for scrolling, and so, I’ve turned to the Cookbook example, Working with Long Lists, — again utilizing the ListView widget to demonstrate the scrolling behaviour.

Currently there’s four subsclasses of the class, ScrollPhysics, — three of them are demonstrated below. The first one emulates the behaviour seen in Apple iPhones, BouncingScrollPhysics. The second class, ClampingScrollPhysics, gives the over-scroll indication glow — typically seen on Android phones. The last one demonstrated is called, NeverScrollableScrollPhysics, disabling scrolling capability completely. The one not on display is, AlwaysScrollableScrollPhysics. It provides the appropriate behaviour depending upon the platform: Android or iOS.

Image for post
Image for post
Image for post
Image for post
Image for post
Image for post

Wrap It Up

Once again, we’ll look to a ListView example to convey what happens when the property, shrinkWrap, is set to true and not left at its default setting of false. It’s readily apparent what happens with the use of a bold red border.

It’s stated in the documentation, “ If the scroll view does not shrink wrap, then the scroll view will expand to the maximum allowed size in the [scrollDirection].” — In the example above, it’s in the vertical direction. If you set the property, shrinkWrap, to true as we’ve done above, the scrollable list (or scroll view) will wrap its content and be as big as it children widgets will allow.

The documentation further notes there’s a penalty in performance with this property set to true: “ Shrink wrapping the content of the scroll view is significantly more expensive than expanding to the maximum allowed size because the content can expand and contract during scrolling, which means the size of the scroll view needs to be recomputed whenever the scroll position changes.”

Few More To Go

We’ll continue to go through the list of named parameters and describe their use, but we’ll do so by now introducing the four constructors that can be used to create a GridView widget.

Image for post
Image for post
GridView.count

Learn By Example

What we’ll do now is implement the very same simple example with the remaining constructors offered by the GridView class. Doing so will emphasize the similarities, and more so, the differences between these five constructors.

Delegate Your Grid

The original generative constructor, GridView(), requires a class object explicitly passed to its named parameter, gridDelegate. It’s to control the layout of the children within the GridView, and it extends the class, SliverGridDelegate.

The constructors, GridView, GridView.builder, and GridView.custom explicitly require such a ‘grid delegate’ to be passed to the named parameter, gridDelegate. While the other constructors, GridView.count and GridViewe.extent, creates such a class object for you. For example, the class, SliverGridDelegateWithFixedCrossAxisCount, is assigned to the GridView.count constructor and creates a layout with a fixed number of widgets along the cross axis. The class, SliverGridDelegateWithMaxCrossAxisExtent, creates a layout of widgets along the cross-axis given the maximum extent of ‘space’ along that axis, and is assigned to the constructor, GridView.extent. Below is a screenshot of the GridView.count constructor. You can see it’s ‘delegates’ being created for it.

Image for post
Image for post
GridView.count

Examples of the Simple Example

We’ll now look at how the other constructors can be used to produce the same results with regards to the simple example. You can see below (displayed on the left), the GridView’s ‘grid delegate’ takes in the properties, crossAxisSpacing and mainAxisSpacing, that are involved in the spacing the widgets. It also takes in the ‘cross axis count’ and so you see the integer, 2. Finally, it takes in the explicit List of already defined widgets to be displayed in the GridView.

Looking closely at the right at the GridView.builder implementation, it too has to assign a ‘grid delegate.’ It’s logical to use the very same one used by GridView. However, you can further see it instead involves a named parameter called, itemCount, and a ‘switch-case’ statement is being used to display the defined widgets depending on the index value. You can tell it’s not the most efficient logic for such a simple example. That’s because this constructor is more appropriate for grids with a large (or infinite) number of widgets to display. Widgets, in fact, that aren’t yet defined, but instead defined on the fly! You see, this constructor’s ‘itemBuilder’ routine (see below) is only called to define those widgets that are to be currently visible.

Delegate Your Children

The constructor, GridView.custom, requires yet another ‘delegate’ class be used. One that supplies the ‘visible widgets’ displayed in the GridView at any one time. Again, the constructor, GridView.count, is given its own ‘Child Delegate’ and doesn’t need to worry about this. The screenshot of the GridView.count constructor below shows it uses a class is called, SliverChildListDelegate which extends the class, SliverChildDelegate. It’s this class that takes in the defined List of widgets to then display them appropriately while the GridView is scrolling through them.

Image for post
Image for post
GridView.count

When you’re given an explicit List of widgets already defined, it’s suggested you use the class, SliverChildListDelegate — as does the constructor, GridView.count. Although, it is inefficient to have an explicit List already defined. Particularly if it’s a really large one. After all, only so many can be viewed at any one time in the GridView. The remaining are just taking up memory. To avoid creating more widgets than are visible, it’s recommended you instead use the class, SliverChildBuilderDelegate — as does the constructor, GridView.builder. See a pattern here? Below is a screenshot of the constructor, GridView.builder.

Image for post
Image for post
GridView.builder

Just a Sliver

When viewing widgets in a GridView, you’re seeing what Google calls are ‘the slivers’ in the Gridview’s viewport. So in other words, the ‘Sliver Child Delegate’ class described above is responsible for creating the ‘slivers’ (the visible widgets) for the GridView’s viewport.

Inside the SliverChildListDelegate class (see below), the List of widgets is, in turn, brought in to create their corresponding Sliver object. In the code, each Widget is referenced by the variable, child, and each is further ‘wrapped’ or encased in an additional class object depending on the boolean values of the three named parameters: addAutomaticKeepAlives, addRepaintBoundaries, and addSemanticIndexes. You can see this below.

Image for post
Image for post
SliverChildListDelegate

A Few More Parameters

These three are among the few remaining we’ve yet to examine, we’ll quickly do so now leaving only then three more to go.

addRepaintBoundaries
By default it’s set to true. Doing so means the Slivers are not ‘repainted’ if and when they are scrolled back into view. As it is, with this simple example, this named parameter could easily be set to false as they don’t scroll at all and so there’s no ‘repainting’ required.

addAutomaticKeepAlives
If set to true, the Slivers are preserved in memory when they would otherwise be garbage collected when scrolled off-screen and out of view. This is set to true by default but should be considered set to false to release the memory of very very large lists.

addSemanticIndexes
Allows acoustic software to announce the widget values when used by the visually impaired. It’s default value is also set to true implementing the indexing necessary to allow for such software to work properly.

A Simple Example Sample

Let’s take a look at how the constructor, GridView.custom, would be used to recreate our simple example. As you see below, the class, SliverChildListDelegate, is passed the List of widgets. Pretty straight-forward. When would you use the constructor, GridView.custom? Personally, it’s not readily apparent that I would. It would only be when I need a custom ‘SliverChildDelegate’ for one reason or another, and that’s yet to happen.

To What Extent?

The last constructor we’ll look at is the GridView.extent. Like, GridView.count, it’s more commonly used, and you can see above how it is implemented to convey our simple example as well. Pretty straight forward. However, what is the named parameter, maxCrossAxisExtent, all about? Well, we can quickly see the part it plays if you double the current value from 200 to 400. See below.

We see there’s not enough room now for two columns but only one column of bigger green boxes is displayed. Consequently, you can scroll up and down through those boxes now. Unlike, the GridView.count constructor with it’s ‘cross axis count’ explicitly passed to it (the number 2 in our case), the GridView.extent actually ‘calculates’ the count in its ‘grid delegate, SliverGridDelegateWithMaxCrossAxisExtent. In the screenshot below, the count calculated is the number 1. Note, the ‘available’ number of pixels that make up the cross axis of a particular device is found in the property, constraints.crossAxisExent. You can see that value for my device below.

Image for post
Image for post
SliverGridDelegateWithMaxCrossAxisExtent

Essentially you’re breaking up the number of available pixels along the cross axis by the ‘extent’ number specified plus any cross-axis spacing. In our case, the cross axis spacing is set at 10. Watch what happens when I get rid of the specified padding giving the widget more space to work with.

Notice the ‘available’ number of pixels on the cross axis has increased allowing the ‘extent’ value to produce a count of 2 — returning to two columns.

Image for post
Image for post
SliverGridDelegateWithMaxCrossAxisExtent

Need Some Cache

The named parameter, cacheExtent, describes how many pixels the cache area extends before the leading edge and after the trailing edge of the viewport. Slivers that fall in this cache area are created in memory (when possibly scrrolling into view) or preserved in memory (when scrolled out of view) even though they are not visible on the screen.

Flutter actually supplies a default ‘cache’ value of 250.00. Depending on your specific needs, you are allowed to adjust that amount. However, it can’t be zero or less.

Image for post
Image for post
RenderAbstractViewport

It’s A Drag

The last named parameter, dragStartBehavior, can have one of two possible enumerated types: DragStartBehavior.start and DragStartBehavior.down. The default is set to set to DragStartBehavior.start. It means that ‘scrolling drag behaviour’ will begin upon the detection of a drag gesture. If set to
DragStartBehavior.down, the ‘scrolling drag behaviour’ will begin when a mouse click down event is first detected. It’s said, setting this to DragStartBehavior.start will make the drag animation smoother. While the DragStartBehavior.down, it’s said, will make ‘scrolling drag behaviour’ feel slightly more reactive.

What’s Your View?

In the end, it would seem the constructor, GridView.count, was the most appropriate one to use for such a simple example. With more elaborate grid layouts and or ones with an immense number of widgets to display, the constructors, GridView.builder and even GridView.custom would be better choice.

With all the constructors, it all comes down to generating both the ‘grid delegate’ (defines the grid layout) and the ‘child delegate’ (widgets into Slivers) and then supplying them finally to the widget, SliverGrid, that’s responsible for creating the ‘Grid View’ that you see.

Image for post
Image for post
GridView class

TL;DR

Allow me to refer you to another example written by Srikanth regarding the GridView and it’s many constructor variations. Simply another simple example you could download and play with.

I did make a slight variation of his example by merely confining the ‘children widget’ content found consistently in every GridView example into a property called, sampleContainer. You can then see the ‘differences’ in each in a shorter stretch of code in the example code you could download.

Image for post
Image for post
gridview_demo02.dart

Finally, as stated in the GridView documentation, “If GridView is no longer sufficient, for example, because the scroll view is to have both a grid and a list, or because the grid is to be combined with a SliverAppBar, etc, it is straightforward to port code from using GridView to using CustomScrollView directly.”

CustomScrollView does indeed allow you a few more options, but GridView will certainly remain a prominent tool in your Flutter toolkit.

Cheers.

  • Source code dated November 27, 2019
Image for post
Image for post
Decode Flutter Series

Additional articles that may be of interest to you.

Image for post
Image for post
Image for post
Image for post
Image for post
Image for post

Written by

Freelance Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store