📝

graphql_flutter + graphql_codegenを使用したFlutterでのGraphQL

2022/07/06に公開

はじめに

FlutterでGraphQLを使用しようと調査するなかで、graphql_flutter + graphql_codegenの組み合わせが使いやすそうでした。
使いやすそうと思った点は、GraphQLのスキーマからgraphql_codegenによるコードの自動生成です。
queryやmutationを記載し、それをもとにgraphql_flutterのコードも自動生成され、データの構造や型もわかります。

サンプルコードを用意したので、それをもとに紹介したいと思います。

サンプルコード

サンプルコードはこちらです。
https://github.com/greendrop/flutter_graphql_pokemon

GraphQLサーバーはこちらを使用させていただきました。
https://github.com/lucasbento/graphql-pokemon

画面イメージ

環境

  • Flutter: 3.0.4
  • graphql_flutter: 5.1.0
  • graphql_codegen: 0.9.0
  • build_runner: 2.1.11
  • flutter_hooks: 0.18.5+1

サンプルコードの内容

GraphQLのスキーマからの自動生成

まずは、GraphQLのスキーマからコードを自動生成します。

SDL取得

https://graphql-pokemon2.vercel.app からSDLを取得します。
取得方法はいろいろありますが、私はChrome拡張のAltair GraphQL Clientを使用しました。

以下のパスに保存しています。
lib/graphql/schema.graphql

Query記載

GraphQLよりデータを取得するため、Queryを記載します。

lib/graphql/pokemons.graphql

query Pokemons($first: Int!) {
  pokemons(first: $first) {
    id
    number
    name
    image
  }
}

自動生成の設定

graphql_codegenはbuild_runnerによる自動生成のため、そちらの設定をしていきます。

build.yaml

targets:
  $default:
    builders:
      graphql_codegen:
        options:
          scopes:
            - lib/graphql/**
          clients:
            - graphql
            - graphql_flutter

build_runner実行し、コードの自動生成

build_runnerを実行すると、以下のファイルが作成されます。

flutter pub run build_runner build
  • lib/graphql/schema.graphql.dart
  • lib/graphql/pokemons.graphql.dart
  • lib/graphql/pokemons.graphql.g.dart

GraphQLクライアントの作成

SDL, Queryからコードが自動生成されたので、次にgraphql_flutterを使用してGraphQLのクライアントを作成します。

Hiveの初期化

GraphQLのクライアントのキャッシュにHiveを使用しているので、そちらの初期化を行います。

lib/main.dart


// Flutter imports:
import 'package:flutter/material.dart';

// Package imports:
import 'package:graphql_flutter/graphql_flutter.dart';

// Project imports:
import 'package:flutter_graphql_pokemon/app_root.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initHiveForFlutter();

  runApp(const AppRoot());
}

GraphQLのクライアントを作成

GraphQLのクライアントを作成します。

lib/app_root.dart

// Flutter imports:
import 'package:flutter/material.dart';

// Package imports:
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

// Project imports:
import 'package:flutter_graphql_pokemon/ui/main/main_page.dart';

class AppRoot extends HookConsumerWidget {
  const AppRoot({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final link = HttpLink('https://graphql-pokemon2.vercel.app');

    final client = ValueNotifier<GraphQLClient>(
      GraphQLClient(
        link: link,
        cache: GraphQLCache(store: HiveStore()),
      ),
    );

    return GraphQLProvider(
      client: client,
      child: const MaterialApp(title: 'GraphQL Pokémon', home: MainPage()),
    );
  }
}

Queryから一覧を表示

useQuery$Pokemonsが自動生成されたQueryのメソッドになります。
取得したデータ構造や型も定義されたものが取得できています。

lib/ui/main/main_page.dart

// Flutter imports:
import 'package:flutter/material.dart';

// Package imports:
import 'package:hooks_riverpod/hooks_riverpod.dart';

// Project imports:
import 'package:flutter_graphql_pokemon/graphql/pokemons.graphql.dart';

class MainPage extends HookConsumerWidget {
  const MainPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final appBar = AppBar(
      title: const Text('GraphQL Pokémon'),
    );
    final queryResult = useQuery$Pokemons(
      Options$Query$Pokemons(
        variables: Variables$Query$Pokemons(first: 200),
      ),
    );
    final result = queryResult.result;

    Widget bodyChild;
    if (result.isLoading) {
      bodyChild = const Center(child: CircularProgressIndicator());
    } else if (result.hasException) {
      bodyChild = Text(result.exception.toString());
    } else {
      final pokemons = result.parsedData?.pokemons ?? [];
      bodyChild = ListView.builder(
        itemCount: pokemons.length,
        itemBuilder: (context, index) {
          final pokemon = pokemons[index];
          return ListTile(
            leading: Image.network(pokemon?.image ?? ''),
            title: Text(pokemon?.name ?? ''),
          );
        },
      );
    }

    final body = RefreshIndicator(
      onRefresh: queryResult.refetch,
      child: bodyChild,
    );

    return Scaffold(
      appBar: appBar,
      body: body,
    );
  }
}

まとめ

graphql_flutter + graphql_codegenによるFlutterでのGraphQLはいかがだったでしょうか?
FlutterでGraphQLを使う場合、artemisferryがあると思いますが、graphql_flutter + graphql_codegenも選択肢に加えてみてはどうでしょう。

Discussion