FutureBuilder — cos’è, lavorare con Future in Flutter

Autore: IT Sectr Pubblicato: 2026-07-02 Tempo di lettura: 8 min

FutureBuilder è un widget in Flutter che ricostruisce automaticamente la propria interfaccia basandosi sullo stato corrente di AsyncSnapshot ottenuto da un Future fornito. A differenza della chiamata manuale a setState dopo await, FutureBuilder offre un approccio dichiarativo: si sottoscrive al Future al primo render e chiama la funzione builder ad ogni cambiamento di stato — caricamento, errore o dati pronti. Secondo il Riferimento API Flutter (2026), FutureBuilder è particolarmente utile per caricare dati dalla rete, leggere da un database e qualsiasi operazione asincrona dove l’interfaccia deve mostrare un indicatore di caricamento, un messaggio di errore o contenuto pronto.

Punti Chiave

  • FutureBuilder — widget Flutter per costruire l’interfaccia basata sullo stato del Future tramite AsyncSnapshot (none, waiting, active, done)
  • AsyncSnapshot — un oggetto contenente lo stato corrente di un’operazione asincrona: connectionState, data e error
  • builder — una funzione di callback invocata ad ogni cambiamento di stato del Future per ricostruire l’interfaccia
  • Gestione errori — AsyncSnapshot.hasError permette di mostrare un’interfaccia di fallback al fallimento dell’operazione asincrona
  • ConnectionState — un enum con quattro valori: none (nessuna operazione), waiting (in attesa), active (stream), done (completato)

Cos’è FutureBuilder in Flutter

FutureBuilder è un widget integrato di Flutter del pacchetto widgets che prende un Future e una funzione builder. Quando lo stato del Future cambia (in esecuzione, completato con dati, completato con errore), FutureBuilder ricostruisce automaticamente l’interfaccia chiamando il builder con un nuovo AsyncSnapshot. Questo elimina la necessità di gestire manualmente lo stato di caricamento tramite setState e flag.

A differenza di StreamBuilder, che lavora con flussi di dati (Stream), FutureBuilder è progettato per operazioni asincrone singole: richiesta HTTP, lettura file, query database. FutureBuilder gestisce autonomamente la sottoscrizione al Future: al primo build, avvia il Future e ne traccia il completamento. Quando il widget viene distrutto, FutureBuilder non cancella il Future — questa è responsabilità dello sviluppatore.

Secondo il Flutter Cookbook (2026), FutureBuilder è raccomandato per i casi in cui un’operazione asincrona viene eseguita una volta all’inizializzazione dello schermo. Per operazioni ricorrenti o flussi di dati, usate StreamBuilder. Entrambi i widget seguono lo stesso pattern di UI Reattiva, ma FutureBuilder è ottimizzato per richieste singole.

Come funziona FutureBuilder internamente

L’implementazione interna di FutureBuilder si sottoscrive al Future usando Future.then e catchError. All’avvio, FutureBuilder imposta connectionState su ConnectionState.waiting e chiama il builder con dati vuoti. Al completamento con successo, connectionState passa a ConnectionState.done con i dati. In caso di errore, snapshot.error viene riempito con l’oggetto errore. Ogni cambiamento attiva una ricostruzione del widget.

AsyncSnapshot: stati e proprietà

AsyncSnapshot è un oggetto contenitore che FutureBuilder passa alla funzione builder ad ogni cambiamento di stato. Contiene tutte le informazioni sullo stato corrente dell’operazione asincrona: se il caricamento è in corso, quali dati sono stati ricevuti o se si è verificato un errore. Comprendere AsyncSnapshot è la chiave per costruire correttamente l’interfaccia con FutureBuilder.

ProprietàTipoDescrizione
connectionStateConnectionStateStato corrente della connessione (none, waiting, active, done)
dataT?Dati ricevuti dal Future (null fino al completamento o in caso di errore)
errorObject?Oggetto errore se il Future è terminato con un’eccezione
hasDatabooltrue se data non è null e connectionState è ConnectionState.done
hasErrorbooltrue se il Future è terminato con un errore

ConnectionState: quattro stati di un’operazione asincrona

