В чем разница между StreamBuilder и Future?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между 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();
});
}
Сравнение
| Аспект | FutureBuilder | StreamBuilder |
|---|---|---|
| Тип данных | 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). Выбор зависит от характера данных: статические или динамические.