GridView in Flutter is a scrollable, 2D array of widgets that displays a list of items as a grid by combining row and column classes. Also, it’s commonly used to show a list of photos or any other items we want. For example, you can use GridView to display products, images, posts, etc. Not to mention, it can contain any type of widgets including text, icons, images, etc based on the user requirements.
GridView
has various types that can be implemented in different cases and scenarios. These types are as the following:
- GridView.builder
- GridView.count
- Gridview.custom
- GridView.extent
GridView is a more complicated version of ListView as it comes in a tabular form which gives it an interesting and professional look.
In this tutorial, we’ll discuss the 4 types of GridView
as well as a special type of GridView known as Staggered GridView and walk through practical examples to learn the difference between them so you can start implementing GridView
in your Flutter applications.
So without further ado, let’s start our tutorial!
Table of Contents
- GridView Overview
- GridView.builder
- GridView.count in Flutter
- GridView.extent in Flutter
- GridView.custom in Flutter
- Staggered Grid View
- Conclusion
GridView Overview
To begin with, GridView
has some common properties that we need to understand in order to implement it correctly. The most important property which is used with all types of GridView
is gridDelegate
.
1 gridDelegate
a mandatory property which controls how the items within the grid are displayed. There are 2 main types of gridDelegate
known as SliverGridDelegateWithFixedCrossAxisCount
and SliverGridDelegateWithMaxCrossAxisExtent
.
Now, let’s go through each one of them separately
SliverGridDelegateWithFixedCrossAxisCount
This creates a fixed number of tiles in the cross axis. For example, if the grid is vertical, this delegate will create a layout with a fixed number of columns. If the grid is horizontal, this delegate will create a layout with a fixed number of rows. It creates grids with equally sized and spaced tiles.
It has 5 properties which are:
- crossAxisCount: a required properties that specifies the number of tiles within the grid cross axis (default is horizontal).
- childAspectRatio: The ratio of the cross-axis to the main-axis extent of each child. In other words, the ratio of the width to the height (width:height)
- crossAxisSpacing: the space between the children in the cross axis.
- mainAxisSpacing: the space between the children in the main axis
- mainAxisExtent: the size of each child in the main axis. It define the logical pixels taken by each tile in the main-axis.
GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( childAspectRatio: 1, crossAxisSpacing: 16, mainAxisSpacing: 16, crossAxisCount: 2, ), children: [ Container(color: Colors.red,), Container(color: Colors.blue,), Container(color: Colors.yellow,), Container(color: Colors.green,), ], ),

SliverGridDelegateWithMaxCrossAxisExtent
This creates grid layouts with tiles that each have a maximum cross-axis extent with equally sized and spaced tiles. For example, if the grid is vertical with a width of 500 pixels and the maximum cross extent is 150, the delegate will create a grid of 4 columns that are 125 pixels wide.
As a matter of fact, it has similar properties to SliverGridDelegateWithFixedCrossAxisCount
but instead of crossAxisCount
it has maxCrossAxisExtent
- maxCrossAxisExtent: specifies the maximum extent (size) of the tile in the cross axis.
GridView( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 120, ), children: [ Container(color: Colors.red,), Container(color: Colors.blue,), Container(color: Colors.yellow,), Container(color: Colors.green,), ], ),