L’enum ConnectionState definisce la fase di un’operazione asincrona. None — stato iniziale quando il Future non è ancora stato avviato (usato raramente, tipicamente al primo build senza initialData). Waiting — il Future è in esecuzione, dati non ancora ricevuti. Active — usato solo da StreamBuilder per stream con dati parziali. Done — il Future è completato, i dati sono disponibili tramite snapshot.data o l’errore tramite snapshot.error.

La corretta gestione di tutti gli stati di AsyncSnapshot nella funzione builder è un requisito obbligatorio per il codice di produzione. Se non gestite lo stato waiting, l’utente vedrà una schermata vuota durante il caricamento. Se non gestite hasError, l’utente riceverà un’Eccezione senza spiegazione. Il pattern raccomandato: verificare hasError → verificare hasData → mostrare caricamento per default.

Pattern di utilizzo di FutureBuilder

FutureBuilder può essere usato in diversi pattern standard, ciascuno che risolve un compito specifico. Esaminiamo gli scenari principali: caricamento dati all’inizializzazione, caricamento con cache, richieste parallele e gestione errori con ripetizione.

Caricamento dati all’inizializzazione dello schermo

Il pattern più comune — FutureBuilder nel metodo build di un StatefulWidget o StatelessWidget. Il Future viene passato da initState o creato direttamente in build. È importante non creare il Future nel metodo build ad ogni ricostruzione — questo porterà a richieste ripetute. Usate un Future memorizzato in un campo dello State.

Caricamento con cache e aggiornamento

Per evitare richieste ripetute, FutureBuilder può essere combinato con CachedNetworkImage o una cache locale. Dopo il primo caricamento, i dati vengono salvati in memoria o SharedPreferences, e FutureBuilder mostra i dati in cache istantaneamente mentre li aggiorna dalla rete in parallelo. Questo migliora l’esperienza utente grazie a una risposta immediata.

Secondo pub.dev (2026), la cache è particolarmente rilevante per immagini e liste di dati. FutureBuilder con CachedNetworkImageProvider mostra automaticamente un’immagine in cache e, in sua assenza — un indicatore di caricamento seguito dal file scaricato.

FutureBuilder vs setState: cosa scegliere

FutureBuilder e la gestione manuale dello stato tramite setState sono due approcci all’interfaccia asincrona in Flutter. Ciascuno ha i propri vantaggi e limiti. La scelta dipende dalla complessità dello schermo e dal numero di operazioni asincrone.

FutureBuilder vince in semplicità: non è necessario dichiarare campi per lo stato di caricamento, dati ed errore — tutto è gestito tramite AsyncSnapshot. È ideale per schermi semplici con una operazione asincrona (una richiesta HTTP, lettura database). Tuttavia, con 5+ operazioni asincrone su uno schermo, FutureBuilder crea un annidamento eccessivo — risultando in una “Piramide” di FutureBuilder annidati.

setState con flag di stato manuali dà più controllo e leggibilità per logiche complesse. Per schermi con molte richieste dipendenti (caricare utente → caricare suoi ordini → caricare dettagli ordine), è meglio usare setState con ChangeNotifier o Bloc. Secondo la Guida alla Gestione dello Stato di Flutter (2026), per scenari complessi si raccomanda Riverpod o Bloc invece di FutureBuilder, poiché forniscono una migliore separazione tra logica e presentazione.

Esempio FutureBuilder con caricamento dati di rete

Consideriamo un esempio pratico di FutureBuilder per caricare una lista di utenti da un’API REST. Il codice dimostra la corretta gestione di tutti e tre gli stati di AsyncSnapshot: caricamento, errore e dati pronti.

dart
class UserListPage extends StatefulWidget {
  const UserListPage({super.key});

  @override
  State<UserListPage> createState() => _UserListPageState();
}

class _UserListPageState extends State<UserListPage> {
  final Future<List<User>> usersFuture = UserRepository().fetchUsers();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Utenti')),
      body: FutureBuilder<List<User>>(
        future: usersFuture,
        builder: (context, AsyncSnapshot<List<User>> snapshot) {
          if (snapshot.hasError) {
            return Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Icon(Icons.error_outline, size: 48, color: Colors.red),
                  const SizedBox(height: 16),
                  Text('Errore: ${snapshot.error}'),
                ],
              ),
            );
          }

          if (snapshot.hasData) {
            final users = snapshot.data!;
            return ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) {
                return ListTile(
                  leading: CircleAvatar(backgroundImage: NetworkImage(users[index].avatarUrl)),
                  title: Text(users[index].name),
                  subtitle: Text(users[index].email),
                );
              },
            );
          }

          return const Center(child: CircularProgressIndicator());
        },
      ),
    );
  }
}

