Flutter is taking the world by storm and this comes as no surprise. It offers unmatched performance and robust multi-platform support. In this article, I will walk you through integrating GraphQL into Flutter Apps, making queries, and performing mutations.
What Is GraphQL?
GraphQL is a query language, developed by Facebook. It is supposed to replace REST as a mean of API communication. I will assume some basic knowledge of GraphQL for the REST (pun intended) of the article. If you have not heard of it, I strongly suggest you read this article first.
Server Setup
Developing a GraphQL API is out of scope of this article. Instead, we will use a pre-made one. Head out to the repo and clone it. I have forked this server from @haikyuu, many thanks to you.
Once you have it cloned, open it in terminal and run:
npm i
npm start
This will install all dependencies and start the server. This server is a basic TODO app backend, it lets you create, query and modify tasks. It is built using json-graphql-server
, be sure to check it out.
Project Setup
After we got the server up and running, lets set up the Flutter project. This is not an introductory tutorial, and if you have never used Flutter, read this article first. After you have created the project, the directory structure should look like this:
If you try to run the app, this should happen in the emulator:
Congratulations, we are halfway there!
Add GraphQL dependencies
Before we start working with GraphQL in our Flutter app, we need to install some dependencies. Open the pubspec.yaml
file and add this line in it (in dependencies
section:
graphql_flutter: ^3.0.0
Now, install the packages using this command:
flutter pub get
In a few seconds, the dependencies should install.
Write some queries
As you know, you talk to GraphQL APIs using queries. We are going to need 3 of them: a query to get a list of all tasks, a mutation to create new ones, and a mutation to complete an existing one. Create a file api.dart
in /lib
and put this code in it:
Firstly, note the imports on lines 1-2. The first one is a core flutter package that propagates updates down to components. The second one is the graphql_flutter
package we installed earlier.
On lines 4-9 we create a GraphQL client
. It will be used to access our API. The link
is using the address http://10.0.2.2:3000
because this is the IP of the host computer from inside of the emulator. If you are running the app on a physical device, you need to replace this address with the local IP address of your PC (or use something like ngrok).
Lastly, we have queries. The first one is pretty straightforward: get all queries with fields id, title, and completed. createTaskMutation is a mutation to create tasks (obviously!) and it accepts 2 parameters: the id
of the newly created task and its title
. The backend we are using will not check for id
uniqueness, due to its simplicity. The last query will update the completed
field on a task with id
.
There were no changes to the UI yet, so the app should look the same.
Set up the UI
All we have left now is to bind it to the UI. I will not explain the UI in depth, as you are expected to know the basics, instead, I will focus on how it interacts with GraphQL. Firstly, replace all code in main.dart
with this:
The first thing you should notice is the GraphQLProvider
on line 11. By wrapping the whole application in it, we provide the rest of the widgets with the GraphQL client. This means we can run queries and mutations from anywhere in the app.
Now, examine the build
function of the _ListPageState
class on line 75. Here, we are using the Query
widget to run the query and pass the results to its children. Query
accepts a options
argument of type QueryOptions
, which set up the query. The gql(getTasksQuery)
will parse the query and pollInterval: 1
means that this query will refresh every second (this is highly inefficient, but will do for now).
Query
will pass the QueryResult
and the refetch
and fetchMore
functions to its builder
callback. QueryResult
has fields loading
and hasException
, which we use to determine what to render (lines 85-90). refetch
will re-run the query if we call it, and we do, each time user toggles a task (also highly inefficient). fetchMore
is used when implementing pagination, but we will leave it for now.
Now, look at the TaskList
widget, specifically, its build
function. It is wrapped with the Mutation
class, which works pretty much like Query
. The main difference is that Mutation
does not automatically execute the mutation. It will pass RunMutation
to its builder
function that must be called to execute the mutation. You can see it in action on line 121. It accepts a map with arguments for the mutation (id
and completed
, in this case). We then call onRefresh
to re-run the query and get updated data.
Lastly, the function onCreate
on line 33 is responsible for showing a dialog to ask for the task title and creating the task itself. The AlertDialog
is wrapped in now familiar Mutation
and line 63 calls the mutation itself. Now we are passing id
and title
, the completed
is false
by default.
This is how the app should look:
If you got lost at any point, the Github repo is available here.
Closing notes
Thank you for reading, I hope now you know how to use GraphQL in Flutter. In my coming posts I will go into performance optimizations and real-time updating. Stay tuned!