Получение данных с помощью GraphQL в Flutter
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

Гибкость Flutter в обработке данных делает его отличным выбором для современной разработки приложений. Хотя большинство разработчиков знакомы с REST API, GraphQL предлагает мощную альтернативу, которая может значительно улучшить производительность и опыт разработки вашего приложения.
Гибкость Flutter в обработке данных делает его отличным выбором для современной разработки приложений. Хотя большинство разработчиков знакомы с REST API, GraphQL предлагает мощную альтернативу, которая может значительно улучшить производительность и опыт разработки вашего приложения.
Независимо от того, разрабатываете ли вы простое приложение или сложное корпоративное решение, выбор правильной стратегии получения данных критически важен для производительности и поддержки.
Не являетесь участником? Прочитайте это здесь.
Получение данных: два основных подхода
Когда речь идет о получении данных в Flutter, у вас есть два основных варианта:
REST API: традиционный подход с использованием нескольких конечных точек для различных требований к данным. Каждая конечная точка возвращает фиксированную структуру данных, часто приводя к избыточному или недостаточному получению данных.
GraphQL: язык запросов, который позволяет запрашивать ровно те данные, которые вам нужны, из одной конечной точки. Вместо множества вызовов REST вы пишете один запрос, указывая свои точные требования.
В этом руководстве мы сосредоточимся на GraphQL и продемонстрируем, почему он может стать предпочтительным выбором для многих разработчиков.
Понимание GraphQL
GraphQL — это язык запросов и среда выполнения для API, которые дают клиентам возможность запрашивать ровно то, что им нужно. В отличие от REST, где вы ограничены структурой данных, которую предоставляет конечная точка, GraphQL позволяет вам указать ровно те поля, которые вы хотите.
Вместо вызова нескольких конечных точек REST, таких как:
/api/users/1/api/users/1/posts/api/users/1/profile
Вы можете сделать один запрос GraphQL:
query {
user(id: 1) {
name
posts {
title
}
profile {
avatar
}
}
}Преимущества GraphQL перед REST
1. Точное получение данных
С GraphQL вы запрашиваете ровно то, что вам нужно. Больше не нужно получать целые объекты пользователей, когда вам нужны только имя и email. Это снижает использование полосы пропускания и улучшает производительность приложения, особенно на мобильных сетях.
2. Независимость фронтенда
Нужно добавить новое поле в ваш интерфейс? С REST вам, возможно, придется создать новую конечную точку или изменить существующие. С GraphQL вы просто обновляете свой запрос. Это исключает необходимость изменений на стороне сервера для многих требований фронтенда, что приводит к более быстрым циклам разработки.
3. Один запрос к сети
Сложные интерфейсы часто требуют данных из нескольких источников. REST может потребовать нескольких вызовов API, в то время как GraphQL может получить все необходимые данные в одном запросе, снижая нагрузку на сеть.
Что мы будем создавать
Мы создадим приложение на Flutter, которое демонстрирует мощь GraphQL на практике:

Функции приложения:
- Список персонажей: Получение персонажей Рика и Морти с помощью запросов GraphQL
- Кэширование ответов: Кэширование ответов GraphQL для улучшения производительности
- Бесконечная пагинация: Загрузка большего количества персонажей при прокрутке
- Кэширование изображений: Кэширование изображений персонажей для уменьшения количества сетевых вызовов
- Состояния загрузки: Отображение индикаторов прогресса для вызовов API и загрузки изображений
- Обработка ошибок: Корректная обработка сбоев сети
Для целей разработкимы пропустим проверку сертификатов для эмулятора/разрабатываемой среды
Эта реализация демонстрирует эффективность GraphQL — мы получаем ровно те данные, которые нам нужны (имя, изображение, вид, происхождение) в одном запросе, а не несколькими вызовами REST.
Демонстрационный API: Rick and Morty GraphQL
Для этого руководства мы будем использовать GraphQL API Rick and Morty, который предоставляет идеальную площадку для изучения концепций GraphQL.
Конечная точка API: https://rickandmortyapi.com/graphql
Этот API включает в себя GraphQL playground, где можно тестировать запросы интерактивно. Плейграунд показывает вам схему, доступные запросы и позволяет экспериментировать с различными запросами данных перед их реализацией в вашем приложении Flutter.

