How to Create a GridView Layout in Flutter: Complete Guide with Code Examples

To create a GridView layout in Flutter, instantiate the GridView widget using constructors like GridView.count for fixed columns, GridView.extent for maximum item widths, or GridView.builder for lazy loading, each leveraging a SliverGridDelegate to arrange children in a two-dimensional scrollable grid.

The GridView widget is a fundamental layout component in the Flutter framework that extends BoxScrollView (defined in packages/flutter/lib/src/widgets/scroll_view.dart) to display children in a scrollable grid format. Internally, it utilizes a SliverGrid rendering object (implemented in packages/flutter/lib/src/rendering/sliver_grid.dart) to calculate positions based on a grid delegate. Whether you are building an image gallery, a dashboard, or a product catalog, understanding how to create a gridview layout in Flutter efficiently is essential for performance and responsive design.

Understanding the GridView Widget Architecture

GridView inherits its scrolling capabilities from BoxScrollView, which provides the foundation for scroll physics, padding, and controller handling. According to the Flutter source code in packages/flutter/lib/src/widgets/grid_view.dart, the widget operates by combining a ScrollView with a SliverGrid delegate system.

The layout mechanism relies on two primary grid delegate types:

  • SliverGridDelegateWithFixedCrossAxisCount – Enforces a specific number of columns (or rows, depending on scroll direction).
  • SliverGridDelegateWithMaxCrossAxisExtent – Calculates the number of columns based on available space and a maximum item width.

These delegates determine how the available cross-axis space translates into child positioning rules, making the grid responsive across different screen sizes.

Choosing the Right GridView Constructor

Flutter provides four factory constructors in packages/flutter/lib/src/widgets/grid_view.dart to accommodate different use cases.

GridView.count (Fixed Cross-Axis Count)

Use GridView.count when you need a fixed number of columns or rows regardless of screen width. This constructor internally creates a SliverGridDelegateWithFixedCrossAxisCount and is ideal for layouts requiring consistent column counts, such as photo galleries with three columns.

GridView.extent (Maximum Cross-Axis Extent)

Use GridView.extent when you want items to have a consistent maximum width (or height), allowing the framework to automatically calculate how many items fit per row. This constructor uses SliverGridDelegateWithMaxCrossAxisExtent and creates responsive grids that adapt to different screen sizes.

GridView.builder (Lazy Loading)

Use GridView.builder for large or infinite datasets. This constructor accepts an itemBuilder callback and only builds widgets for visible items, significantly reducing memory usage compared to eager constructors. It requires explicit configuration of the gridDelegate parameter.

GridView.custom (Full Control)

Use GridView.custom when you need complete control over the child model, allowing you to provide a custom SliverChildDelegate for complex animation or retention policies.

Practical Code Examples

The following examples demonstrate how to implement each constructor pattern using the Flutter framework source.

Fixed Three-Column Grid

This example uses GridView.count to create a grid with exactly three columns:

import 'package:flutter/material.dart';

class SimpleGridExample extends StatelessWidget {
  const SimpleGridExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GridView.count Example')),
      body: GridView.count(
        crossAxisCount: 3,                     // three columns
        padding: const EdgeInsets.all(8.0),
        crossAxisSpacing: 8.0,
        mainAxisSpacing: 8.0,
        children: List.generate(30, (index) {
          return Container(
            color: Colors.blue[(index % 9 + 1) * 100],
            child: Center(
              child: Text('Item $index',
                  style: const TextStyle(color: Colors.white)),
            ),
          );
        }),
      ),
    );
  }
}

Responsive Grid with Maximum Item Width

This example uses GridView.extent to ensure each tile never exceeds 150 logical pixels wide:

import 'package:flutter/material.dart';

class ResponsiveGridExample extends StatelessWidget {
  const ResponsiveGridExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GridView.extent Example')),
      body: GridView.extent(
        maxCrossAxisExtent: 150,               // each tile ≤ 150 px wide
        padding: const EdgeInsets.all(8.0),
        crossAxisSpacing: 8.0,
        mainAxisSpacing: 8.0,
        children: List.generate(50, (index) {
          return Card(
            elevation: 2,
            child: Center(child: Text('Item $index')),
          );
        }),
      ),
    );
  }
}

Lazy-Loading Grid for Large Datasets

