Загрузка данных из сети и FutureBuilder
FutureBuilder связывает асинхронные данные с интерфейсом, автоматически показывая загрузку, ошибку или результат.
Суть: вы запрашиваете данные по HTTP, парсите JSON в объекты Dart, а
FutureBuilderсам перестраивает интерфейс в зависимости от состоянияFuture: ожидание, успех или ошибка.
В реальных проектах сетевой слой обычно прячут за классом-репозиторием: виджеты не знают про http и URL, они просто просят у репозитория «дай пользователя», а тот заботится о запросе, парсинге и кешировании. Такое разделение делает код тестируемым и позволяет подменить сеть заглушкой в тестах. Даже если сейчас это кажется избыточным, привыкайте отделять получение данных от их отображения — это окупится в любом растущем приложении.
Настоящее приложение живёт данными из интернета. Цепочка такая: отправить HTTP-запрос пакетом http, получить ответ в формате JSON, распарсить его в модель Dart и показать. Чтобы аккуратно отрисовать все стадии — спиннер во время загрузки, сообщение при ошибке, данные при успехе — Flutter даёт виджет FutureBuilder.
import 'dart:convert';
import 'package:http/http.dart' as http;
class User {
final String name;
User(this.name);
factory User.fromJson(Map<String, dynamic> json) =>
User(json['name'] as String);
}
Future<User> fetchUser() async {
final res = await http.get(Uri.parse('https://api.example.com/user'));
if (res.statusCode == 200) {
return User.fromJson(jsonDecode(res.body));
}
throw Exception('Не удалось загрузить');
}
// в build:
FutureBuilder<User>(
future: fetchUser(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator(); // загрузка
} else if (snapshot.hasError) {
return Text('Ошибка: ${snapshot.error}'); // ошибка
} else {
return Text('Привет, ${snapshot.data!.name}'); // успех
}
},
)
Именованный конструктор User.fromJson превращает Map (результат jsonDecode) в типизированный объект. FutureBuilder получает future и через snapshot сообщает текущее состояние.
Как работает FutureBuilder под капотом
FutureBuilder подписывается на переданный Future и перестраивает себя при смене его состояния. snapshot.connectionState сообщает, идёт ли ожидание; hasError — случилась ли ошибка; data — готовый результат. Так вы декларативно описываете все три ветки интерфейса, а синхронизацию берёт на себя Flutter.
fetchUser() -> Future<User>
|
v
FutureBuilder следит за snapshot:
waiting -> CircularProgressIndicator() (спиннер)
|
v
hasError -> Text('Ошибка: ...') (сбой сети)
|
v
hasData -> Text('Привет, Аня') (успех)
# Модель парсинга JSON и веток состояния FutureBuilder
import json
raw = '{"name": "Аня", "age": 20}' # тело ответа сервера
def from_json(raw):
data = json.loads(raw) # как jsonDecode в Dart
return {'name': data['name']} # как User.fromJson
def render(state, error=None, data=None):
if state == 'waiting':
return 'спиннер загрузки'
if state == 'error':
return f'Ошибка: {error}'
return f"Привет, {data['name']}"
print(render('waiting'))
print(render('error', error='нет сети'))
print(render('done', data=from_json(raw)))
Частые ошибки
- Создавать
Futureпрямо вbuildу StatefulWidget — он пересоздаётся при каждом перестроении, и запрос летит снова. ХранитеFutureв поле и инициализируйте вinitState. - Не обрабатывать
hasError— при сбое сети пользователь увидит исключение. - Обращаться к
snapshot.data!до проверки — пока данных нет, это null.
Best practices
- Всегда показывайте три состояния: загрузка, ошибка, данные — пользователь должен понимать, что происходит.
- Парсите JSON в типизированные модели через
fromJson, а не таскайте сырые Map по приложению. - В StatefulWidget кешируйте
Futureв поле, инициализируя его вinitState.
Ручной разбор JSON через fromJson хорош для обучения и небольших моделей, но в крупных проектах его автоматизируют генераторами кода вроде json_serializable или пакетами с неизменяемыми моделями вроде freezed. Они избавляют от рутины и опечаток в разборе десятков полей. Принцип, однако, остаётся тем же, что вы изучили вручную: сырой текст ответа превращается в типизированный объект Dart, с которым дальше работает весь интерфейс.
Итог: FutureBuilder — мост между сетью и интерфейсом: запрос, парсинг JSON в модель и автоматическая отрисовка состояний. Это финальный кусок пазла — теперь вы умеете построить полноценный экран с реальными данными и завершаете курс готовым к собственному приложению.