Flutter GraphQL Type Generation and How to Use Generated Types/Fragments in Widgets and Widget Tests
Using Artemis to generate GraphQL types in Flutter app to make passing data and widget testing easier with working example
In this post, we will cover:
- Set up GraphQL client in Flutter app
- Use Artemis to generate GraphQL types
- Use Fragments in GraphQL
- Parse custom GraphGL types to Dart types
- Save and run scripts from pubspec.yaml
- Create data from generated types for widget testing
In our example, the end result looks like this:
1. Use GraphQL in Flutter app
Package
graphql_flutter: Documentation
flutter pub add graphql_flutter
Set up
Create a widget graphql_provider.dart
as below.
Line 13: update the path to your GraphQL API endpoint.
Line 14–16: optional, only if you have authentication and need to pass in token to your GraphQL API. If you would like to know more about flutter OAuth authentication, check out my other post OAuth Authentication in Flutter App and Set Up GraphQL with Authentication Token.
Line 21–34: also optional, if you leave it out, it will use GraphQL cache. I have noCache
there because somehow it doesn’t work with fragments, which I will talk about in section 3.
Usage
In main.dart
, wrap GraphqlProvider
around your screen widgets (line 19–21).
Now you can use queries/mutation in the child widgets, check out here to find out how to do it. But we are going to do it slightly different because we are going to use Artemis.
2. Use Artemis to generate GraphQL types
This post and another one explains very well why we need each package and why we do each step, have a read if you like. Here I will just focus on what exactly needs to be done.
Packages
artemis / build_runner / json_serializable
flutter pub add artemis
flutter pub add -d build_runner json_serializable
Set up
create build.yaml
in your project root as below.
Line 5: all the queries, mutations, anything ending with .gql
will go inside your_project/graphql
folder.
Line 6: You need to get schema file that tells artemis what all the types look like from your GraphQL server, I put it in your_project/tmp/introspection.gql
.
Line 10: specify where fragment files are, you will need this if you use fragments. In my example, any file ending with .fragment.gql
in your_project/graphql
folder. Will explain in section 3.
Line 15–21: optional, only if you have custom GraphQL type that you want to parse to specific dart type, will explain in section 4.
Usage
In your_project/graphql
folder, create a file for our first query.
# /graphql/task/tasks.query.gql
query Tasks($filter: TaskFiltering, $page: Int = 1, $perPage: Int = 10) {
tasks(filter: $filter, page: $page, perPage: $perPage) {
entries {
...TaskListItem
}
pageInfo {
totalEntries
}
}
currentUser {
id
email
}
}
# /graphql/task/task_list_item.fragment.gql
fragment TaskListItem on Task {
id
title
description {
plainText
}
dueOn
completedAt
contact {
...ContactName
}
company {
...CompanyName
}
assignedTo {
...EmployeeName
}
createdBy {
...EmployeeName
}
}
# /graphql/contact/contact_name.fragment.gql
fragment ContactName on Contact {
id
primaryEmail
info {
firstName
lastName
}
}
Run script below and you will see files generated in your_project/lib/__generated__/graphql_types.dart
.
flutter pub run build_runner build --delete-conflicting-outputs
Now we are ready to call the query in our widget tasks_screen.dart
.
Line 18–26: TaskQuery
is coming from generated code /__generated__/graphql_types.graphql.dart
, as well as TasksArguments
, TaskFiltering
, etc.
Line 29–36: the result coming from GraphQL server is json and we parse into a dart type Tasks$Query$TaskEntries
here.
3. Use Fragments in GraphQL and Flutter widgets
Okay now the question is, we render the tasks in TaskList
widget, but what type is tasks
.
The answer is, TaskListItemMixin
(see line 8).
And line 10 here in task_list_item.dart
.
Another thing is, somehow fragment just doesn’t work with GraphQL cache, here is the github issue about it. So we have to disable cache, if you scroll back up to graphql_provider.dart
, you will see in Line 21–34.
4. Parse custom GraphGL types to Dart types
In our GraphQL backend, we have a type ISO8601DateTime
, and I want it to be converted to dart DateTime
, we can achieve this by using custom parser.
In build.yaml
line 15–21, we specify which type we want to convert and which parser we want to use.
Then we create iso8601_date_time_coercers.dart
in /your_project/lib/utils/coercers
folder as below.
Now all the ISO8601DateTime
data will be dart's DateTime
type. Yay.
5. Save and run scripts from pubspec.yaml
Running flutter pub run build_runner build --delete-conflicting-outputs
is just too clumbsome and it is impossible to remember the command. So let’s add a tool so we can run scripts alias like in package.json
.
Package
rps: Documentation
dart pub global activate rps
Set up
In your pubspec.yaml
, add scripts
like below.
dependencies:
...
scripts:
codegen: flutter pub run build_runner build --delete-conflicting-outputs
Now, we can run rps codegen
.
6. Create data from generated types for widget testing
Now the most important thing of a project, testing! How can we create some tasks for our widget tests?
Here we are, we can create task_list_item_factory.dart
as below.
And here is how we use it in task_list_item.test.dart
.
Line 9: initialise our factory.
Line 16: create a task item using factory.
This is it, hope this helps =)
Summary
your_project/
— graphql/
— — contact/
— — — contact_name.fragment.gql
— — task/
— — — tasks.query.gql
— — — task_list_item.fragment.gql
— lib/
— — __generated__/
— — — graphql_types.graphql.dart // code generated by artemis
— — auth/
— — — widgets/
— — — — graphql_provider.dart
— — task/
— — — screens/
— — — — tasks_screen.dart
— — — widgets/
— — — — task_list.dart
— — — — task_list_item.dart
— — utils/
— — — coercers/
— — — — iso08601_date_time_coercers.dart
— test/
— — task/
— — — widgets/
— — — — task_list_item_factory.dart
— — — — task_list_item.test.dart