我尝试使用Flutter进行GraphQL开发(使用graphql_codegen和graphql_flutter库)

我是Never株式会社的创始人。

株式会社Never以“永不停止创造”为愿景,并持续开发产品来实现理想。我们提供移动应用的委托开发、技术支持和咨询服务。如果您需要应用开发委托或在开发方面遇到困难,请随时轻松地与我们联系。
https://neverjp.com/contact/

总之/简要来说

最近,我正在使用GraphQL,所以为了学习,我尝试使用了标题中提到的包。在众多的包中,我选择了graphql_codegen,它可以为我生成mutation和query的代码,并选择了具有缓存功能的graphql_flutter,因为我觉得它们实际上很有用。

GraphQL的API

 

这次我们使用了这个。

演示应用程序

undefined

GraphQL代码生成工具

 

    • graphql_codegen

 

    build_runner(コード生成に必要)

将“の”包添加到pubspec.yaml中.

添加schema.graphql

除了IDE的GraphiQL和Apollo Studio之外,我们这次使用的是Postman来获取模式。

GraphQL schema是Dart代码生成的必需文件。它定义了查询、变更、订阅等结构,并基于该模式来实现查询和变更,以适当的数据类型响应。换句话说,根据这个文件来解释在.graphql中定义的查询,并进行代码生成,如果没有它就无法生成代码。我自己也遇到了些问题。。。

type Query {
    characters(page: Int!, filter: FilterCharacter): Characters
    locations(page: Int!): Locations
    episodes(page: Int!): Episodes
}

type Character {
    id: ID!
    name: String
    image: String
}

type Location {
    id: ID!
    name: String
    type: String
}

type Episode {
    id: ID!
    name: String
    episode: String
}

type Characters {
    results: [Character]
}

type Locations {
    results: [Location]
}

type Episodes {
    results: [Episode]
}

input FilterCharacter {
    name: String
}

请添加 queries.graphql

我們將提供取得數據所需的相關信息。

query GetCharacters($page: Int!, $name: String!) {
    characters(page: $page, filter: { name: $name }) {
        results {
            id
            name
            image
        }
    }
}

query GetLocations {
    locations(page: 1) {
        results {
            id
            name
            type
        }
    }
}

query GetEpisodes {
    episodes(page: 1) {
        results {
            id
            name
            episode
        }
    }
}

自动生成的设置

因为要使用 graphql_codegen 进行自动代码生成,所以需要进行相应的设置。

在build.yaml文件中添加以下内容。

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

我正在添加 graphql_flutter 来使用。

