FutureBuilder — це віджет у Flutter, який автоматично перебудовує свій інтерфейс на основі поточного стану AsyncSnapshot, отриманого з переданого Future. На відміну від ручного виклику setState після await, FutureBuilder надає декларативний підхід: він підписується на Future при першому відтворенні та викликає builder-функцію при кожній зміні стану — завантаження, помилка або готові дані. Згідно з Flutter API Reference (2026), FutureBuilder особливо корисний для завантаження даних з мережі, читання з бази даних та будь-яких асинхронних операцій, де UI має відображати індикатор завантаження, повідомлення про помилку або готовий вміст.
Головне
FutureBuilder — це вбудований віджет Flutter із пакета widgets, який приймає Future
На відміну від StreamBuilder, який працює з потоками даних (Stream), FutureBuilder орієнтований на одноразові асинхронні операції: HTTP-запит, читання з файлу, запит до бази даних. FutureBuilder сам керує підпискою на Future: при першій побудові він запускає Future та відстежує його завершення. При знищенні віджета FutureBuilder не скасовує Future — це відповідальність розробника.
Згідно з Flutter Cookbook (2026), FutureBuilder рекомендується для випадків, коли асинхронна операція запускається один раз при ініціалізації екрана. Для повторюваних операцій або потоків даних використовуйте StreamBuilder. Обидва віджети слідують одному патерну Reactive UI, але FutureBuilder оптимізований під одноразові запити.
Внутрішня реалізація FutureBuilder підписується на Future за допомогою Future.then та catchError. При старті FutureBuilder встановлює connectionState у ConnectionState.waiting та викликає builder з порожніми даними. При успішному завершенні connectionState змінюється на ConnectionState.done з даними. При помилці snapshot.error заповнюється об'єктом помилки. Кожна зміна тригерить перебудову віджета.
AsyncSnapshot — це об'єкт-контейнер, який FutureBuilder передає в builder-функцію при кожній зміні стану. Він містить всю інформацію про поточний статус асинхронної операції: чи триває завантаження, які дані отримано, чи сталася помилка. Розуміння AsyncSnapshot — ключ до правильної побудови UI з FutureBuilder.
| Властивість | Тип | Опис |
|---|---|---|
| connectionState | ConnectionState | Поточний стан з'єднання (none, waiting, active, done) |
| data | T? | Дані, отримані від Future (null до завершення або при помилці) |
| error | Object? | Об'єкт помилки, якщо Future завершилася з винятком |
| hasData | bool | true, якщо data не null та стан ConnectionState.done |
| hasError | bool | true, якщо Future завершилася з помилкою |
Еnum ConnectionState визначає стадію асинхронної операції. None — початковий стан, коли Future ще не запущено (використовується рідко, зазвичай при першій побудові без initialData). Waiting — Future виконується, дані ще не отримано. Active — використовується лише StreamBuilder для потоків з частковими даними. Done — Future завершено, дані доступні через snapshot.data або помилка через snapshot.error.
Правильна обробка всіх станів AsyncSnapshot у builder-функції — обов'язкова вимога для production-коду. Якщо не обробити стан waiting, користувач побачить порожній екран під час завантаження. Якщо не обробити hasError, користувач отримає виняток без пояснення. Рекомендований патерн: перевірка hasError → перевірка hasData → показ завантаження за замовчуванням.
FutureBuilder можна використовувати в кількох стандартних патернах, кожен з яких вирішує конкретне завдання. Розглянемо основні сценарії: завантаження даних при ініціалізації, завантаження з кешуванням, паралельні запити та обробка помилок з повторною спробою.
Найпоширеніший патерн — FutureBuilder у build-методі StatefulWidget або StatelessWidget. Future передається з initState або створюється прямо в build. При цьому важливо не створювати Future у build-методі при кожній перебудові — це призведе до повторних запитів. Використовуйте Future, збережений у полі State.
Для запобігання повторним запитам FutureBuilder можна комбінувати з CachedNetworkImage або локальним кешем. Після першого завантаження дані зберігаються в пам'яті або SharedPreferences, а FutureBuilder відображає кешовані дані миттєво, паралельно оновлюючи їх з мережі. Це покращує UX за рахунок миттєвого відгуку.
Згідно з pub.dev (2026), кешування особливо актуальне для зображень та списків даних. FutureBuilder з CachedNetworkImageProvider автоматично відображає закешоване зображення, а при його відсутності — індикатор завантаження з наступним відображенням завантаженого файлу.
FutureBuilder та ручне керування станом через setState — два підходи до асинхронного UI у Flutter. Кожен має свої переваги та обмеження. Вибір залежить від складності екрана та кількості асинхронних операцій.
FutureBuilder перемагає в простоті: не потрібно оголошувати поля для стану завантаження, даних та помилки — все керується через AsyncSnapshot. Він ідеальний для простих екранів з однією асинхронною операцією (один HTTP-запит, читання з бази). Однак при 5+ асинхронних операціях на одному екрані FutureBuilder створює надмірну вкладеність — виходить «піраміда» з вкладених FutureBuilder.
setState з ручними прапорцями стану дає більше контролю та читабельності при складній логіці. Для екранів з безліччю залежних запитів (завантажити користувача → завантажити його замовлення → завантажити деталі замовлення) краще використовувати setState з ChangeNotifier або Bloc. Згідно з Flutter State Management Guide (2026), для складних сценаріїв рекомендується використовувати Riverpod або Bloc замість FutureBuilder, оскільки вони забезпечують краще розділення логіки та представлення.
Розглянемо практичний приклад FutureBuilder для завантаження списку користувачів з REST API. Код демонструє коректну обробку всіх трьох станів AsyncSnapshot: завантаження, помилка та готові дані.
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 при кожній зміні стану 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 призначений для одноразових асинхронних операцій (один HTTP-запит, одне читання з бази). StreamBuilder працює з потоками даних, які можуть емітувати безліч значень у часі (чат, оновлення ціни, геолокація). StreamBuilder підтримує ConnectionState.active для часткових даних, а FutureBuilder — тільки waiting та done.
Для кількох паралельних Future використовуйте Future.wait та передайте результат в один FutureBuilder. Future.wait приймає список Future і повертає Future — коли всі Future завершено, builder отримує масив результатів. Для послідовних запитів використовуйте ланцюжок Future.then всередині одного Future або вкладені FutureBuilder (менш читабельно). Альтернатива — пакет riverpod з AsyncValue для множинних асинхронних станів.
FutureBuilder не скасовує Future автоматично. Для скасування використовуйте CancelableOperation з пакета async або власний механізм через прапорець cancelled у State. У dispose() встановіть прапорець, а після завершення Future перевіряйте його перед викликом setState. Альтернативно використовуйте пакет riverpod з AutoDispose, який автоматично скасовує асинхронні операції при виході з екрана.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також