FutureBuilder — що це таке, робота з Future у Flutter

Автор: IT Sectr Опубліковано: 2026-07-02 Час читання: 8 хв

FutureBuilder — це віджет у Flutter, який автоматично перебудовує свій інтерфейс на основі поточного стану AsyncSnapshot, отриманого з переданого Future. На відміну від ручного виклику setState після await, FutureBuilder надає декларативний підхід: він підписується на Future при першому відтворенні та викликає builder-функцію при кожній зміні стану — завантаження, помилка або готові дані. Згідно з Flutter API Reference (2026), FutureBuilder особливо корисний для завантаження даних з мережі, читання з бази даних та будь-яких асинхронних операцій, де UI має відображати індикатор завантаження, повідомлення про помилку або готовий вміст.

Головне

  • FutureBuilder — віджет Flutter для побудови UI на основі стану Future через AsyncSnapshot (none, waiting, active, done)
  • AsyncSnapshot — об'єкт, що містить поточний стан асинхронної операції: connectionState, data та error
  • builder — callback-функція, що викликається при кожній зміні стану Future для перебудови UI
  • Обробка помилок — AsyncSnapshot.hasError дозволяє відобразити запасний інтерфейс при збої асинхронної операції
  • ConnectionState — enum з чотирма значеннями: none (немає операції), waiting (очікування), active (потік), done (завершено)

Що таке FutureBuilder у Flutter

FutureBuilder — це вбудований віджет Flutter із пакета widgets, який приймає Future та builder-функцію. Коли стан Future змінюється (виконується, завершено з даними, завершено з помилкою), FutureBuilder автоматично перебудовує UI, викликаючи builder з новим AsyncSnapshot. Це позбавляє необхідності вручну керувати станом завантаження через setState та прапорці.

На відміну від StreamBuilder, який працює з потоками даних (Stream), FutureBuilder орієнтований на одноразові асинхронні операції: HTTP-запит, читання з файлу, запит до бази даних. FutureBuilder сам керує підпискою на Future: при першій побудові він запускає Future та відстежує його завершення. При знищенні віджета FutureBuilder не скасовує Future — це відповідальність розробника.

Згідно з Flutter Cookbook (2026), FutureBuilder рекомендується для випадків, коли асинхронна операція запускається один раз при ініціалізації екрана. Для повторюваних операцій або потоків даних використовуйте StreamBuilder. Обидва віджети слідують одному патерну Reactive UI, але FutureBuilder оптимізований під одноразові запити.

Як FutureBuilder працює всередині

Внутрішня реалізація FutureBuilder підписується на Future за допомогою Future.then та catchError. При старті FutureBuilder встановлює connectionState у ConnectionState.waiting та викликає builder з порожніми даними. При успішному завершенні connectionState змінюється на ConnectionState.done з даними. При помилці snapshot.error заповнюється об'єктом помилки. Кожна зміна тригерить перебудову віджета.

AsyncSnapshot: стани та властивості

AsyncSnapshot — це об'єкт-контейнер, який FutureBuilder передає в builder-функцію при кожній зміні стану. Він містить всю інформацію про поточний статус асинхронної операції: чи триває завантаження, які дані отримано, чи сталася помилка. Розуміння AsyncSnapshot — ключ до правильної побудови UI з FutureBuilder.

ВластивістьТипОпис
connectionStateConnectionStateПоточний стан з'єднання (none, waiting, active, done)
dataT?Дані, отримані від Future (null до завершення або при помилці)
errorObject?Об'єкт помилки, якщо Future завершилася з винятком
hasDatabooltrue, якщо data не null та стан ConnectionState.done
hasErrorbooltrue, якщо Future завершилася з помилкою

ConnectionState: чотири стани асинхронної операції

Еnum ConnectionState визначає стадію асинхронної операції. None — початковий стан, коли Future ще не запущено (використовується рідко, зазвичай при першій побудові без initialData). Waiting — Future виконується, дані ще не отримано. Active — використовується лише StreamBuilder для потоків з частковими даними. Done — Future завершено, дані доступні через snapshot.data або помилка через snapshot.error.

Правильна обробка всіх станів AsyncSnapshot у builder-функції — обов'язкова вимога для production-коду. Якщо не обробити стан waiting, користувач побачить порожній екран під час завантаження. Якщо не обробити hasError, користувач отримає виняток без пояснення. Рекомендований патерн: перевірка hasError → перевірка hasData → показ завантаження за замовчуванням.

Патерни використання FutureBuilder

FutureBuilder можна використовувати в кількох стандартних патернах, кожен з яких вирішує конкретне завдання. Розглянемо основні сценарії: завантаження даних при ініціалізації, завантаження з кешуванням, паралельні запити та обробка помилок з повторною спробою.

Завантаження даних при ініціалізації екрана

Найпоширеніший патерн — FutureBuilder у build-методі StatefulWidget або StatelessWidget. Future передається з initState або створюється прямо в build. При цьому важливо не створювати Future у build-методі при кожній перебудові — це призведе до повторних запитів. Використовуйте Future, збережений у полі State.

Завантаження з кешуванням та оновленням

Для запобігання повторним запитам FutureBuilder можна комбінувати з CachedNetworkImage або локальним кешем. Після першого завантаження дані зберігаються в пам'яті або SharedPreferences, а FutureBuilder відображає кешовані дані миттєво, паралельно оновлюючи їх з мережі. Це покращує UX за рахунок миттєвого відгуку.

