FutureBuilder — co to jest, praca z Future we Flutter

Autor: IT Sectr Opublikowano: 2026-07-02 Czas czytania: 8 min

FutureBuilder — to widget we Flutter, który automatycznie przebudowuje swój interfejs na podstawie bieżącego stanu AsyncSnapshot, otrzymywanego z przekazanego Future. W przeciwieństwie do ręcznego wywoływania setState po await, FutureBuilder zapewnia deklaratywne podejście: subskrybuje Future przy pierwszym renderowaniu i wywołuje funkcję builder przy każdej zmianie stanu — ładowanie, błąd lub gotowe dane. Według Flutter API Reference (2026), FutureBuilder jest szczególnie przydatny do ładowania danych z sieci, odczytu z bazy danych i wszelkich operacji asynchronicznych, gdzie UI musi wyświetlać wskaźnik ładowania, komunikat o błędzie lub gotową treść.

Najważniejsze

  • FutureBuilder — widget Flutter do budowania UI w oparciu o stan Future przez AsyncSnapshot (none, waiting, active, done)
  • AsyncSnapshot — obiekt zawierający bieżący stan operacji asynchronicznej: connectionState, data i error
  • builder — funkcja callback wywoływana przy każdej zmianie stanu Future w celu przebudowania UI
  • Obsługa błędów — AsyncSnapshot.hasError pozwala wyświetlić interfejs zastępczy w przypadku błędu operacji asynchronicznej
  • ConnectionState — enum z czterema wartościami: none (brak operacji), waiting (oczekiwanie), active (strumień), done (zakończone)

Czym jest FutureBuilder we Flutter

FutureBuilder — to wbudowany widget Flutter z pakietu widgets, który przyjmuje Future i funkcję builder. Przy zmianie stanu Future (wykonuje się, zakończony z danymi, zakończony z błędem) FutureBuilder automatycznie przebudowuje UI, wywołując builder z nowym AsyncSnapshot. Eliminuje to konieczność ręcznego zarządzania stanem ładowania przez setState i flagi.

W przeciwieństwie do StreamBuilder, który pracuje ze strumieniami danych (Stream), FutureBuilder jest przeznaczony do jednorazowych operacji asynchronicznych: zapytanie HTTP, odczyt z pliku, zapytanie do bazy danych. FutureBuilder sam zarządza subskrypcją Future: przy pierwszym budowaniu uruchamia Future i śledzi jego zakończenie. Przy zniszczeniu widgetu FutureBuilder nie anuluje Future — to odpowiedzialność programisty.

Według Flutter Cookbook (2026), FutureBuilder jest zalecany w przypadkach, gdy operacja asynchroniczna uruchamiana jest raz przy inicjalizacji ekranu. Dla powtarzających się operacji lub strumieni danych używaj StreamBuilder. Oba widgety kierują się tym samym wzorcem Reactive UI, ale FutureBuilder jest zoptymalizowany pod kątem jednorazowych zapytań.

Jak FutureBuilder działa pod maską

Wewnętrzna implementacja FutureBuilder subskrybuje Future za pomocą Future.then i catchError. Przy starcie FutureBuilder ustawia connectionState na ConnectionState.waiting i wywołuje builder z pustymi danymi. Po pomyślnym zakończeniu connectionState zmienia się na ConnectionState.done z danymi. Przy błędzie snapshot.error wypełniany jest obiektem błędu. Każda zmiana wyzwala przebudowę widgetu.

AsyncSnapshot: stany i właściwości

AsyncSnapshot — to obiekt-kontener, który FutureBuilder przekazuje do funkcji builder przy każdej zmianie stanu. Zawiera wszystkie informacje o bieżącym statusie operacji asynchronicznej: czy trwa ładowanie, jakie dane zostały otrzymane, czy wystąpił błąd. Zrozumienie AsyncSnapshot to klucz do prawidłowego budowania UI z FutureBuilder.

WłaściwośćTypOpis
connectionStateConnectionStateBieżący stan połączenia (none, waiting, active, done)
dataT?Dane otrzymane z Future (null do zakończenia lub przy błędzie)
errorObject?Obiekt błędu, jeśli Future zakończyła się wyjątkiem
hasDatabooltrue, jeśli data nie jest null i stan to ConnectionState.done
hasErrorbooltrue, jeśli Future zakończyła się błędem

