Изучение HTTP-запросов во Flutter
Мне не терпится поделиться с вами информацией о мире HTTP-запросов во Flutter и о том, какую важную роль они играют в разработке мобильных приложений. Прежде чем перейти к практическим примерам, я хотел бы обратить ваше внимание на несколько ресурсов, которые могут дополнить и расширить ваше понимание этой темы.
Я создал видеоролик на YouTube, посвященный именно той теме, которую мы сейчас рассмотрим. В нем я демонстрирую выполнение HTTP-запросов в среде Flutter, предоставляя визуальное руководство, которое может улучшить ваше понимание. Видео можно найти здесь. (Настоятельно рекомендуется!)
Кроме того, я создал репозиторий на GitHub, где вы можете найти соответствующие фрагменты кода, дополнительные материалы и ресурсы, связанные с примерами, которые мы будем обсуждать. Репозиторий GitHub доступен здесь.
Давайте вместе отправимся в это путешествие, открывая для себя мощь и многогранность HTTP-запросов в Flutter и способы их использования для динамического взаимодействия с данными в мобильных приложениях.
Использование пакета http
Пакет http
в Flutter упрощает процесс выполнения HTTP-запросов и обработки ответов. Рассмотрим два примера кодов, демонстрирующих запросы GET и POST.
Пример кода 1: Выполнение POST-запроса
Следующий код демонстрирует отправку данных на сервер с помощью POST-запроса:
import 'dart:async';
import 'dart:convert';
import 'package:apitutorial/home.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> createAlbum(String title) async {
final response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/albums'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'title': title,
}),
);
if (response.statusCode == 201) {
// If the server did return a 201 CREATED response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
// If the server did not return a 201 CREATED response,
// then throw an exception.
throw Exception('Failed to create album.');
}
}
class Album {
final int id;
final String title;
const Album({required this.id, required this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
id: json['id'] as int,
title: json['title'] as String,
);
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
final TextEditingController _controller = TextEditingController();
Future<Album>? _futureAlbum;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Create Data Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: Scaffold(
appBar: AppBar(
title: const Text('Create Data Example'),
),
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(8),
child: (_futureAlbum == null) ? buildColumn() : buildFutureBuilder(),
),
),
);
}
Column buildColumn() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: _controller,
decoration: const InputDecoration(hintText: 'Enter Title'),
),
ElevatedButton(
onPressed: () {
setState(() {
_futureAlbum = createAlbum(_controller.text);
});
},
child: const Text('Create Data'),
),
],
);
}
FutureBuilder<Album> buildFutureBuilder() {
return FutureBuilder<Album>(
future: _futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
);
}
}
Этот код представляет собой простое приложение Flutter, демонстрирующее создание альбома путем отправки POST-запроса на имитируемую конечную точку API с использованием пакета http в Dart. Ниже приведена разбивка основных компонентов:
Объявления типа import
- Импорт необходимых пакетов Dart и Flutter, таких как
async
,http
,material
иjson
.
Класс Album
- Класс
Album
представляет собой альбом с идентификатором (id
) и названием (title
). Он содержит конструктор и фабричный методfromJson
для преобразования JSON-данных в объектAlbum
.
Функция createAlbum
createAlbum
- это асинхронная функция, использующая методhttp.post
для отправки POST-запроса к указанной конечной точке API (https://jsonplaceholder.typicode.com/albums).- В теле запроса она отправляет полезную нагрузку в формате JSON, содержащую название (
title
) альбома. - В случае успешного выполнения запроса (возвращается код состояния 201 - Created) выполняется разбор JSON-файла ответа и создание объекта
Album
с помощью фабричного методаfromJson
. - Если запрос не выполняется или возвращает другой код состояния, то возникает исключение, указывающее на невозможность создания альбома.
Функция main
- Функция
main
устанавливает приложение Flutter, запуская виджетMyApp
.
Класс MyApp
MyApp
- это виджет с состоянием, определяющий корень приложения.
Класс _MyAppState
_MyAppState
- это состояние, связанное сMyApp
и содержащее текстовый контроллер для поля ввода и объектFuture<Album>
для обработки асинхронного создания альбома.
Метод build
- Метод
build
устанавливает пользовательский интерфейс приложения. - В нем настраивается тема приложения и определяется
Scaffold
сAppBar
и тело. - Тело содержит контейнер с виджетом столбца, в котором находится текстовое поле для ввода названия альбома и кнопка, запускающая создание альбома.
Метод buildColumn
buildColumn
возвращает колонку, содержащую текстовое поле и кнопку.- Кнопка запускает создание альбома, вызывая при нажатии функцию
createAlbum
.
Метод buildFutureBuilder
buildFutureBuilder
возвращает виджетFutureBuilder
. Он отображает различные элементы пользовательского интерфейса в зависимости от состояния асинхронной операции (_futureAlbum
).- Если операция завершена и прошла успешно, то на экране отображается название созданного альбома.
- Если в процессе работы возникла ошибка, то на экран выводится сообщение об ошибке.
- Во время выполнения операции на экране отображается круговой индикатор хода выполнения.
Приложение позволяет пользователям ввести название альбома, создать его с помощью POST-запроса и вывести результат или сообщение об ошибке, используя FutureBuilder
от Flutter.
Данный код демонстрирует функцию createAlbum
, которая отправляет POST-запрос на сервер. Он включает класс Album
, представляющий структуру альбома, и пользовательский интерфейс, настроенный в классе MyApp
, позволяющий пользователям вводить название альбома и создавать его.
Пример кода 2: Выполнение GET-запроса
Рассмотрим код, который получает данные с сервера с помощью GET-запроса:
import 'package:apitutorial/model/response/list_of_response.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:cached_network_image/cached_network_image.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<ListOfData> _list = [];
Future<List<ListOfData>> getAllData() async {
try {
final response =
await http.get(Uri.parse('https://fakestoreapi.com/products'));
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
_list = data.map<ListOfData>((e) => ListOfData.fromJson(e)).toList();
debugPrint('${_list.length}');
return _list;
} else {
debugPrint(
'Error in API call Please check your backend and URL carefully');
}
return _list;
} catch (e) {
debugPrint('$e');
}
return _list;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: getAllData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: _list.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text('${_list[index].title}'),
subtitle: Text('${_list[index].description}'),
leading: SizedBox(
height: 50,
width: 50,
child: CachedNetworkImage(
imageUrl: '${_list[index].image}',
progressIndicatorBuilder: (context, url, progress) =>
CircularProgressIndicator(
value: progress.progress),
),
),
trailing: Text('${_list[index].price}'),
);
});
} else if (snapshot.hasError) {
return const Text("Error");
}
return const Text("No Data");
}),
);
}
}
Этот код в Flutter представляет собой StatefulWidget
с именем HomeScreen
, который получает данные из указанной конечной точки API и отображает информацию в виде ListView
.
Ниже приводится описание кода:
Объявления типа import
- Включены различные импорты пакетов, такие как
material
из Flutter,http
для выполнения HTTP-запросов,cached_network_image
для эффективной загрузки и кэширования сетевых изображений,json
для кодирования и декодирования JSON-данных.
Класс HomeScreen
HomeScreen
- этоStatefulWidget
, представляющий главный экран приложения.
Класс _HomeScreenState
_HomeScreenState
управляет состояниемHomeScreen
.
Переменные состояния
_list
- это список объектовListOfData
.
Метод getAllData
getAllData
- это асинхронная функция, которая выполняет HTTP GET-запрос к https://fakestoreapi.com/products для получения данных.- Если статус ответа равен 200 (OK), то JSON-ответ декодируется и используется для заполнения
_list
путем отображения JSON-данных на объектыListOfData
(предположительно определенные вlist_of_response.dart
). - Отладочные операторы печати используются для отображения количества найденных элементов или ошибок, возникших при вызове API.
Метод build
- Метод
build
настраивает пользовательский интерфейс дляHomeScreen
. - Он отображает
Scaffold
, содержащийFutureBuilder
, который ожидает результатаgetAllData
. - При получении данных он отображает виджет
ListView.builder
, заполняя список данными, полученными из API.
Каждый элемент списка представлен ListTile
, содержащим текстовые поля для названия, описания и цены. В него также входит виджет CachedNetworkImage
для загрузки и отображения изображения товара.
FutureBuilder
отвечает за отображение различных компонентов пользовательского интерфейса в зависимости от текущего состояния асинхронной операции:
- Если данные доступны, то выводится список элементов, полученных из API.
- Если при вызове API произошла ошибка, то выдается сообщение об ошибке.
- Если данные отсутствуют, то выводится сообщение об отсутствии данных.
Этот код предоставляет базовую структуру для получения данных из API и отображения их в виде списка, включая изображения, загружаемые из сетевых URL с помощью пакета cached_network_image
для обеспечения эффективного кэширования и загрузки изображений в приложении Flutter.
Заключение
Оба примера иллюстрируют практическую реализацию выполнения HTTP-запросов во Flutter с использованием пакета http
. Первый код посвящен созданию данных с помощью POST-запроса, а второй - получению и отображению данных с помощью GET-запроса.
Спасибо за прочтение! Счастливого кодинга!