2 crossAxisSpacing
This is an optional property that defines the number of pixels (space) between the grid children in the cross Axis. Take note that cross axis is determined based on the scroll direction of the grid. By default, the cross axis is the vertical direction of the grid which you can change using the scrollDirection
property.
scrollDirection: Axis.vertical, // or scrollDirection: Axis.horizontal,
3 crossAxisCount
This is another optional property that specifies the number of widgets in the cross axis (columns by default). You can use this property if you want the grid to display a specific number of widgets within the cross axis.
crossAxisCount: 2
4 mainAxisSpacing
This is a similar property to crossAxisSpacing but it determines the number of pixels between the grid in the main axis (space between rows by default)
mainAxisSpacing: 4.0
GridView.builder
GridView.builder
in Flutter creates a scrollable 2D array of widgets on demand. It is suitable for grids with large amount of content as the builder only creates those children who are visible to the user. In other words, GridView.builder
creates dynamic widgets using either SliverGridDelegateWithFixedCrossAxisCoun
t or SliverGridDelegateWithMaxCrossAxisExtent
. gridView.builder
has few special attributes which are:
- itemCount: defines the number of children to be displayed within the grid.
- itemBuuilder: used to create the content of the grid based on the number of widgets specified in
itemCount
. This is only called when indices is > 0 and <itemCount
.
GridView.builder constructor
GridView.builder( {Key? key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController? controller, bool? primary, ScrollPhysics? physics, bool shrinkWrap = false, EdgeInsetsGeometry? padding, required SliverGridDelegate gridDelegate, required IndexedWidgetBuilder itemBuilder, int? itemCount, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, double? cacheExtent, int? semanticChildCount, DragStartBehavior dragStartBehavior = DragStartBehavior.start, ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, String? restorationId, Clip clipBehavior = Clip.hardEdge} )
Example
GridView.builder( itemCount: 6, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0 ), itemBuilder: (BuildContext context, int index) { return Container( color: Colors.red, child: Center( child: Text( (index + 1).toString(), style: TextStyle(fontSize: 30), ), ), ); }, )

GridView.count in Flutter
GridView.count
is the mot commonly grid used by Flutter developers and it’s the easiest one to implement. This is because we already know the number of widgets we need to generate. It allows you to create a specific number of columns and rows within the grid. GridView.count
has a mandatory property which is crossAxisCount
which is as previously mentioned, used to determines the number of widgets within the cross axis (columns). In addition, it is easier to implement as it doesn’t require you to use the gridDelegate
property.
GridView.count
is commonly used to create a keypad UI as you need a specific number of widgets in both horizontal and vertical directions.
GridView.count constructor
GridView.count( {Key? key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController? controller, bool? primary, ScrollPhysics? physics, bool shrinkWrap = false, EdgeInsetsGeometry? padding, required int crossAxisCount, double mainAxisSpacing = 0.0, double crossAxisSpacing = 0.0, double childAspectRatio = 1.0, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, double? cacheExtent, List<Widget> children = const <Widget>[], int? semanticChildCount, DragStartBehavior dragStartBehavior = DragStartBehavior.start, ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, String? restorationId, Clip clipBehavior = Clip.hardEdge} )
Example
GridView.count( crossAxisCount: 3, mainAxisSpacing: 4, crossAxisSpacing: 4, children: [ Container( color: Colors.red, ), Container( color: Colors.yellow, ), Container( color: Colors.green, ), ], )

GridView.extent in Flutter
GridView.extent
creates a scrollable 2D array of widgets with children that each have a maximum cross-axis extent. It uses SliverGridDelegateWithMaxCrossAxisExtent
as its gridDelegate
. In other words, it allows you to create widgets with equal size and space. Also, it has a required property which is maxCrossAxisExtent
.
GridView.extent constructor
GridView.extent( {Key? key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController? controller, bool? primary, ScrollPhysics? physics, bool shrinkWrap = false, EdgeInsetsGeometry? padding, required double maxCrossAxisExtent, double mainAxisSpacing = 0.0, double crossAxisSpacing = 0.0, double childAspectRatio = 1.0, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, double? cacheExtent, List<Widget> children = const <Widget>[], int? semanticChildCount, DragStartBehavior dragStartBehavior = DragStartBehavior.start, ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, String? restorationId, Clip clipBehavior = Clip.hardEdge} )
Example
Now in the example below, you’ll notice that only 2 widgets are created in the cross axis. This is because we specified the maxCrossAxisExtent to be 300 therefore the screen size cannot display more than two widgets within the row. If you change the value to be less, for example 100, then more widgets will be displayed within one row.
GridView.extent( maxCrossAxisExtent: 300, crossAxisSpacing: 4, mainAxisSpacing: 4, children: [ Container( color: Colors.red, ), Container( color: Colors.yellow, ), Container( color: Colors.green, ), Container( color: Colors.blue, ), Container( color: Colors.deepPurple, ), ], )),

