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

May Chen
NEXL Engineering
4 min readDec 9, 2022

--

In this post, we will cover:

  1. Set up GraphQL client in Flutter app
  2. Use Artemis to generate GraphQL types
  3. Use Fragments in GraphQL
  4. Parse custom GraphGL types to Dart types
  5. Save and run scripts from pubspec.yaml
  6. 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

--

--

May Chen
NEXL Engineering

A developer who occasionally has existential crisis and thinks if we are heading to the wrong direction, technology is just getting us there sooner.