Nell’esempio, FutureBuilder gestisce tutti e tre gli stati. In caso di errore, viene mostrata un’icona con un messaggio di errore. In caso di caricamento riuscito — una ListView con avatar e nomi. Durante il caricamento — un CircularProgressIndicator. Il Future è dichiarato come campo di classe, impedendo chiamate ripetute durante la ricostruzione. Questo pattern copre il 90% degli scenari di utilizzo di FutureBuilder nelle app mobili.

Domande Frequenti

Perché FutureBuilder chiama il builder più volte?

FutureBuilder chiama il builder ad ogni cambiamento di stato del Future: la prima volta alla creazione (connectionState: none o waiting), la seconda volta al completamento (connectionState: done). Se il widget genitore viene ricostruito, anche FutureBuilder viene ricostruito. Per evitare chiamate ripetute, assicuratevi che il Future sia creato al di fuori del metodo build — altrimenti ogni chiamata a build creerà un nuovo Future.

Come evitare una richiesta ripetuta durante la ricostruzione?

Memorizzate il Future in un campo di StatefulWidget (in initState) o usate la memoizzazione. Se il Future viene creato all’interno del metodo build, ogni chiamata a build creerà un nuovo Future, e FutureBuilder riavvierà l’operazione asincrona. Per StatelessWidget, usate il pacchetto cached_future o widget keep-alive in modo che il Future venga eseguito una volta indipendentemente dalle ricostruzioni.

In che cosa FutureBuilder differisce da StreamBuilder?

FutureBuilder è progettato per operazioni asincrone singole (una richiesta HTTP, una lettura database). StreamBuilder lavora con flussi di dati che possono emettere più valori nel tempo (chat, aggiornamenti prezzi, geolocalizzazione). StreamBuilder supporta ConnectionState.active per dati parziali, mentre FutureBuilder supporta solo waiting e done.

Come usare FutureBuilder con più Future?

Per più Future paralleli, usate Future.wait e passate il risultato a un singolo FutureBuilder. Future.wait prende una lista di Future e restituisce un Future — quando tutti i Future sono completati, il builder riceve un array di risultati. Per richieste sequenziali, usate una catena Future.then all’interno di un Future o FutureBuilder annidati (meno leggibili). Un’alternativa è il pacchetto riverpod con AsyncValue per stati asincroni multipli.

Come cancellare un Future uscendo dallo schermo?

FutureBuilder non cancella automaticamente il Future. Per cancellare, usate CancelableOperation dal pacchetto async o un meccanismo personalizzato tramite un flag cancelled nello State. Impostate il flag in dispose() e verificatelo dopo il completamento del Future prima di chiamare setState. In alternativa, usate il pacchetto riverpod con AutoDispose, che cancella automaticamente le operazioni asincrone uscendo dallo schermo.

Riepilogo

  • FutureBuilder — widget Flutter per costruzione dichiarativa dell’interfaccia basata sullo stato del Future tramite AsyncSnapshot (waiting, done, error)
  • AsyncSnapshot — contenitore con connectionState, data e error; essenziale per la corretta gestione di tutti gli stati dell’operazione asincrona
  • builder — callback con tre rami: hasError (mostrare errore), hasData (mostrare dati), default (indicatore di caricamento)
  • FutureBuilder vs setState — FutureBuilder è più semplice per una operazione, setState con Bloc/Riverpod è migliore per logiche complesse con richieste multiple
  • Prevenzione richieste ripetute — Future dovrebbe essere un campo dello State, non createlo nel metodo build per evitare riavvii ad ogni ricostruzione
  • Cancellazione Future — FutureBuilder non cancella il Future al dispose; usate CancelableOperation o un flag di cancellazione per prevenire setState dopo la distruzione
  • Future multipli — per richieste parallele usate Future.wait con un singolo FutureBuilder; per sequenziali — catene in un singolo Future

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche