我尝试使用Flutter进行GraphQL开发(使用graphql_codegen和graphql_flutter库)
我是Never株式会社的创始人。
株式会社Never以“永不停止创造”为愿景,并持续开发产品来实现理想。我们提供移动应用的委托开发、技术支持和咨询服务。如果您需要应用开发委托或在开发方面遇到困难,请随时轻松地与我们联系。
https://neverjp.com/contact/
总之/简要来说
最近,我正在使用GraphQL,所以为了学习,我尝试使用了标题中提到的包。在众多的包中,我选择了graphql_codegen,它可以为我生成mutation和query的代码,并选择了具有缓存功能的graphql_flutter,因为我觉得它们实际上很有用。
GraphQL的API
这次我们使用了这个。
演示应用程序

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的包。
非常感谢!