ConnectionState: cztery stany operacji asynchronicznej

Enum ConnectionState określa etap operacji asynchronicznej. None — stan początkowy, gdy Future nie został jeszcze uruchomiony (używany rzadko, zwykle przy pierwszym budowaniu bez initialData). Waiting — Future jest wykonywany, dane nie zostały jeszcze otrzymane. Active — używany tylko przez StreamBuilder dla strumieni z częściowymi danymi. Done — Future zakończony, dane dostępne przez snapshot.data lub błąd przez snapshot.error.

Prawidłowa obsługa wszystkich stanów AsyncSnapshot w funkcji builder — obowiązkowy wymóg dla kodu produkcyjnego. Jeśli nie obsłużysz stanu waiting, użytkownik zobaczy pusty ekran podczas ładowania. Jeśli nie obsłużysz hasError, użytkownik otrzyma Exception bez wyjaśnienia. Zalecany wzorzec: sprawdzenie hasError → sprawdzenie hasData → domyślnie pokazanie ładowania.

Wzorce użycia FutureBuilder

FutureBuilder można używać w kilku standardowych wzorcach, z których każdy rozwiązuje konkretne zadanie. Rozważmy główne scenariusze: ładowanie danych przy inicjalizacji, ładowanie z buforowaniem, równoległe zapytania i obsługa błędów z ponowieniem.

Ładowanie danych przy inicjalizacji ekranu

Najczęstszy wzorzec — FutureBuilder w metodzie build StatefulWidget lub StatelessWidget. Future jest przekazywany z initState lub tworzony bezpośrednio w build. Ważne, aby nie tworzyć Future w metodzie build przy każdym przebudowaniu — doprowadzi to do ponownych zapytań. Używaj Future zapisanego w polu State.

Ładowanie z buforowaniem i odświeżaniem

Aby zapobiec ponownym zapytaniom, FutureBuilder można łączyć z CachedNetworkImage lub lokalnym buforem. Po pierwszym załadowaniu dane są zapisywane w pamięci lub SharedPreferences, a FutureBuilder wyświetla buforowane dane natychmiast, równolegle aktualizując je z sieci. Poprawia to UX dzięki natychmiastowej odpowiedzi.

Według pub.dev (2026), buforowanie jest szczególnie istotne dla obrazów i list danych. FutureBuilder z CachedNetworkImageProvider automatycznie wyświetla buforowany obraz, a w przypadku jego braku — wskaźnik ładowania z następczym wyświetleniem pobranego pliku.

FutureBuilder vs setState: co wybrać

FutureBuilder i ręczne zarządzanie stanem przez setState — dwa podejścia do asynchronicznego UI we Flutter. Każde ma swoje zalety i ograniczenia. Wybór zależy od złożoności ekranu i liczby operacji asynchronicznych.

FutureBuilder wygrywa prostotą: nie trzeba deklarować pól dla stanu ładowania, danych i błędu — wszystko jest zarządzane przez AsyncSnapshot. Jest idealny dla prostych ekranów z jedną operacją asynchroniczną (jedno zapytanie HTTP, odczyt z bazy). Jednak przy 5+ operacjach asynchronicznych na jednym ekranie FutureBuilder tworzy nadmierne zagnieżdżenie — powstaje „piramida“ zagnieżdżonych FutureBuilder.

setState z ręcznymi flagami stanu daje więcej kontroli i czytelności przy złożonej logice. Dla ekranów z wieloma zależnymi zapytaniami (załadowanie użytkownika → załadowanie jego zamówień → załadowanie szczegółów zamówienia) lepiej użyć setState z ChangeNotifier lub Bloc. Według Flutter State Management Guide (2026), dla złożonych scenariuszy zaleca się używanie Riverpod lub Bloc zamiast FutureBuilder, ponieważ zapewniają one lepsze oddzielenie logiki od prezentacji.

Przykład FutureBuilder z ładowaniem danych z sieci

Rozważmy praktyczny przykład FutureBuilder do ładowania listy użytkowników z REST API. Kod demonstruje prawidłową obsługę wszystkich trzech stanów AsyncSnapshot: ładowanie, błąd i gotowe dane.

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('Użytkownicy')),
      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('Błąd: ${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());
        },
      ),
    );
  }
}

