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 позволяет отобразить fallback-интерфейс при сбое асинхронной операции
  • 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: четыре состояния асинхронной операции

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

Правильная обработка всех состояний AsyncSnapshot в builder-функции — обязательное требование для production-кода. Если не обработать состояние waiting, пользователь увидит пустой экран во время загрузки. Если не обработать hasError, пользователь получит Exception без объяснения. Рекомендуемый паттерн: проверка 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('Users')),
      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('Error: ${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) или используйте memoization. Если 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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