Grafica shimmer per gli stati di caricamento in Flutter con shimmer
GuidePrincipiante25 min Flutter 3.x

Grafica shimmer per gli stati di caricamento in Flutter con shimmer

Mostrare uno spinner circolare durante il caricamento è funzionale ma poco moderno. Gli skeleton loader con effetto shimmer (quella luce che scorre sui placeholder grigi) danno all'utente l'idea che i contenuti stiano già arrivando, migliorando la percezione delle prestazioni.

In questo tutorial useremo il pacchetto shimmer per costruire una schermata con una lista di card che mostra placeholder animati mentre simuliamo il caricamento dei dati da una API. Vedremo come strutturare il codice in modo pulito per passare dallo stato di loading allo stato di loaded.

  1. 1

    Aggiungere la dipendenza shimmer

    Per prima cosa aggiungiamo il pacchetto shimmer al progetto. Puoi farlo da terminale con il comando flutter pub add shimmer, oppure inserendolo manualmente nella sezione dependencies del file pubspec.yaml.

    Dopo aver salvato, esegui flutter pub get per scaricare il pacchetto (il comando pub add lo fa già in automatico).

    dependencies:
      flutter:
        sdk: flutter
      shimmer: ^3.0.0

    Risultato atteso

    Il pacchetto shimmer è disponibile nel progetto e importabile con `import 'package:shimmer/shimmer.dart';`.

  2. 2

    Creare un modello dati e simulare il caricamento

    Definiamo un semplice modello Articolo e una funzione che simula una chiamata di rete con un ritardo tramite Future.delayed. Questo ci permetterà di vedere l'effetto shimmer per qualche secondo prima di mostrare i dati reali.

    class Articolo {
      final String titolo;
      final String descrizione;
    
      Articolo({required this.titolo, required this.descrizione});
    }
    
    Future<List<Articolo>> caricaArticoli() async {
      // Simula una chiamata di rete
      await Future.delayed(const Duration(seconds: 3));
      return List.generate(
        8,
        (i) => Articolo(
          titolo: 'Articolo numero ${i + 1}',
          descrizione: 'Questa è la descrizione dettagliata dell\'articolo ${i + 1}.',
        ),
      );
    }

    Risultato atteso

    Hai una sorgente dati asincrona che restituisce una lista di articoli dopo 3 secondi.

  3. 3

    Costruire il widget placeholder (skeleton)

    Il segreto di un buon skeleton è riprodurre la forma del contenuto finale con blocchi grigi. Creiamo un widget ArticoloSkeleton che imita il layout di una card articolo: un blocco per il titolo e due blocchi più stretti per la descrizione.

    È importante che i placeholder abbiano un colore pieno (di solito bianco o grigio chiaro), perché sarà l'effetto shimmer a colorarli.

    import 'package:flutter/material.dart';
    
    class ArticoloSkeleton extends StatelessWidget {
      const ArticoloSkeleton({super.key});
    
      Widget _box({double? width, double height = 14}) {
        return Container(
          width: width,
          height: height,
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(4),
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Card(
          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _box(width: 180, height: 18),
                const SizedBox(height: 12),
                _box(width: double.infinity),
                const SizedBox(height: 8),
                _box(width: 220),
              ],
            ),
          ),
        );
      }
    }

    Risultato atteso

    Un widget riutilizzabile che riproduce la struttura di una card articolo con blocchi bianchi.

  4. 4

    Applicare l'effetto shimmer con Shimmer.fromColors

    Ora avvolgiamo la lista di skeleton in un widget Shimmer.fromColors. Questo widget applica l'animazione di scorrimento della luce a tutti i figli.

    I due parametri chiave sono baseColor (il colore di base dei blocchi) e highlightColor (il colore della luce che scorre). Usiamo un ListView con un numero fisso di skeleton per riempire lo schermo.

    import 'package:flutter/material.dart';
    import 'package:shimmer/shimmer.dart';
    
    class ListaSkeleton extends StatelessWidget {
      const ListaSkeleton({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Shimmer.fromColors(
          baseColor: Colors.grey.shade300,
          highlightColor: Colors.grey.shade100,
          child: ListView.builder(
            physics: const NeverScrollableScrollPhysics(),
            itemCount: 6,
            itemBuilder: (context, index) => const ArticoloSkeleton(),
          ),
        );
      }
    }

    Risultato atteso

    Una lista di card grigie con una luce che scorre orizzontalmente in modo continuo.

  5. 5

    Mettere tutto insieme con FutureBuilder

    Infine colleghiamo il caricamento dati alla UI. Usiamo un FutureBuilder per mostrare lo skeleton (ListaSkeleton) mentre lo stato è waiting, e la lista reale quando i dati sono disponibili.

    Nota come lo skeleton abbia esattamente lo stesso layout delle card finali: questo evita salti visivi (layout shift) quando i dati arrivano.

    class SchermataArticoli extends StatefulWidget {
      const SchermataArticoli({super.key});
    
      @override
      State<SchermataArticoli> createState() => _SchermataArticoliState();
    }
    
    class _SchermataArticoliState extends State<SchermataArticoli> {
      late Future<List<Articolo>> _future;
    
      @override
      void initState() {
        super.initState();
        _future = caricaArticoli();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Articoli')),
          body: FutureBuilder<List<Articolo>>(
            future: _future,
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const ListaSkeleton();
              }
              if (snapshot.hasError) {
                return Center(child: Text('Errore: ${snapshot.error}'));
              }
              final articoli = snapshot.data ?? [];
              return ListView.builder(
                itemCount: articoli.length,
                itemBuilder: (context, index) {
                  final a = articoli[index];
                  return Card(
                    margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                    child: ListTile(
                      title: Text(a.titolo),
                      subtitle: Text(a.descrizione),
                    ),
                  );
                },
              );
            },
          ),
        );
      }
    }

    Risultato atteso

    All'apertura della schermata vedi lo skeleton animato per 3 secondi, poi appaiono le card con i dati reali senza sfarfallii.

  6. 6

    Personalizzare direzione e velocità dell'effetto

    Il widget Shimmer permette di personalizzare la direzione dello scorrimento e la durata dell'animazione. Usa direction per cambiare l'asse (ad esempio ShimmerDirection.rtl per destra-sinistra) e period per rallentare o velocizzare l'effetto.

    Regola questi valori in base allo stile della tua app: un effetto più lento risulta più elegante, uno più rapido comunica maggiore reattività.

    Shimmer.fromColors(
      baseColor: Colors.grey.shade300,
      highlightColor: Colors.grey.shade100,
      direction: ShimmerDirection.ltr,
      period: const Duration(milliseconds: 1500),
      child: const ListaSkeleton(),
    )

    Risultato atteso

    L'animazione shimmer si adatta alla direzione e alla velocità desiderate, integrandosi con il design della tua app.

CondividiXLinkedInFacebookWhatsApp

Commenti (0)

Ancora nessun commento. Inizia tu!