【Flutter】コード自動生成とGraphQLClientを使ってGraphQL・Apollo ServerのAPIにアクセス

flutter

1. 概要

上記では、GraphQLClientを使ってGraphQLよりデータを取得する内容でした。今回は、モデルクラスの自動生成をやって同じ結果となる内容となります。

対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。

2. nodeのインストール

こちらを参考

3. Back-End側のAPI開発

こちらを参考

4. DockerでMySQLを起動

5. Back-End側のサーバーを起動

6. プロジェクトの準備

6-1. プロジェクトを作成

7. 必要なライブラリをインストール

npm i graphql
npm i -D typescript @graphql-codegen/cli @graphql-codegen/flutter-freezed

8. GraphQL Code Generatorの設定ファイルを生成

8-1. codegen.ts

npx graphql-code-generator init
    Welcome to GraphQL Code Generator!
    Answer few questions and we will setup everything for you.

? What type of application are you building? Application built with other framework or vanilla JS
? Where is your schema?: (path or url) http://localhost:4000
? Pick plugins: TypeScript (required by other typescript plugins)
? Where to write the output: lib/data/models/app_models.dart
? Do you want to generate an introspection file? No
? How to name the config file? codegen.ts
? What script in package.json should run the codegen? codegen
Fetching latest versions of selected plugins...

    Config file generated at codegen.ts

      $ npm install

    To install the plugins.

      $ npm run codegen

    To run GraphQL Code Generator.
  • 上記コマンド実行で「codegen.ts」が生成される
    • 「typescript」⇒「flutter-freezed」に修正
import type { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  overwrite: true,
  schema: "http://localhost:4000",
  generates: {
    "lib/data/models/app_models.dart": {
      plugins: ["flutter-freezed"],
    },
  },
};

export default config;

9. Dependenciesの追加

9-1. graphql_flutterやfreezedを追加

flutter pub add graphql_flutter freezed_annotation json_annotation
flutter pub add --dev build_runner freezed json_serializable

10. ソースコードの自動生成

10-1. graphql-codegenを実行

npm run codegen
  • このコマンド実行で下記ファイルが生成される
    • lib/data/models/app_models.dart

10-2. build_runnerを実行

flutter pub run build_runner build delete-conflicting-outputs
  • このコマンド実行で下記2つのファイルが生成される
    • lib/data/models/app_models.freezed.dart
    • lib/data/models/app_models.g.dart

11. ソースコード

11-1. lib/data/models/*.dart

※自動生成ファイル

  • app_models.dart
  • app_models.freezed.dart
  • app_models.g.dart

11-2. lib/ui/components/client.dart

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

const String host = 'localhost';
final HttpLink httpLink = HttpLink('http://$host:4000');

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

11-3. lib/ui/views/home.dart

import 'package:flutter/material.dart';
import 'package:graphql_codegen/data/models/app_models.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    const String allUsers = r'''
      query Users($published: Boolean!) {
        users(published: $published) {
          id
          name
          email
          profile {
            id
            bio
            userId
          }
          posts {
            id
            title
            content
            authorId
            published
          }
        }
      }
      ''';

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Query(
          options: QueryOptions(
              document: gql(allUsers), variables: const {'published': false}),
          builder: (QueryResult result,
              {FetchMore? fetchMore, VoidCallback? refetch}) {
            if (result.hasException) {
              return Text('Error: ${result.exception.toString()}');
            }
            if (result.isLoading) {
              return const CircularProgressIndicator();
            }
            if (result.data == null || result.data!.isEmpty) {
              return const Text('No data');
            }
            final List<User> users = result.data!['users']
                .map<User>((user) => User.fromJson(user))
                .toList();

            return ListView.builder(
                itemCount: users.length,
                itemBuilder: (context, index) => ListTile(
                      leading: const Icon(Icons.email),
                      title: Text(users[index].name!),
                      subtitle: Text(users[index].email),
                    ));
          }),
    );
  }
}

11-4. lib\main.dart

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:graphql_codegen/ui/components/client.dart';
import 'package:graphql_codegen/ui/views/home.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GraphQLProvider(
      client: client,
      child: CacheProvider(
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const MyHomePage(title: 'Flutter - GraphQL with Codegen'),
        ),
      ),
    );
  }
}

12. 結果

13. 備考

Flutterでfreezedやgraphql-code-generatorでモデルクラスを自動生成しGraphQLClientを使ってGraphQLのAPIにアクセスし、データを取得する内容でした。

14. 参考

投稿者プロフィール

Sondon
開発好きなシステムエンジニアです。
卓球にハマってます。

関連記事

  1. flutter

    【Flutter】画面をキャプチャし画像として保存

  2. 【Widget of the Week】#4 AnimatedCont…

  3. flutter

    【Flutter】StateNotifierProviderで状態管理…

  4. flutter

    【Flutter】Pankoに「どこでもあみだくじ」機能を追加

  5. flutter

    【Flutter】Cloud Firestoreと連携

  6. flutter

    【Flutter】アプリにAdmobバナー広告を設置

最近の記事

制作実績一覧

  1. Checkeys