Получение данных из Интернета необходимо для большинства приложений. К счастью, Dart и Flutter предоставляют инструменты, такие как пакет http, для этого типа работы.
Для этого нужно:
- Добавьте пакет http.
- Сделайте сетевой запрос, используя пакет http.
- Преобразуйте ответ в пользовательский объект Dart.
- Получить и отобразить данные с помощью Flutter.
Добавьте пакет http
Пакет http обеспечивает самый простой способ извлечения данных из Интернета.
Чтобы установить пакет http, добавьте его в раздел зависимостей файла pubspec.yaml. Вы можете найти последнюю версию http пакета pub.dev.
dependencies: http: <latest_version>
Импортируйте пакет http.
import 'package:http/http.dart' as http;
Сделать сетевой запрос
Здесь показано, как извлечь образец альбома из JSONPlaceholder с помощью метода http.get ().
Future<http.Response> fetchAlbum() {
return http.get('https://jsonplaceholder.typicode.com/albums/1');
}Метод http.get () возвращает Future, содержащий Response.
- Future – основной класс Dart для работы с асинхронными операциями. Объект Futureпредставляет потенциальное значение или ошибку, которая будет доступна в будущем.
- Класс http.Response содержит данные, полученные при успешном вызове http.
Конвертировать ответ в пользовательский объект Dart
Хотя сделать сетевой запрос легко, работа с необработанным Future<http.Response> не очень удобна. Чтобы сделать вашу жизнь проще, преобразуйте http.Response в объект Dart.
Создать класс Album
Сначала создайте класс Album, который содержит данные из сетевого запроса. Он включает конструктор, который создает Album из JSON.
Преобразование JSON вручную – единственный вариант.
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}Преобразовать http.Response в Album
Теперь используйте следующие шаги, чтобы обновить функцию fetchAlbum () и вернуть <Future<Album>:
- Преобразуйте тело ответа в JSON Map с помощью пакета dart: convert.
- Если сервер возвращает ответ OK с кодом состояния 200, преобразуйте JSON Map в Album, используя метод fromJson ().
- Если сервер не возвращает ответ OK с кодом состояния 200, выдается исключение. (Даже в случае ответа сервера «404 Не найдено» выведите исключение. Не возвращайте null.)
Future<Album> fetchAlbum() async {
final response = await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(json.decode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}Теперь у вас есть функция, которая загружает альбом из Интернета.
Получить данные
Вызовите метод fetch () в методах initState () или didChangeDependencies ().
Метод initState () вызывается ровно один раз, и никогда больше. Если вы хотите иметь возможность перезагрузить API в ответ на изменение InheritedWidget, поместите вызов в метод didChangeDependencies().
class _MyAppState extends State<MyApp> {
Future<Album> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
Future используется на следующем этапе.Показать данные
Для отображения данных на экране используйте виджет FutureBuilder. Виджет FutureBuilder идет с Flutter и упрощает работу с асинхронными источниками данных.
Вы должны предоставить два параметра:
- Future, с которым вы хотите работать. В этом случае будущее возвращается из функции fetchAlbum ().
- Функция builder, которая сообщает Flutter, что показывать, в зависимости от состояния Future: загрузка, успех или ошибка.
Обратите внимание, что snapshot.hasData возвращает true только в том случае, если значение данных snapshot не null. Вот почему функция fetchAlbum должна выдавать исключение даже в случае ответа сервера «404 Not Found». Если fetchAlbum возвращает null, то индикатор загрузки отображается бесконечно.
FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
);Почему fetchAlbum() вызывается в initState()?
Хотя это удобно, не рекомендуется помещать вызов API в метод build().
Flutter вызывает метод build() каждый раз, когда ему нужно что-то изменить в представлении, и это происходит на удивление часто. Оставив вызов fetch в вашем методе build(), вы переполняете API ненужными вызовами и замедляете работу вашего приложения.
Полный пример
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(json.decode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Album> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}