Згідно з pub.dev (2026), кешування особливо актуальне для зображень та списків даних. FutureBuilder з CachedNetworkImageProvider автоматично відображає закешоване зображення, а при його відсутності — індикатор завантаження з наступним відображенням завантаженого файлу.

FutureBuilder vs setState: що вибрати

FutureBuilder та ручне керування станом через setState — два підходи до асинхронного UI у Flutter. Кожен має свої переваги та обмеження. Вибір залежить від складності екрана та кількості асинхронних операцій.

FutureBuilder перемагає в простоті: не потрібно оголошувати поля для стану завантаження, даних та помилки — все керується через AsyncSnapshot. Він ідеальний для простих екранів з однією асинхронною операцією (один HTTP-запит, читання з бази). Однак при 5+ асинхронних операціях на одному екрані FutureBuilder створює надмірну вкладеність — виходить «піраміда» з вкладених FutureBuilder.

setState з ручними прапорцями стану дає більше контролю та читабельності при складній логіці. Для екранів з безліччю залежних запитів (завантажити користувача → завантажити його замовлення → завантажити деталі замовлення) краще використовувати setState з ChangeNotifier або Bloc. Згідно з Flutter State Management Guide (2026), для складних сценаріїв рекомендується використовувати Riverpod або Bloc замість FutureBuilder, оскільки вони забезпечують краще розділення логіки та представлення.

Приклад FutureBuilder із завантаженням даних з мережі

Розглянемо практичний приклад FutureBuilder для завантаження списку користувачів з REST API. Код демонструє коректну обробку всіх трьох станів AsyncSnapshot: завантаження, помилка та готові дані.

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('Користувачі')),
      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('Помилка: ${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());
        },
      ),
    );
  }
}

У прикладі FutureBuilder обробляє всі три стани. При помилці відображається іконка з повідомленням про помилку. При успішному завантаженні — ListView з аватарками та іменами. Під час завантаження — CircularProgressIndicator. Future оголошено як поле класу, що запобігає повторному виклику при перебудові. Такий патерн покриває 90% сценаріїв використання FutureBuilder у мобільних додатках.

Поширені запитання

Чому FutureBuilder викликає builder кілька разів?

FutureBuilder викликає builder при кожній зміні стану Future: перший раз при створенні (connectionState: none або waiting), другий раз при завершенні (connectionState: done). Якщо батьківський віджет перебудовується, FutureBuilder також перебудовується. Для запобігання повторним викликам переконайтеся, що Future створюється поза build-методом — інакше кожен виклик build створює новий Future.

Як запобігти повторному запиту при перебудові?

Збережіть Future у полі StatefulWidget (в initState) або використовуйте мемоізацію. Якщо Future створюється всередині build-методу, кожен виклик build буде створювати новий Future, і FutureBuilder перезапустить асинхронну операцію. Для StatelessWidget використовуйте пакет cached_future або keep-alive віджети, щоб Future виконувався один раз незалежно від перебудов.

Чим FutureBuilder відрізняється від StreamBuilder?

FutureBuilder призначений для одноразових асинхронних операцій (один HTTP-запит, одне читання з бази). StreamBuilder працює з потоками даних, які можуть емітувати безліч значень у часі (чат, оновлення ціни, геолокація). StreamBuilder підтримує ConnectionState.active для часткових даних, а FutureBuilder — тільки waiting та done.

Як використовувати FutureBuilder з кількома Future?

Для кількох паралельних Future використовуйте Future.wait та передайте результат в один FutureBuilder. Future.wait приймає список Future і повертає Future — коли всі Future завершено, builder отримує масив результатів. Для послідовних запитів використовуйте ланцюжок Future.then всередині одного Future або вкладені FutureBuilder (менш читабельно). Альтернатива — пакет riverpod з AsyncValue для множинних асинхронних станів.

Як скасувати Future при виході з екрана?

FutureBuilder не скасовує Future автоматично. Для скасування використовуйте CancelableOperation з пакета async або власний механізм через прапорець cancelled у State. У dispose() встановіть прапорець, а після завершення Future перевіряйте його перед викликом setState. Альтернативно використовуйте пакет riverpod з AutoDispose, який автоматично скасовує асинхронні операції при виході з екрана.

Підсумки

  • FutureBuilder — віджет Flutter для декларативної побудови UI на основі стану Future через AsyncSnapshot (waiting, done, error)
  • AsyncSnapshot — контейнер з connectionState, data та error; обов'язковий для коректної обробки всіх станів асинхронної операції
  • builder — callback з трьома гілками: hasError (показ помилки), hasData (відображення даних), default (індикатор завантаження)
  • FutureBuilder vs setState — FutureBuilder простіший для однієї операції, setState з Bloc/Riverpod кращий для складної логіки з численними запитами
  • Запобігання повторним запитам — Future має бути полем State, не створюйте його в build-методі, щоб уникнути перезапуску при кожній перебудові
  • Скасування Future — FutureBuilder не скасовує Future при dispose; використовуйте CancelableOperation або прапорець скасування для запобігання setState після знищення
  • Множинні Future — для паралельних запитів використовуйте Future.wait з одним FutureBuilder; для послідовних — ланцюжки в одному Future

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також