Требуемые зависимости
Добавьте эти пакеты в ваш pubspec.yaml:
graphql: ^5.2.1
cached_network_image: ^3.4.1
http: ^1.5.0
Назначение пакетов:
graphql: Предоставляет клиент GraphQL и функциональность запросовcached_network_image: Обрабатывает кэширование изображений и загрузку с поддержкой заполнителейhttp: HTTP-клиент для сетевых запросов (требуется пакетом GraphQL)
Руководство по реализации
Шаг 1: Настройка окружения разработки
Сначала настроим перехват HTTP-запросов для обработки самоподписанных сертификатов в средах разработки:
import 'package:flutter/material.dart';
import 'package:graphql/client.dart';
import 'package:http/io_client.dart';
import 'dart:io';
void main() {
HttpOverrides.global = MyHttpOverrides();
runApp(const MyApp());
}
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
}
}
Эта настройка особенно полезна при работе с эмуляторами или средами разработки, которые могут иметь проблемы с сертификатами.
Шаг 2: Настройка GraphQL клиента
Далее настроим GraphQL клиент:
final httpLink = HttpLink(
'https://rickandmortyapi.com/graphql',
httpClient: httpClient,
);
// Инициализация GraphQL клиента с кэшированием
final client = GraphQLClient(
cache: GraphQLCache(),
link: httpLink,
);
Конструктор GraphQLCache() автоматически обрабатывает кэширование ответов, улучшая производительность за счёт избежания избыточных сетевых запросов.
Шаг 3: Реализация GraphQL запроса
Теперь реализуем основную логику GraphQL запроса и логику пагинации:
class _ItemListScreenState extends State<ItemListScreen> {
final ScrollController _scrollController = ScrollController();
List<Map<String, dynamic>> items = [];
bool isLoading = false;
bool hasMore = true;
int currentPage = 1;
final int pageSize = 20;
Future<void> _fetchItems() async {
setState(() => isLoading = true);
// Определяем GraphQL запрос
final query = gql("""query {
characters(page: $currentPage, filter: { name: "rick" }) {
info {
count
}
results {
name
image
species
origin {
name
}
}
}
}""");
// Выполняем запрос
final result = await widget.client.query(
QueryOptions(document: query),
);
// Обработка ошибок
if (result.hasException) {
debugPrint(result.exception.toString());
setState(() => isLoading = false);
return;
}
// Обработка результатов
final fetched = List<Map<String, dynamic>>.from(
result.data?['characters']['results'] ?? [],
);
setState(() {
currentPage++;
items.addAll(fetched);
isLoading = false;
if (fetched.length < pageSize) {
hasMore = false;
}
});
}
}Обратите внимание, как GraphQL запрос указывает точно те данные, которые нам нужны: name, image, species, и origin.name. Мы не загружаем лишние данные, что делает наше приложение более эффективным.
Шаг 4: Реализация бесконечной прокрутки
Добавим пагинацию через обнаружение прокрутки:
@override
void initState() {
super.initState();
_fetchItems();
// Слушаем события прокрутки
_scrollController.addListener(() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 200 &&
!isLoading &&
hasMore) {
_fetchItems(); // Загружаем следующую страницу
}
});
}
Это создаёт плавный эффект бесконечной прокрутки, загружая новые данные, когда пользователь приближается к низу списка.
Шаг 5: Реализация интерфейса с кэшированными изображениями
Наконец, создадим компоненты интерфейса:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"GraphQL Пример",
style: TextStyle(color: Colors.white)
),
backgroundColor: Theme.of(context).primaryColor
),
body: ListView.builder(
controller: _scrollController,
itemCount: items.length + (hasMore ? 1 : 0),
itemBuilder: (context, index) {
// Показываем индикатор загрузки внизу
if (index >= items.length) {
return const Padding(
padding: EdgeInsets.all(16.0),
child: Center(child: CircularProgressIndicator()),
);
}
final item = items[index];
return ItemTile(item: item);
},
),
);
}
И пользовательский элемент списка с кэшированными изображениями:
class ItemTile extends StatelessWidget {
final Map<String, dynamic> item;
const ItemTile({super.key, required this.item});
@override
Widget build(BuildContext context) {
return ListTile(
leading: ClipRRect(
borderRadius: BorderRadius.circular(36),
child: CachedNetworkImage(
imageUrl: item['image'],
width: 60,
height: 60,
fit: BoxFit.cover,
placeholder: (context, url) => const SizedBox(
width: 60,
height: 60,
child: Center(child: CircularProgressIndicator()),
),
errorWidget: (context, url, error) => const Icon(Icons.error),
),
),
title: Text(
item['name'] ?? '',
style: Theme.of(context).textTheme.titleMedium,
),
subtitle: Text(
item['origin']['name'] ?? '',
style: Theme.of(context).textTheme.bodyMedium,
),
trailing: Badge(
label: Text(item['species'] ?? ''),
backgroundColor: item['species'] == 'Human'
? Colors.blue
: Colors.red.shade800,
),
);
}
}The CachedNetworkImage widget automatically handles image caching, placeholder display during loading, and error states.
Демонстрация приложения

Ограничения GraphQL, которые следует учитывать
Несмотря на значительные преимущества, важно понять, почему GraphQL не используется повсеместно:
1. Сложность обслуживания
GraphQL требует более глубокого понимания оптимизации запросов и проектирования схемы. Плохо написанные запросы могут влиять на производительность, и разработчикам необходимо изучить специфичные для GraphQL концепции, такие как ограничение глубины запросов и анализ сложности.
2. Кривая обучения
Переход с REST на GraphQL требует изменения мышления. Разработчикам необходимо думать в терминах графов и связей, а не простых паттернов запроса-ответа.
3. Сложность кэширования
Несмотря на мощные возможности кэширования, реализация эффективных стратегий кэширования может быть сложнее, чем кэширование традиционных REST-эндпоинтов.
Несмотря на эти соображения, преимущества GraphQL часто перевешивают кривую обучения, особенно для сложных приложений с разнообразными требованиями к данным.
Ссылка на GitHub
https://github.com/qureshiayaz29/GraphQL-sample
Заключение
GraphQL предоставляет мощный и эффективный способ получения данных в приложениях на Flutter. На примере списка персонажей Rick and Morty мы продемонстрировали, как:
- Настроить клиент GraphQL с кэшированием
- Написать эффективные запросы, которые получают только необходимые данные
- Реализовать бесконечную прокрутку с пагинацией
- Обработать состояния загрузки и ошибки
- Кэшировать изображения для улучшения производительности
Основные преимущества — точное извлечение данных, снижение накладных расходов сети и независимость фронтенда — делают GraphQL особенно ценным для сложных приложений на Flutter с разнообразными требованиями к данным.
Несмотря на кривую обучения, инвестиции окупаются за счет улучшенной производительности приложений и более эффективных рабочих процессов разработки. Рассмотрите возможность использования GraphQL, когда ваше приложение требует гибкого извлечения данных и вы хотите минимизировать ненужный трафик сети.
Начните с простых запросов, как показано здесь, и постепенно исследуйте более сложные функции GraphQL по мере роста сложности вашего приложения.