Получение данных из Интернета необходимо для большинства приложений. К счастью, 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(); }, ), ), ), ); } }