GridView.custom in Flutter
GridView.custom
creates a scrollable 2D array of widgets with custom SliverGridDelegate
and SliverChildDelegate
. It has two required properties which are gridDelegate
and childrenDelegate
. As previously mentioned, gridDelegate
is used to control the arrangement of the widgets within the grid using either SliverGridDelegateWithFixedCrossAxisCount
or SliverGridDelegateWithMaxCrossAxisExtent
. childrenDelegate
is used to create the children for the grid.
GridView.custom constructor
const GridView.custom( {Key? key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController? controller, bool? primary, ScrollPhysics? physics, bool shrinkWrap = false, EdgeInsetsGeometry? padding, required SliverGridDelegate gridDelegate, required SliverChildDelegate childrenDelegate, double? cacheExtent, int? semanticChildCount, DragStartBehavior dragStartBehavior = DragStartBehavior.start, ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, String? restorationId, Clip clipBehavior = Clip.hardEdge} )
Example
GridView.custom( childrenDelegate: SliverChildBuilderDelegate((BuildContext context, int index) { return Container(color: Colors.red,); }, childCount: 3 ), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 4, mainAxisSpacing: 4, ), )
Staggered Grid View
Staggered GridView in flutter creates widgets in varying height and width which gives the grid a unique look by displaying its children in different sizes. This is commonly used to display pictures on social media platforms like Pinterest and phone gallery. In other words, you can have widgets in both portrait and landscape orientation. It has multiple types and layouts such as StaggeredGridView.count, StaggeredGridView.extent, etc.
Now, we’ll only cover StaggeredGridView.countBuilder
while we’ll discuss the other types in future posts.
you can consider StaggeredGridView.countBuilder as a mix of the normal GridView.count and GridView.builder with additional feature (the varying tiles sizing). It creates widgets dynamically on demand with a fixed number of tiles within the cross axis in different sizes.
StaggeredGridView.countBuilder properties
- staggeredTileBuilder: returns a staggeredTile which has several constructors that controls the tiles orientations. These constructors are:
- StaggeredTile.count : creates the tiles based on the specified number of cells in both cross and main axis.
- StaggeredTile.fit : creates tile based on its original size.
- StaggeredTile.extent : creates tiles based on a specific extent (size)
- crossAxisCount: defines the number of widgets in the cross axis.
- mainAxisSpacing and crossAxisSpacing: defines the space between the grid children in the main and cross axis.
- itemCount and itemBuilder: itemCount is to define the number of widgets to generate while itemBuilder is to generate these widgets based on itemCount value.
Example
First of all, you need to add the StaggeredGridView dependency in the pubspec.yaml file and run pub get
flutter_staggered_grid_view: 0.4.1
Next, you need to import the library in the dart file where you’ll create the staggered grid view
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
Now, we can start implementing the staggered grid view
StaggeredGridView.countBuilder( crossAxisCount: 2, mainAxisSpacing: 4, crossAxisSpacing: 4, itemCount: 20, padding: EdgeInsets.only(left: 10, right: 10, top: 5), itemBuilder: (context, index) { return Container(color: index %2 == 0? Colors.lightBlue[300]: Colors.blueGrey, child: Center( child: Text(index.toString(), style: TextStyle(fontSize: 30, color: Colors.white, fontWeight: FontWeight.bold), ), ),); }, staggeredTileBuilder: (index) { return StaggeredTile.count(1,index.isOdd? 1.5 : 2); })

Conclusion
This brings an end to our interesting tutorial where we learnt all about GridView layout and its various types in addition to Staggered Grid View.
These are some of the essential widgets that every Flutter developer should know about and learn how to implement. Make sure to practice them well and understand how they differ from each other.
If you wish to learn more about Flutter, I have a whole category called Flutter that’s ripe for you to explore. You can learn about Flutter widgets, layouts, onboarding screen, tabbar, inkwell and gesture detector, stepper and practice with a bunch of real examples.
Also, don’t forget to like and share my Facebook page, share the post with those who care to learn, and subscribe to my blog to be one of the firsts to know about my newest posts!
Thank you!