Flutter: Ferry を使った GraphQL 簡易アプリの作成

## プロジェクト作成
公式ドキュメントにそってセットアップ

https://ferrygraphql.com/docs/setup/

```sh
ghq create gqlflutter
flutter create .
```

## パッケージインストール

```sh
flutter pub add gql_http_link
flutter pub add ferry_flutter # 追加
flutter pub add -d ferry_generator
flutter pub add -d build_runner
```

## スキーマファイルをダウンロード

参考
https://ferrygraphql.com/docs/codegen

### スキーマダウンロードするツールをインストール

```sh
npm install -g get-graphql-schema
```

### スキーマをダウンロード

今回はポケモンのGraphQL API https://graphql-pokemon2.vercel.app を利用させてもらいました。ありがとうございます。

```sh
get-graphql-schema https://graphql-pokemon2.vercel.app > lib/schema.graphql
```

## コードを書く

### GraphQL クエリーの用意

ポケモンデータを取得するクエリを書く

```graphql
# lib/graphql/all_pokemon.graphql
query AllPokemon($first: Int!) {
  pokemons(first: $first) {
    id
    name
    maxHP
    image
  }
}
```

### build.yaml を作成

こちらの yaml をプロジェクトルートに保存

https://ferrygraphql.com/docs/codegen#build-generated-classes

```yaml
# build.yaml
targets:
  $default:
    builders:
      gql_build|schema_builder:
        enabled: true
      gql_build|ast_builder:
        enabled: true
      gql_build|data_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql
      gql_build|var_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql
      gql_build|serializer_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql

      ferry_generator|req_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql
```

### GraphQL クエリから Dart コードを生成

```sh
flutter pub run build_runner build
```

lib/graphql は下記のようになる

```
all_pokemon.data.gql.dart
all_pokemon.data.gql.g.dart
all_pokemon.graphql
all_pokemon.req.gql.dart
all_pokemon.var.gql.dart
```

### GraphQL client の準備

GraphQL Client を取得できるメソッドを準備

```dart
class MyHomePage extends StatefulWidget {
  // ...
  
  Client? _client;  
  
  Client get client {
    if (_client == null) {
      final link = HttpLink('https://graphql-pokemon2.vercel.app');
      return _client = Client(link: link);
    } else {
      return _client!; // NOTE: ! 消せないの?
    }
  }
  // ...
}
```

### ポケモンをリスト表示

ferry には `ferry_flutter` パッケージで flutter で使いやすいように Widget が用意されています。

Operation widget を使うと良い感じにデータ取得ができます。

```dart
class MyHomePage extends StatefulWidget {
  // ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Operation(
        client: client,
        operationRequest: GAllPokemonReq((b) => b..vars.first = 151),
        builder: (BuildContext context,
            OperationResponse<GAllPokemonData, GAllPokemonVars>? response,
            Object? error) {
          if (response == null || response.loading) {
            return const Center(child: CircularProgressIndicator());
          }

          final pokemons = response.data?.pokemons ?? BuiltList();

          return ListView.builder(
              itemCount: pokemons.length,
              itemBuilder: (context, index) {
                final pokemon = pokemons[index];

                return Card(
                  child: Row(children: <Widget>[
                    SizedBox(
                      child: Image.network(
                          pokemon.image ?? 'https://placehold.jp/150x150.png'),
                      width: 100,
                    ),
                    Padding(
                      padding: const EdgeInsets.only(left: 20),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text('Name: ${pokemon.name ?? '[unknown]'}'),
                          Text(
                            'MAX HP: ${pokemon.maxHP}',
                            textAlign: TextAlign.left,
                          ),
                        ],
                      ),
                    )
                  ]),
                );
              });
        },
      ),
    );

```

## 完成

こんな感じでポケモンの一覧が表示されれば完成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10012/43c1e80a-6a5f-c0f6-f272-87666aef7596.png" width="300px" />