FlutterFlow's GridView widget displays items in a multi-column grid with configurable crossAxisCount, spacing, and child aspect ratio. Use Responsive Values on crossAxisCount to show 2 columns on mobile, 3 on tablet, and 4 on desktop. Bind the GridView to a Backend Query with Generate Dynamic Children for dynamic data. For variable-height Pinterest-style layouts, create a Custom Widget with the flutter_staggered_grid_view package.
Building a Responsive Grid View in FlutterFlow
Grids are ideal for product catalogs, image galleries, card collections, and dashboards. FlutterFlow's GridView widget handles the multi-column layout automatically, and Responsive Values ensure it adapts to phones, tablets, and desktops. This tutorial covers the standard grid and the advanced staggered layout.
Prerequisites
- A FlutterFlow project open in the builder
- A Firestore collection with items to display (optional for static grids)
- FlutterFlow Pro plan for the staggered grid Custom Widget
Step-by-step guide
Add a GridView widget and set the column count and spacing
Add a GridView widget and set the column count and spacing
Drag a GridView from the Widget Palette onto your page. In the Properties Panel, set crossAxisCount to 2 (two items per row on mobile). Set mainAxisSpacing to 12 (vertical gap between rows) and crossAxisSpacing to 12 (horizontal gap between columns). Set childAspectRatio to 0.75 for portrait-oriented cards or 1.0 for square cards. The aspect ratio controls the height of each cell relative to its width.
Expected result: A two-column grid with 12px spacing between items appears on the page.
Make the grid responsive with different column counts per screen size
Make the grid responsive with different column counts per screen size
Select the crossAxisCount property in the Properties Panel and click the responsive icon (screen sizes icon). Set mobile to 2, tablet to 3, and desktop to 4. This uses FlutterFlow's Responsive Values feature so the grid automatically shows more columns on larger screens. Also adjust childAspectRatio responsively if your items have different ideal proportions on different screens.
Expected result: The grid shows 2 columns on phone, 3 on tablet, and 4 on desktop when testing with different preview sizes.
Design a grid item Component with image and text
Design a grid item Component with image and text
Create a new Component called GridItemCard. Build its layout: a Container with borderRadius 12, clipBehavior (clip children to rounded corners). Inside, a Column: top child is an Image widget (fit: cover, takes 60% of height), bottom child is a Padding with Column containing title Text (Body Medium Bold) and price Text (Body Small, Primary color). Add Component Parameters: imageUrl (String), title (String), price (String), onTap (Action). Bind each widget to its parameter.
Expected result: A compact card Component with image on top and text below, accepting data via parameters.
Bind the GridView to a Backend Query with Generate Dynamic Children
Bind the GridView to a Backend Query with Generate Dynamic Children
Select the GridView and add a Backend Query: choose your collection (e.g., products) and set the order (e.g., by createdAt descending). Enable Generate Dynamic Children. Drag your GridItemCard Component as the child template. Map imageUrl to the document's imageUrl field, title to name, price to a formatted price string. Add an On Tap callback that navigates to the product detail page with the document ID as a Route Parameter.
Expected result: The grid populates with data from Firestore, showing one GridItemCard per document.
Build a staggered grid Custom Widget for variable-height items
Build a staggered grid Custom Widget for variable-height items
For a Pinterest-style layout where items have different heights, create a Custom Widget. Add the flutter_staggered_grid_view package to Pubspec Dependencies. Create a StatelessWidget that uses MasonryGridView.count with crossAxisCount from a parameter. Each child is a Container with variable height based on content. Pass the items as a JSON List Component Parameter and parse them in the widget to build the children.
1import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';23class StaggeredGridWidget extends StatelessWidget {4 const StaggeredGridWidget({5 super.key,6 this.width,7 this.height,8 required this.itemCount,9 });1011 final double? width;12 final double? height;13 final int itemCount;1415 @override16 Widget build(BuildContext context) {17 return SizedBox(18 width: width ?? double.infinity,19 height: height ?? 600,20 child: MasonryGridView.count(21 crossAxisCount: 2,22 mainAxisSpacing: 12,23 crossAxisSpacing: 12,24 itemCount: itemCount,25 itemBuilder: (context, index) {26 return Container(27 height: (index % 3 == 0) ? 200 : 150,28 decoration: BoxDecoration(29 color: Colors.grey[200],30 borderRadius: BorderRadius.circular(12),31 ),32 );33 },34 ),35 );36 }37}Expected result: A Pinterest-style masonry grid where items have varying heights and flow naturally.
Complete working example
1import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';2import 'dart:convert';34class StaggeredGridWidget extends StatelessWidget {5 const StaggeredGridWidget({6 super.key,7 this.width,8 this.height,9 required this.itemsJson,10 this.crossAxisCount,11 });1213 final double? width;14 final double? height;15 final String itemsJson;16 final int? crossAxisCount;1718 @override19 Widget build(BuildContext context) {20 final items = (jsonDecode(itemsJson) as List).cast<Map<String, dynamic>>();2122 return SizedBox(23 width: width ?? double.infinity,24 height: height ?? 600,25 child: MasonryGridView.count(26 crossAxisCount: crossAxisCount ?? 2,27 mainAxisSpacing: 12,28 crossAxisSpacing: 12,29 itemCount: items.length,30 itemBuilder: (context, index) {31 final item = items[index];32 return Container(33 decoration: BoxDecoration(34 borderRadius: BorderRadius.circular(12),35 color: Colors.grey[100],36 ),37 child: Column(38 crossAxisAlignment: CrossAxisAlignment.start,39 children: [40 ClipRRect(41 borderRadius: const BorderRadius.vertical(42 top: Radius.circular(12),43 ),44 child: Image.network(45 item['imageUrl'] ?? '',46 fit: BoxFit.cover,47 width: double.infinity,48 ),49 ),50 Padding(51 padding: const EdgeInsets.all(8),52 child: Text(53 item['title'] ?? '',54 style: Theme.of(context).textTheme.bodyMedium,55 ),56 ),57 ],58 ),59 );60 },61 ),62 );63 }64}Common mistakes when creating a Custom Grid View for Your FlutterFlow App
Why it's a problem: Setting childAspectRatio incorrectly for content with varying text lengths
How to avoid: Either constrain your text to a fixed number of lines (maxLines + overflow ellipsis) or use a staggered grid Custom Widget for variable-height items.
Why it's a problem: Using a fixed crossAxisCount that does not adapt to screen size
How to avoid: Use Responsive Values on crossAxisCount: 2 for mobile, 3 for tablet, 4 for desktop.
Why it's a problem: Placing a GridView inside a SingleChildScrollView or Column without Expanded
How to avoid: Wrap GridView in Expanded when inside a Column, or use shrinkWrap: true only for short grids. Never nest scrollable widgets without proper constraints.
Best practices
- Use Responsive Values on crossAxisCount to adapt columns to mobile, tablet, and desktop
- Set childAspectRatio to match your content — 1.0 for square, 0.75 for portrait, 1.5 for landscape
- Create a reusable grid item Component for consistent styling across the app
- Use the staggered grid Custom Widget when items have variable heights (image galleries, blogs)
- Add mainAxisSpacing and crossAxisSpacing of 8-16px for comfortable visual separation
- Limit text in grid items to 1-2 lines with ellipsis overflow to maintain consistent cell height
- Wrap GridView in Expanded when placing it inside a Column to avoid unbounded height errors
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a responsive grid view in FlutterFlow that shows 2 columns on mobile, 3 on tablet, and 4 on desktop. Each grid item is a card with an image, title, and price from Firestore. Also explain how to make a Pinterest-style staggered grid using a Custom Widget.
Add a GridView to my page with crossAxisCount 2, mainAxisSpacing 12, crossAxisSpacing 12, and childAspectRatio 0.75. I will add a card Component as the child template.
Frequently asked questions
What is the difference between GridView and Wrap in FlutterFlow?
GridView enforces fixed column counts with uniform cell sizes and supports scrolling and lazy rendering. Wrap flows items left to right and wraps to the next line, but items can vary in width. Use GridView for uniform grids, Wrap for tag-like layouts.
Can I make a single item span two columns in a GridView?
Not with FlutterFlow's built-in GridView. For spanning items, you need a Custom Widget using SliverGrid with custom layouts or a staggered grid delegate.
How do I add infinite scroll to a GridView?
Enable Infinite Scroll in the Backend Query settings on the GridView, the same way as with ListView. Set a page size and FlutterFlow loads more items as the user scrolls.
Why are my grid items different heights?
GridView enforces uniform height based on childAspectRatio. If items look different heights, check that you are not using shrinkWrap or placing the GridView in an unconstrained parent.
Can I drag and reorder items in a GridView?
Not with the built-in GridView. Drag-and-reorder requires a Custom Widget using Flutter's ReorderableGridView or similar package.
Can RapidDev help with advanced grid layouts?
Yes. RapidDev can build staggered grids, spanning items, drag-to-reorder grids, and responsive masonry layouts using Custom Widgets.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation