flutter-gridview-rejillas-mosaicos

Rejillas en Flutter con GridView

  • 4 min

Una rejilla es una disposición de elementos en filas y columnas.

Hasta ahora, nuestras Apps han sido muy verticales. Todo fluía de arriba a abajo en una lista infinita. Pero, ¿y si queremos hacer una galería de fotos como la de Instagram? ¿O un panel de control con botones grandes?

Para eso necesitamos organizar el contenido en dos dimensiones. Necesitamos una Rejilla (Grid).

En Flutter, el widget encargado de esto es el GridView. Funciona de forma muy similar al ListView, pero añade un nivel de complejidad: tenemos que decidir cuántas columnas queremos y qué forma tendrán las celdas.

GridView.count: un número fijo de columnas

Si sabes exactamente cuántas columnas quieres (por ejemplo, “quiero 2 columnas fijas”), el constructor más sencillo es GridView.count.

Es perfecto para menús estáticos o galerías simples.

GridView.count(
  crossAxisCount: 2, // Número de columnas (Eje cruzado)
  mainAxisSpacing: 10, // Espacio vertical entre filas
  crossAxisSpacing: 10, // Espacio horizontal entre columnas
  padding: EdgeInsets.all(20), // Margen externo de la rejilla
  
  children: [
    Container(color: Colors.red, child: Center(child: Text("1"))),
    Container(color: Colors.blue, child: Center(child: Text("2"))),
    Container(color: Colors.green, child: Center(child: Text("3"))),
    Container(color: Colors.orange, child: Center(child: Text("4"))),
  ],
)
Copied!

Relación de aspecto

Por defecto, GridView intenta que las celdas sean cuadradas (relación 1:1). Pero a veces queremos tarjetas rectangulares (más anchas que altas, o viceversa).

Para controlar esto usamos la propiedad childAspectRatio.

  • 1.0: Cuadrado perfecto.
  • < 1.0: Más alto que ancho (Vertical, ej: 0.7 para pósters de cine).
  • > 1.0: Más ancho que alto (Horizontal, ej: 1.5 para tarjetas de noticias).
GridView.count(
  crossAxisCount: 2,
  childAspectRatio: 1.5, // Celdas rectangulares apaisadas
  children: [...],
)
Copied!

GridView.extent: rejillas adaptables

El problema de GridView.count es que es rígido. Si pones crossAxisCount: 2, se verán 2 columnas en un móvil pequeño (quedando bien) y 2 columnas en una Tablet de 12 pulgadas (quedando unas celdas gigantescas y feas).

Para solucionar esto, usamos GridView.extent.

En lugar de decirle “cuántas columnas”, le decimos: “¿Cuál es el ancho MÁXIMO que puede tener cada celda?”. Flutter calculará cuántas caben automáticamente.

GridView.extent(
  maxCrossAxisExtent: 150, // Cada celda medirá COMO MÁXIMO 150px de ancho
  children: [...],
)
Copied!
  • En un móvil de 300px de ancho -> Caben 2 celdas.
  • En una Tablet de 900px de ancho -> Caben 6 celdas.

Esta es la forma más sencilla de hacer tu diseño Responsive (adaptable) sin complicarte con cálculos de pantalla manuales.

GridView.builder: datos dinámicos

Al igual que con las listas, si vamos a mostrar 1.000 fotos descargadas de internet, no podemos usar los constructores normales. Necesitamos el renderizado perezoso (Lazy Loading).

Usaremos GridView.builder. Pero aquí la sintaxis se complica un poco, porque necesitamos un “Delegado” (gridDelegate) que controle la geometría.

El gridDelegate es el objeto que contiene la lógica matemática de cómo distribuir las baldosas.

GridView.builder(
  itemCount: 100, // Tenemos 100 elementos
  
  // Definimos la estructura de la rejilla aquí
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3, // 3 Columnas fijas
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
  ),
  
  // Construimos el item bajo demanda (igual que ListView)
  itemBuilder: (context, index) {
    return Container(
      color: Colors.teal[100 * (index % 9)],
      child: Center(child: Text('Item $index')),
    );
  },
)
Copied!

Los dos delegados principales

El nombre de la clase es largo (SliverGridDelegateWith...), pero hay dos delegados que usaremos habitualmente:

  1. SliverGridDelegateWithFixedCrossAxisCount: Equivalente a .count. Fijas el número de columnas.
  2. SliverGridDelegateWithMaxCrossAxisExtent: Equivalente a .extent. Fijas el ancho máximo del item para que sea responsivo.

Un consejo de diseño: SliverGrid

Aunque GridView es potente, a veces querrás mezclar una lista normal con una rejilla en la misma pantalla (por ejemplo, una cabecera, luego una rejilla de fotos y luego un pie de página).

Anidar un GridView desplazable dentro de un ListView suele provocar conflictos de restricciones y de scroll. Se puede resolver en casos pequeños con shrinkWrap y una física adecuada, pero para composiciones grandes es preferible compartir un único sistema de scroll.

Para esos casos avanzados se usan CustomScrollView y los slivers. Es un tema avanzado, pero conviene recordar el nombre: si necesitas combinar una cabecera, una lista y una rejilla bajo el mismo scroll, busca SliverList y SliverGrid.