DevGang
Авторизоваться

Изучение 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-запроса.

Спасибо за прочтение! Счастливого кодинга!

Источник:

#Flutter
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу