Pinkesh Darji I love to solve problems using technology that improves users' lives on a major scale. Over the last seven-plus years, I've been developing and leading various mobile apps in different areas.

How to create a grid list in Flutter using GridView

4 min read 1331

Flutter Logo

When developing apps in Flutter, you may encounter the need to display a list of items as a grid. You can show data in a grid format — i.e., with rows and columns — using the GridView class in Flutter.

Using GridView is the best way to combine the Row and Column classes and create a scrollable grid list. A common use case is showing a list of photos, such as in the Google and Apple native photo apps.

In this tutorial, we’ll demonstrate how to implement GridView in your Flutter app. We’ll also walk through some practical examples so you can see GridView in action.

We’ll cover the following in detail:

If you’re a visual learner, check out this quick video tutorial:

What is GridView?

In Flutter, GridView is a widget that displays a list of items as a 2D array. In simple terms, the items are shown in a table format.

Unlike a normal list, in which items are rendered only in one direction, GridView renders items both horizontally and vertically. The figure below represents how GridView is different from a normal list in a Flutter app:

GridView in Flutter

Here is the minimal code to get GridView up and running:

We made a custom demo for .
No really. Click here to check it out.

GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
  ),
  children: [
    Image.network('https://picsum.photos/250?image=1'),
    Image.network('https://picsum.photos/250?image=2'),
    Image.network('https://picsum.photos/250?image=3'),
    Image.network('https://picsum.photos/250?image=4'),
  ],
)

gridDelegate is a property that controls how items are shown in a list. In our example, it is given a SliverGridDelegateWithFixedCrossAxisCount() with crossAxisCount set to 3. That means we want to display three items horizontally if the scroll direction is vertical and three items vertically if the scroll direction is horizontal. The default scroll direction for any list is vertical only, so the items are shown horizontally.

children refers to the list of items given here. It accepts a list of any widgets so you can show anything you would like to appear on the screen.

Here is the output:

GridView Demo

Here’s how the code is translated into the UI:

Code Translated to UI

GridView properties

Let’s have look at some properties of the GridView.

crossAxisSpacing

Setting a value for this property allows you to place a space between items on the cross axis. That means space will appear horizontally if the scroll direction is vertical.

GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 3, 
      crossAxisSpacing: 16),
  children: [
    Image.network('https://picsum.photos/250?image=1'),
    Image.network('https://picsum.photos/250?image=2'),
    Image.network('https://picsum.photos/250?image=3'),
)

CrossAxisSpacing

mainAxisSpacing

The main axis refers to the axis in which the list scrolls. The space between the items in the scrolling direction is given using the mainAxisSpacing property.

GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 3, 
      mainAxisSpacing: 16),
  children: [
    Image.network('https://picsum.photos/250?image=1'),
    Image.network('https://picsum.photos/250?image=2'),
    Image.network('https://picsum.photos/250?image=3'),
)

MainAxisSpacing

scrollDirection

You may want to change the scroll direction when the GridView is displayed in landscape mode. Setting scrollDirection to Axis.horizontal will do just that.

GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
  ),
  scrollDirection: Axis.horizontal,
  children: [
    ...
  ],
)

GridView Demo Horizontal

physics

This property allows you to set the scroll behavior for the list. You may not want the list to scroll at all. Let’s say you’re showing a picture collage, for example. You can disable the scroll by setting the physics value to NeverScrollableScrollPhysics(). By default, it uses the ClampingScrollPhysics() for Android and BouncingScrollPhysics() for iOS and looks like this:

Physics Comparison

shrinkWrap

Setting the shrinkWrap value to true causes GridView to take only the required space to fill items in the scroll direction. This defaults to false and reserves the entire screen even if the items are not in the list:

/////shrinkWrap: false,
Column(
  children: [
    Expanded(
      child: GridView(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
        ),
        shrinkWrap: false,
        children: [... ],
      ),
    ),
    ElevatedButton(onPressed: () {}, child: Text('Close'))
  ],
)
/////shrinkWrap: true,
Column(
  children: [
    GridView(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3,
      ),
      shrinkWrap: true,
      children: [...],
    ),
    ElevatedButton(onPressed: () {}, child: Text('Close'))
  ],
)

When shrinkWrap is false, we need to wrap the GridView inside the Expanded widget so that it takes all available space. Otherwise, it will throw an error.

