Что такое FutureBuilder?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
FutureBuilder - виджет для работы с асинхронными операциями
FutureBuilder - это виджет, который строит UI на основе состояния Future. Это стандартный способ отображения результатов асинхронных операций (загрузка данных, API запросы).
Базовая структура
FutureBuilder<String>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
} else if (snapshot.hasData) {
return Text("Data: ${snapshot.data}");
}
return Text("No data");
},
)
Состояния ConnectionState
ConnectionState.none - Future еще не начал выполняться ConnectionState.waiting - Future в процессе (показываем loader) ConnectionState.active - Stream активен (для StreamBuilder) ConnectionState.done - Future завершен (показываем результат)
Практические примеры
Загрузка данных с API:
Future<List<Post>> fetchPosts() async {
final response = await http.get(Uri.parse("https://api.example.com/posts"));
if (response.statusCode == 200) {
return Post.fromJsonList(jsonDecode(response.body));
} else {
throw Exception("Failed to load posts");
}
}
FutureBuilder<List<Post>>(
future: fetchPosts(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text("Error: ${snapshot.error}"));
} else if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data![index].title),
);
},
);
}
return Center(child: Text("No data"));
},
)
Инициализация данных в StatefulWidget:
class UserProfile extends StatefulWidget {
final int userId;
const UserProfile({required this.userId});
@override
State<UserProfile> createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
late Future<User> userFuture;
@override
void initState() {
super.initState();
userFuture = fetchUser(widget.userId);
}
@override
Widget build(BuildContext context) {
return FutureBuilder<User>(
future: userFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(title: Text("Loading...")),
body: Center(child: CircularProgressIndicator()),
);
} else if (snapshot.hasError) {
return Scaffold(
appBar: AppBar(title: Text("Error")),
body: Center(child: Text("${snapshot.error}")),
);
} else if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(title: Text(snapshot.data!.name)),
body: UserContent(user: snapshot.data!),
);
}
return Scaffold(body: Center(child: Text("No data")));
},
);
}
}
Проверка snapshot данных
builder: (context, snapshot) {
// Проверка состояния подключения
if (snapshot.connectionState == ConnectionState.waiting) {
return LoadingWidget();
}
// Проверка ошибок
if (snapshot.hasError) {
return ErrorWidget(error: snapshot.error);
}
// Проверка наличия данных
if (!snapshot.hasData) {
return EmptyWidget();
}
// Получение данных (гарантированно не null)
final data = snapshot.data!;
return DataWidget(data: data);
}
FutureBuilder vs StreamBuilder
FutureBuilder - для одного результата (Future)
Future<Data> loadOnce() async { ... }
StreamBuilder - для множественных результатов (Stream)
Stream<Data> streamData() async* {
while(true) {
yield fetchData();
await Future.delayed(Duration(seconds: 1));
}
}
Проблемы и решения
Проблема: FutureBuilder создается каждый раз
// Плохо - Future создается при каждом build
FutureBuilder<Data>(
future: fetchData(), // Новый Future каждый раз!
...
)
// Хорошо - Future создается один раз в initState
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late Future<Data> _future;
@override
void initState() {
super.initState();
_future = fetchData();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<Data>(
future: _future,
...
);
}
}
Best Practice
- Создавай Future в initState для StatefulWidget
- Всегда обрабатывай все состояния (waiting, error, data)
- Используй snapshot.hasError для проверки ошибок
- Используй snapshot.data! только после проверки hasData
- Для сложного состояния используй BLoC или Riverpod
FutureBuilder - стандартный и удобный способ работы с асинхронными операциями во Flutter.