← Назад к вопросам
Приведи пример использования Safe Area
1.0 Junior🔥 251 комментариев
#Flutter виджеты
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример использования FutureBuilder
FutureBuilder — это один из самых полезных виджетов в Flutter для работы с асинхронными операциями.
Основной пример: загрузка данных пользователя
import 'package:flutter/material.dart';
class UserProfilePage extends StatelessWidget {
final String userId;
UserProfilePage({required this.userId});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('User Profile')),
body: FutureBuilder<User>(
future: fetchUser(userId),
builder: (BuildContext context, AsyncSnapshot<User> 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 UserContent(user: snapshot.data!);
} else {
return Center(child: Text('No data'));
}
},
),
);
}
}
class UserContent extends StatelessWidget {
final User user;
UserContent({required this.user});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
CircleAvatar(radius: 50, backgroundImage: NetworkImage(user.avatar)),
SizedBox(height: 20),
Text(user.name, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Text(user.email),
SizedBox(height: 20),
Text('Bio: ${user.bio}'),
],
),
);
}
}
future<User> fetchUser(String userId) async {
final response = await http.get(Uri.parse('https://api.example.com/users/$userId'));
if (response.statusCode == 200) {
return User.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load user');
}
}
class User {
final String id;
final String name;
final String email;
final String avatar;
final String bio;
User({
required this.id,
required this.name,
required this.email,
required this.avatar,
required this.bio,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
avatar: json['avatar'],
bio: json['bio'],
);
}
}
Состояния FutureBuilder
FutureBuilder имеет 4 основных состояния:
- ConnectionState.none — Future еще не создан
- ConnectionState.waiting — Future выполняется (загрузка)
- ConnectionState.active — Stream активен (не для Future)
- ConnectionState.done — Future завершен
Пример с эффектной загрузкой
class DataListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Posts')),
body: FutureBuilder<List<Post>>(
future: fetchPosts(),
builder: (context, snapshot) {
// Состояние загрузки
if (snapshot.connectionState == ConnectionState.waiting) {
return _LoadingWidget();
}
// Состояние ошибки
if (snapshot.hasError) {
return _ErrorWidget(
error: snapshot.error.toString(),
onRetry: () {
// Пересчитать Future
},
);
}
// Нет данных
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return _EmptyWidget();
}
// Успешная загрузка
final posts = snapshot.data!;
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
return PostCard(post: posts[index]);
},
);
},
),
);
}
}
class _LoadingWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading posts...'),
],
),
);
}
}
class _ErrorWidget extends StatelessWidget {
final String error;
final VoidCallback onRetry;
_ErrorWidget({required this.error, required this.onRetry});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: Colors.red),
SizedBox(height: 16),
Text('Error: $error'),
SizedBox(height: 16),
ElevatedButton(
onPressed: onRetry,
child: Text('Retry'),
),
],
),
);
}
}
class _EmptyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.inbox, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('No posts found'),
],
),
);
}
}
Best Practice: Использование с Riverpod
final postsProvider = FutureProvider<List<Post>>((ref) async {
return fetchPosts();
});
class PostsScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final posts = ref.watch(postsProvider);
return posts.when(
data: (posts) => ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) => PostCard(post: posts[index]),
),
loading: () => Center(child: CircularProgressIndicator()),
error: (error, st) => Center(child: Text('Error: $error')),
);
}
}
Важные моменты
- Future кэшируется — Future не пересчитывается, пока виджет не пересоздан
- Обработка null данных — используйте .data! с осторожностью
- Отмена запросов — FutureBuilder не автоматически отменяет Future
- Performance — для часто обновляемых данных лучше использовать StreamBuilder