This example demonstrates GridView.builder with SliverGridDelegateWithFixedCrossAxisCount for memory-efficient rendering of 1000 items:

import 'package:flutter/material.dart';

class LazyGridExample extends StatelessWidget {
  const LazyGridExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GridView.builder Example')),
      body: GridView.builder(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 8,
          mainAxisSpacing: 8,
        ),
        itemCount: 1000,
        itemBuilder: (context, index) {
          return Container(
            color: Colors.orange[(index % 9 + 1) * 100],
            child: Center(
              child: Text('Item $index',
                  style: const TextStyle(color: Colors.white)),
            ),
          );
        },
      ),
    );
  }
}

How GridView Calculates Layout

The layout algorithm implemented in packages/flutter/lib/src/rendering/sliver_grid.dart follows a specific process:

  1. Delegate Selection – The gridDelegate (either SliverGridDelegateWithFixedCrossAxisCount or SliverGridDelegateWithMaxCrossAxisExtent) receives the available cross-axis constraints.
  2. Slot Calculation – For fixed count delegates, the cross-axis extent is divided equally by the count. For max extent delegates, the available space is divided by the max extent, rounding up to determine the count.
  3. Child Positioning – The SliverGrid geometry calculates each child's offset based on the main-axis and cross-axis spacing parameters.
  4. Viewport Clipping – Only children within the visible viewport (plus cache extent) are laid out and rendered.

Because GridView is built on the Sliver architecture, it integrates seamlessly with CustomScrollView, allowing you to combine grids with lists, headers, and other sliver-based widgets in a unified scrollable viewport.

Performance Considerations

When you create a gridview layout in Flutter with many items, prefer lazy constructors over eager ones. The GridView.builder constructor only calls the itemBuilder function for indices currently visible in the viewport, plus a small cache buffer. This approach prevents memory exhaustion when displaying thousands of items.

For fixed, small datasets (under 100 items), GridView.count and GridView.extent are acceptable and provide simpler syntax, but they build all children immediately, consuming more memory upfront.

Summary

  • GridView extends BoxScrollView and uses SliverGrid for two-dimensional layouts in packages/flutter/lib/src/widgets/grid_view.dart.
  • Four constructors provide different trade-offs: count for fixed columns, extent for responsive item widths, builder for lazy loading, and custom for advanced delegation.
  • Grid delegates (SliverGridDelegateWithFixedCrossAxisCount and SliverGridDelegateWithMaxCrossAxisExtent) determine how the cross-axis space translates into column counts and item dimensions.
  • Lazy loading via GridView.builder prevents memory issues by only building visible widgets, essential for large datasets.
  • Sliver architecture allows GridView to nest inside CustomScrollView for complex scrollable compositions.

Frequently Asked Questions

What is the difference between GridView.count and GridView.builder?

GridView.count is an eager constructor that builds all children immediately, suitable for small, finite lists where the column count must remain constant. GridView.builder creates items on-demand as they scroll into view using an itemBuilder callback, making it the correct choice for large datasets (hundreds or thousands of items) to maintain 60fps scrolling performance.

How do I create a responsive GridView that adapts to screen size?

Use GridView.extent with the maxCrossAxisExtent parameter (e.g., maxCrossAxisExtent: 150). According to the implementation in packages/flutter/lib/src/rendering/sliver_grid.dart, the framework automatically calculates how many items fit per row by dividing the available width by the max extent, rounding down, ensuring optimal density across phones and tablets without hardcoding column counts.

Why does my GridView show too much empty space on large screens?

The empty space typically results from using GridView.count with a fixed crossAxisCount on wide screens. Switch to SliverGridDelegateWithMaxCrossAxisExtent via GridView.extent or manually configure the delegate to define a maxCrossAxisExtent that prevents items from stretching excessively while allowing additional columns to appear on wider displays.

Can I use GridView inside a Column or other scrollable widget?

Yes, but you must constrain the GridView's height when nesting inside an unbounded parent like Column. Wrap the GridView in an Expanded or SizedBox with a fixed height, or set shrinkWrap: true (though this defeats lazy loading). Alternatively, replace the outer Column with a CustomScrollView and use the grid as a sliver via SliverToBoxAdapter or by directly using the grid delegate within the custom scroll view, as implemented in packages/flutter/lib/src/widgets/scroll_view.dart.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →