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.
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.
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
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
/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
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
fetchMore functions to its
QueryResult has fields
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 (
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
false by default.
This is how the app should look:
If you got lost at any point, the Github repo is available here.
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!