← Назад к вопросам

В чем разница между StreamBuilder и Future?

1.8 Middle🔥 161 комментариев
#Асинхронность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между StreamBuilder и FutureBuilder

FutureBuilder и StreamBuilder — два способа работы с асинхронными данными в Flutter. FutureBuilder используется для одноразовых операций, а StreamBuilder — для потока данных, которые обновляются.

FutureBuilder

FutureBuilder — виджет, который строит интерфейс на основе Future (одноразовый результат).

Характеристики:

  • Одноразовый результат: выполняется один раз и возвращает результат
  • Состояния: loading, data, error
  • Простота: легко реализовать простые операции
  • Когда использовать: загрузка данных при открытии экрана, HTTP запрос
FutureBuilder<List<User>>(
  future: fetchUsers(), // Future возвращает одноразовый результат
  builder: (context, snapshot) {
    // Ожидание результата
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();
    }
    
    // Ошибка
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }
    
    // Успешный результат
    if (snapshot.hasData) {
      return ListView.builder(
        itemCount: snapshot.data!.length,
        itemBuilder: (context, index) {
          final user = snapshot.data![index];
          return ListTile(title: Text(user.name));
        },
      );
    }
    
    return const SizedBox.shrink();
  },
)

Пример Future функции:

Future<List<User>> fetchUsers() async {
  final response = await http.get(
    Uri.parse('https://api.example.com/users'),
  );
  
  if (response.statusCode == 200) {
    final List<dynamic> json = jsonDecode(response.body);
    return json.map((u) => User.fromJson(u)).toList();
  }
  throw Exception('Failed to load users');
}

StreamBuilder

StreamBuilder — виджет, который слушает поток данных и перестраивается при каждом новом значении.

Характеристики:

  • Поток данных: непрерывный источник данных, много значений
  • Живые обновления: реагирует на каждое новое значение
  • WebSocket, Firestore, BLoC: идеален для real-time данных
  • Когда использовать: чат, live notifications, real-time обновления
StreamBuilder<List<Message>>(
  stream: chatService.messagesStream(), // Stream возвращает много значений
  builder: (context, snapshot) {
    // Ожидание первого значения
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();
    }
    
    // Ошибка
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }
    
    // Каждый новый результат перестраивает виджет
    if (snapshot.hasData) {
      final messages = snapshot.data!;
      return ListView.builder(
        itemCount: messages.length,
        itemBuilder: (context, index) {
          return Text(messages[index].text);
        },
      );
    }
    
    return const SizedBox.shrink();
  },
)

Пример Stream функции:

Stream<List<Message>> messagesStream() {
  // WebSocket или Firestore слушает обновления
  return FirebaseFirestore.instance
      .collection('messages')
      .snapshots()
      .map((snapshot) {
    return snapshot.docs
        .map((doc) => Message.fromJson(doc.data()))
        .toList();
  });
}

Сравнение

АспектFutureBuilderStreamBuilder
Тип данныхFuture (одноразово)Stream (поток)
Количество результатовОдинМного
ОбновленияОдин разПостоянно
Когда использоватьHTTP запросWebSocket, реал-тайм
СложностьПростаСредняя
ПроизводительностьЛучше (один раз)Может быть нагрузка
ПримерыЗагрузка профиляЧат, уведомления

Визуальное объяснение

Future — одноразовый результат:

start → waiting → result → done

Stream — поток значений:

start → waiting → value1 → value2 → value3 → ... (бесконечно)

Практические примеры

Пример 1: FutureBuilder для загрузки пользователя

class UserProfileScreen extends StatelessWidget {
  final String userId;
  
  const UserProfileScreen({required this.userId});
  
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<User>(
      future: UserService().getUser(userId),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Scaffold(
            body: Center(child: CircularProgressIndicator()),
          );
        }
        
        if (snapshot.hasError) {
          return Scaffold(
            body: Center(child: Text('Error: ${snapshot.error}')),
          );
        }
        
        final user = snapshot.data!;
        return Scaffold(
          appBar: AppBar(title: Text(user.name)),
          body: Column(
            children: [
              Text('Name: ${user.name}'),
              Text('Email: ${user.email}'),
            ],
          ),
        );
      },
    );
  }
}

Пример 2: StreamBuilder для live чата

class ChatScreen extends StatefulWidget {
  final String chatId;
  
  const ChatScreen({required this.chatId});
  
  @override
  State<ChatScreen> createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  late ChatService _chatService;
  
  @override
  void initState() {
    super.initState();
    _chatService = ChatService();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Chat')),
      body: StreamBuilder<List<Message>>(
        stream: _chatService.getMessages(widget.chatId),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          
          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }
          
          final messages = snapshot.data ?? [];
          return ListView.builder(
            itemCount: messages.length,
            itemBuilder: (context, index) {
              final message = messages[index];
              return ListTile(
                title: Text(message.text),
                subtitle: Text(message.timestamp.toString()),
              );
            },
          );
        },
      ),
    );
  }
}

Выбор между ними

Используй FutureBuilder когда:

  • Загружаешь данные один раз при открытии экрана
  • Нет необходимости в постоянных обновлениях
  • Простые HTTP запросы (GET пользователя, список товаров)

Используй StreamBuilder когда:

  • Данные обновляются постоянно (чат, уведомления)
  • Используешь WebSocket или Firestore
  • Нужна real-time синхронизация
  • Используешь BLoC паттерн

Комбинирование с BLoC

StreamBuilder часто используется с BLoC:

StreamBuilder<UserState>(
  stream: _userBloc.stateStream,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      final state = snapshot.data!;
      if (state is UserLoading) {
        return const CircularProgressIndicator();
      } else if (state is UserLoaded) {
        return Text(state.user.name);
      } else if (state is UserError) {
        return Text(state.error);
      }
    }
    return const SizedBox.shrink();
  },
)

Вывод

FutureBuilder — для одноразовых асинхронных операций (HTTP запросы). StreamBuilder — для потока постоянно обновляющихся данных (WebSocket, Firestore, BLoC). Выбор зависит от характера данных: статические или динамические.