Showing a list with a fixed number of items

Flutter has a constructor for showing only a few items in a GridView called GridView.count(). This constructor enables you to create a GridView with a fixed number of items. It also simplifies the method of specifying a number of items on the cross-axis.

The sample code looks like this:

GridView.count(
  crossAxisCount: 3,
  children: [
    ...
  ],
)

The number of items to show in the cross-axis is assigned to the crossAxisCount property. If you look carefully, you’ll notice that we don’t need SliverGridDelegateWith FixedCrossAxisCount() anymore.

GridView.count() can be used to create a keypad UI like this:

Keypad UI

Here is the code for the above design:

GridView.count(
  crossAxisCount: 3,
  shrinkWrap: true,
  padding: EdgeInsets.only(left: 24, right: 24),
  children: [
    DialKey(
      number: '1',
      letters: '',
    ),
...
  ],
)

The shrinkWrap property is set to true, which causes GridView to free up some space on the screen.

DialKey() is a custom widget to display a single key. It looks like this:

// DialKey widget
class DialKey extends StatelessWidget {
  final String number;
  final String letters;

  DialKey({this.number, this.letters});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        width: 80,
        height: 80,
        child: FloatingActionButton(
          onPressed: () {},
          backgroundColor: Colors.grey.withOpacity(0.5),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                '$number',
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 24,
                    fontWeight: FontWeight.bold),
              ),
              Text(
                '$letters',
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 16,
                    fontWeight: FontWeight.bold),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Showing a long list in GridView

To show a long list or an infinite number of items that may come from the database, you need GridView.builder() constructor.

Here is the sample code:

GridView.builder(
  itemCount: 100,
  itemBuilder: (context, index) => ItemTile(index),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
  ),
)

itemCount represents the number of items. This helps GridView to estimate the maximum scroll extent.

itemBuilder creates the given widget based on the current index.

Let’s try to build a product listing like this:

Product Listing

The code is as follows:

GridView.builder(
  itemCount: 100,
  itemBuilder: (context, index) => ItemTile(index),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    childAspectRatio: 2,
  ),
)
class ItemTile extends StatelessWidget {
  final int itemNo;

  const ItemTile(
    this.itemNo,
  );

  @override
  Widget build(BuildContext context) {
    final Color color = Colors.primaries[itemNo % Colors.primaries.length];
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ListTile(
        tileColor: color.withOpacity(0.3),
        onTap: () {},
        leading: Container(
          width: 50,
          height: 30,
          color: color.withOpacity(0.5),
          child: Placeholder(
            color: color,
          ),
        ),
        title: Text(
          'Product $itemNo',
          key: Key('text_$itemNo'),
        ),
      ),
    );
  }
}

One important thing to note in the above code is the childAspectRatio property. This can be used to adjust the height of the items, as shown below:

ChildAspectRatio Comparison

Building a responsive GridView

With the release of Flutter 2.0, you can now develop apps for the web and desktop in addition to mobile. When building cross-platform apps, you want to make sure you cater to web users by creating the best possible user experience. In this case, showing more items on the grid when it is displayed on a larger screen can go a long way toward improving the UX for web users.

Let’s modify the previous code to show more items on the cross-axis when displayed on a larger screen:

//Before
GridView.builder(
  itemCount: 100,
  itemBuilder: (context, index) => ItemTile(index),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    childAspectRatio: 2,
  ),
)
//After
LayoutBuilder(builder: (context, constraints) {
  return GridView.builder(
    itemCount: 100,
    itemBuilder: (context, index) => ItemTile(index),
    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: constraints.maxWidth > 700 ? 4 : 1,
      childAspectRatio: 5,
    ),
  );
})

Wrap the GridView inside the LayoutBuilder. The LayoutBuilder provides the constraints, which can be used to determine the width and height. Using the constraints, we can build various user interfaces.

For our example, whenever the screen resolution changes to 700 or greater in width, we will show four items on the cross-axis.

Here is the output:

GridView Demo Output

Conclusion

If you’ve made it this far, you should have all the requisite skills and foundational knowledge to create complex and engaging grid lists in Flutter using GridView.

The full code used for this example is available on GitHub.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Pinkesh Darji I love to solve problems using technology that improves users' lives on a major scale. Over the last seven-plus years, I've been developing and leading various mobile apps in different areas.

Leave a Reply