lib/graphql/**
lib/graphql/    ←コード生成されず

由于没有生成代码而遇到了困境,**是必需的!

使用build_runner进行代码生成。

dart run build_runner build

使用上述命令来生成代码。

代替テキスト

如上所述,将生成文件。

由于比较之后更容易理解,所以是否存在代码生成.

如果不使用codegen

const String getCharacters = r'''
query GetCharacters($page: Int!, $name: String!) {
  characters(page: $page, filter: { name: $name }) {
    results {
      id
      name
      image
    }
  }
}
''';

const String getLocations = r'''
query {
  locations(page: 1) {
    results {
      id
      name
      type
    }
  }
}
''';

const String getEpisodes = r'''
query {
  episodes(page: 1) {
    results {
      id
      name
      episode
    }
  }
}
''';

这样做,必须硬编码下去。
实际上,我也有因打错字而遇到错误而苦恼的经历,所以我想减少这种风险。

如果使用codegen

query GetCharacters($page: Int!, $name: String!) {
    characters(page: $page, filter: { name: $name }) {
        results {
            id
            name
            image
        }
    }
}

query GetLocations {
    locations(page: 1) {
        results {
            id
            name
            type
        }
    }
}

query GetEpisodes {
    episodes(page: 1) {
        results {
            id
            name
            episode
        }
    }
}

这样写也可以,在代码编辑中可以整理代码,并在代码生成阶段检测到错误。

GraphQL_Flutter

 

    • graphql_codegen

 

    flutter_hooks

在pubspec.yaml中添加软件包的依赖
使用flutter_hooks来进行状态管理

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

  @override
  Widget build(BuildContext context) {
    final HttpLink httpLink = HttpLink('https://rickandmortyapi.com/graphql');

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

    return GraphQLProvider(
      client: client,
      child: const MaterialApp(
        home: CharacterList(),
      ),
    );
  }
}

在缓存层面上,使用Hive将数据保存到本地数据库似乎也是一个不错的选择。

列表显示

Query$GetCharacters$Widget(
  options: Options$Query$GetCharacters(
    variables: Variables$Query$GetCharacters(
      page: index.value,
      name: searchText.value,
    ),
  ),
  builder: (
    QueryResult result, {
    VoidCallback? refetch,
    FetchMore? fetchMore,
  }) {
    if (result.isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    if (result.hasException) {
      return Text(result.exception.toString());
    }
    final data = result.data;
    if (data == null) {
      return const Text('データがありません');
    }

    final charactersResponse =
        CharactersResponse.fromJson(data['characters']);
    final characters = charactersResponse.results;

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

        return ListTile(
          title: Text(character.name ?? ''),
          leading: Image.network(character.image ?? ''),
        );
      },
    );
  },
),

运用codegen生成的代码可以如下所示进行编写。

Widget$Query$GetCharacters:这是通过codegen生成的小部件,用于显示GetCharacters查询的结果的小部件。

options: Options$Query$GetCharacters(
    variables: Variables$Query$GetCharacters(
      page: index.value,
      name: searchText.value,
    ),
),

设置执行查询的选项。
Variables$Query$GetCharacters:指定了查询所需的变量(在本例中为页面数字和角色名称)。

在mutation的情况下,需要在mutaions.graphql文件中生成一个mutation,并将Query部分改为Mutation。

代码整段

class CharacterList extends HookWidget {
  const CharacterList({super.key});

  @override
  Widget build(BuildContext context) {
    final searchText = useState<String>('');
    final index = useState<int>(1);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Rick and Morty Characters'),
      ),
      body: Column(
        children: [
          Row(
            children: [
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: TextFormField(
                    decoration: const InputDecoration(
                      labelText: 'Search',
                      border: OutlineInputBorder(),
                    ),
                    onChanged: (value) {
                      searchText.value = value;
                    },
                  ),
                ),
              ),
              IconButton(
                icon: const Icon(Icons.arrow_back),
                onPressed: () {
                  if (index.value > 1) {
                    index.value--;
                  }
                },
              ),
              IconButton(
                icon: const Icon(Icons.arrow_forward),
                onPressed: () => index.value++,
              ),
            ],
          ),
          Expanded(
            child: Query$GetCharacters$Widget(
              options: Options$Query$GetCharacters(
                variables: Variables$Query$GetCharacters(
                  page: index.value,
                  name: searchText.value,
                ),
              ),
              builder: (
                QueryResult result, {
                VoidCallback? refetch,
                FetchMore? fetchMore,
              }) {
                if (result.isLoading) {
                  return const Center(child: CircularProgressIndicator());
                }

                if (result.hasException) {
                  return Text(result.exception.toString());
                }
                final data = result.data;
                if (data == null) {
                  return const Text('データがありません');
                }

                final charactersResponse =
                    CharactersResponse.fromJson(data['characters']);
                final characters = charactersResponse.results;

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

                    return ListTile(
                      title: Text(character.name ?? ''),
                      leading: Image.network(character.image ?? ''),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

结束

我认为graphql_codegen和graphql_flutter都是很好的软件包。

查询和突变都是由graphql_codegen自动生成的,并且如果小部件中的状态是完全内聚的,我会使用hooks和graphql_flutter,如果其他屏幕也需要状态管理,我会使用Riverpod。我觉得对于每个角色,在我内心里都很清楚。
我还想尝试使用和比较其他GraphQL的包。

非常感谢!

bannerAds