Creare griglie dinamiche e responsive in Flutter con GridView e SliverGrid

Foto di Filipe Nobre su Unsplash

GuideIntermedio35 min Flutter 3.x

Creare griglie dinamiche e responsive in Flutter con GridView e SliverGrid

Le griglie sono uno dei pattern di layout più comuni nelle app moderne: gallerie di foto, cataloghi di prodotti, dashboard di card. In questo tutorial vedremo come usare GridView in tutte le sue varianti, come rendere il numero di colonne adattivo con SliverGridDelegateWithMaxCrossAxisExtent, e come integrare una griglia dentro un CustomScrollView tramite SliverGrid. Alla fine avrai una galleria responsive che si adatta a smartphone, tablet e desktop.

  1. 1

    Prepara il progetto e i dati di esempio

    Partiamo da un semplice progetto Flutter. Creiamo un modello Product e una lista di dati fittizi che useremo per popolare le nostre griglie. Non servono pacchetti esterni: GridView e SliverGrid fanno parte del framework.

    class Product {
      final String name;
      final Color color;
      final double price;
    
      const Product({
        required this.name,
        required this.color,
        required this.price,
      });
    }
    
    final List<Product> demoProducts = List.generate(
      30,
      (i) => Product(
        name: 'Prodotto ${i + 1}',
        color: Colors.primaries[i % Colors.primaries.length].shade300,
        price: (i + 1) * 4.99,
      ),
    );

    Risultato atteso

    Hai una lista `demoProducts` con 30 elementi pronti da mostrare in griglia.

  2. 2

    Crea una griglia con GridView.builder e conteggio fisso di colonne

    Il modo più diretto per creare una griglia performante è GridView.builder, che costruisce le celle solo quando servono (lazy). Usiamo SliverGridDelegateWithFixedCrossAxisCount per definire un numero fisso di colonne, la spaziatura e il rapporto d'aspetto delle celle.

    class FixedGridScreen extends StatelessWidget {
      const FixedGridScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Griglia fissa')),
          body: GridView.builder(
            padding: const EdgeInsets.all(12),
            itemCount: demoProducts.length,
            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              mainAxisSpacing: 12,
              crossAxisSpacing: 12,
              childAspectRatio: 0.8,
            ),
            itemBuilder: (context, index) {
              final product = demoProducts[index];
              return ProductCard(product: product);
            },
          ),
        );
      }
    }

    Risultato atteso

    Vedi una griglia a 2 colonne che scorre verticalmente con celle spaziate.

  3. 3

    Costruisci la card riutilizzabile

    Estraiamo la singola cella in un widget ProductCard riutilizzabile. Usare un widget dedicato mantiene il codice pulito e rende il itemBuilder leggibile.

    class ProductCard extends StatelessWidget {
      final Product product;
      const ProductCard({super.key, required this.product});
    
      @override
      Widget build(BuildContext context) {
        return Card(
          clipBehavior: Clip.antiAlias,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Expanded(
                child: Container(
                  color: product.color,
                  child: const Center(
                    child: Icon(Icons.image, color: Colors.white, size: 40),
                  ),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      product.name,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 4),
                    Text('€ ${product.price.toStringAsFixed(2)}'),
                  ],
                ),
              ),
            ],
          ),
        );
      }
    }

    Risultato atteso

    Ogni cella mostra un'area colorata, il nome del prodotto e il prezzo.

  4. 4

    Rendi il numero di colonne adattivo allo spazio disponibile

    Un numero fisso di colonne non è ideale su tablet o desktop. Con SliverGridDelegateWithMaxCrossAxisExtent indichi la larghezza massima che ogni cella può avere: Flutter calcola automaticamente quante colonne stanno nello spazio disponibile. Così la griglia diventa naturalmente responsive senza scrivere logica su MediaQuery.

    GridView.builder(
      padding: const EdgeInsets.all(12),
      itemCount: demoProducts.length,
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 200, // ogni cella al massimo 200px di larghezza
        mainAxisSpacing: 12,
        crossAxisSpacing: 12,
        childAspectRatio: 0.8,
      ),
      itemBuilder: (context, index) =>
          ProductCard(product: demoProducts[index]),
    )

    Risultato atteso

    Su schermi stretti vedi 2 colonne, mentre allargando la finestra ne compaiono automaticamente 3, 4 o più.

  5. 5

    Integra la griglia in un CustomScrollView con SliverGrid

    Quando devi combinare la griglia con altri contenuti scrollabili (una SliverAppBar, un'intestazione, una lista) devi usare SliverGrid dentro un CustomScrollView. La logica del delegate è identica, ma i figli si passano tramite SliverChildBuilderDelegate.

    class SliverGridScreen extends StatelessWidget {
      const SliverGridScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: CustomScrollView(
            slivers: [
              const SliverAppBar(
                floating: true,
                title: Text('Catalogo'),
              ),
              SliverPadding(
                padding: const EdgeInsets.all(12),
                sliver: SliverGrid(
                  gridDelegate:
                      const SliverGridDelegateWithMaxCrossAxisExtent(
                    maxCrossAxisExtent: 200,
                    mainAxisSpacing: 12,
                    crossAxisSpacing: 12,
                    childAspectRatio: 0.8,
                  ),
                  delegate: SliverChildBuilderDelegate(
                    (context, index) =>
                        ProductCard(product: demoProducts[index]),
                    childCount: demoProducts.length,
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }

    Risultato atteso

    La griglia scorre insieme alla SliverAppBar in un unico scroll fluido.

  6. 6

    Aggiungi celle di dimensioni variabili con SliverGrid staggered (opzionale)

    Per griglie tipo Pinterest, dove le celle hanno altezze diverse, il framework di base non basta. Un approccio comune è il pacchetto flutter_staggered_grid_view. Aggiungilo con flutter pub add flutter_staggered_grid_view e usa MasonryGridView per un layout a mattoncini.

    import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
    
    MasonryGridView.count(
      padding: const EdgeInsets.all(12),
      crossAxisCount: 2,
      mainAxisSpacing: 12,
      crossAxisSpacing: 12,
      itemCount: demoProducts.length,
      itemBuilder: (context, index) {
        // altezza variabile per un effetto "mattoncini"
        final height = 120.0 + (index % 4) * 40;
        return SizedBox(
          height: height,
          child: ProductCard(product: demoProducts[index]),
        );
      },
    )

    Risultato atteso

    Ottieni una griglia in stile Pinterest con celle di altezza variabile ben incastrate.

  7. 7

    Applica le best practice per performance e leggibilità

    Alcuni accorgimenti finali per griglie robuste:

    • Usa sempre GridView.builder/SliverGrid con builder per liste lunghe: evita di costruire tutte le celle in memoria.
    • Preferisci maxCrossAxisExtent al conteggio fisso per la responsività cross-device.
    • Imposta key univoche sulle celle se gli elementi possono essere riordinati o rimossi.
    • Aggiungi cacheExtent se vuoi precaricare più celle fuori schermo per uno scroll più fluido.
    • Estrai le celle in widget dedicati per abilitare il riutilizzo e ottimizzare i rebuild.
    GridView.builder(
      cacheExtent: 600, // precarica celle fuori dallo schermo
      itemCount: demoProducts.length,
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 200,
        mainAxisSpacing: 12,
        crossAxisSpacing: 12,
        childAspectRatio: 0.8,
      ),
      itemBuilder: (context, index) {
        final product = demoProducts[index];
        return ProductCard(
          key: ValueKey(product.name),
          product: product,
        );
      },
    )

    Risultato atteso

    Hai una griglia responsive, performante e manutenibile pronta per la produzione.

CondividiXLinkedInFacebookWhatsApp

Commenti (0)

Ancora nessun commento. Inizia tu!