W przykładzie FutureBuilder obsługuje wszystkie trzy stany. Przy błędzie wyświetlana jest ikona z komunikatem o błędzie. Przy pomyślnym załadowaniu — ListView z awatarami i nazwami. Podczas ładowania — CircularProgressIndicator. Future jest zadeklarowane jako pole klasy, co zapobiega ponownemu wywołaniu przy przebudowie. Taki wzorzec pokrywa 90% scenariuszy użycia FutureBuilder w aplikacjach mobilnych.

Często zadawane pytania

Dlaczego FutureBuilder wywołuje builder kilka razy?

FutureBuilder wywołuje builder przy każdej zmianie stanu Future: pierwszy raz przy utworzeniu (connectionState: none lub waiting), drugi raz przy zakończeniu (connectionState: done). Jeśli rodzicielski widget się przebudowuje, FutureBuilder również się przebudowuje. Aby zapobiec ponownym wywołaniom, upewnij się, że Future jest tworzone poza metodą build — w przeciwnym razie każde wywołanie build tworzy nowe Future.

Jak zapobiec ponownemu zapytaniu przy przebudowie?

Zapisz Future w polu StatefulWidget (w initState) lub użyj memoizacji. Jeśli Future jest tworzone wewnątrz metody build, każde wywołanie build będzie tworzyć nowe Future, a FutureBuilder uruchomi ponownie operację asynchroniczną. Dla StatelessWidget użyj pakietu cached_future lub widgetów keep-alive, aby Future wykonało się raz niezależnie od przebudowy.

Czym FutureBuilder różni się od StreamBuilder?

FutureBuilder jest przeznaczony do jednorazowych operacji asynchronicznych (jedno zapytanie HTTP, jeden odczyt z bazy). StreamBuilder pracuje ze strumieniami danych, które mogą emitować wiele wartości w czasie (czat, aktualizacje ceny, geolokalizacja). StreamBuilder obsługuje ConnectionState.active dla danych częściowych, a FutureBuilder — tylko waiting i done.

Jak używać FutureBuilder z wieloma Future?

Dla kilku równoległych Future użyj Future.wait i przekaż wynik do jednego FutureBuilder. Future.wait przyjmuje listę Future i zwraca Future — gdy wszystkie Future zostaną zakończone, builder otrzymuje tablicę wyników. Dla sekwencyjnych zapytań użyj łańcucha Future.then wewnątrz jednego Future lub zagnieżdżonych FutureBuilder (mniej czytelne). Alternatywą jest pakiet riverpod z AsyncValue dla wielokrotnych stanów asynchronicznych.

Jak anulować Future przy wyjściu z ekranu?

FutureBuilder nie anuluje Future automatycznie. Do anulowania użyj CancelableOperation z pakietu async lub własnego mechanizmu przez flagę cancelled w State. W dispose() ustaw flagę, a po zakończeniu Future sprawdź ją przed wywołaniem setState. Alternatywnie użyj pakietu riverpod z AutoDispose, który automatycznie anuluje operacje asynchroniczne przy wyjściu z ekranu.

Podsumowanie

  • FutureBuilder — widget Flutter do deklaratywnego budowania UI w oparciu o stan Future przez AsyncSnapshot (waiting, done, error)
  • AsyncSnapshot — kontener z connectionState, data i error; obowiązkowy do prawidłowej obsługi wszystkich stanów operacji asynchronicznej
  • builder — callback z trzema gałęziami: hasError (wyświetlenie błędu), hasData (wyświetlenie danych), default (wskaźnik ładowania)
  • FutureBuilder vs setState — FutureBuilder prostszy dla jednej operacji, setState z Bloc/Riverpod lepszy dla złożonej logiki z wieloma zapytaniami
  • Zapobieganie ponownym zapytaniom — Future musi być polem State, nie twórz go w metodzie build, aby uniknąć ponownego uruchamiania przy każdej przebudowie
  • Anulowanie Future — FutureBuilder nie anuluje Future przy dispose; używaj CancelableOperation lub flagi anulowania, aby zapobiec setState po zniszczeniu
  • Wiele Future — dla równoległych zapytań używaj Future.wait z jednym FutureBuilder; dla sekwencyjnych — łańcuchy w jednym Future

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również