Waldo sessions now support scripting! – Learn more
App Development

A Complete Guide to GraphQL in Flutter

Llamdo
Llamdo
A Complete Guide to GraphQL in Flutter
May 21, 2021
7
min read

In this article, you’ll learn…

  • what GraphQL is
  • how it affects API
  • what queries are
  • how to use GraphQL

Together, we’ll build an app as a demonstration using a public GraphQL API. You’ll also learn what mutations are and how to use them. You’ll see an example of how to use mutations in applications. By the end of this article, you will have conquered GraphQL, and you’ll be ready to apply it in your applications!

Getting Started

I’m going to make a few assumptions about you:

  • You’ve worked with HTTP requests.
  • You’re familiar with REST API, and maybe you’ve worked with it on a few projects.
  • You’re familiar with HTTP requests and terminologies such as GET, POST, PUT, PATCH, and DELETE.

You might be looking for an API option where you get to implement a lesser number of endpoints.

REST API helps you target several services with different endpoints. So, you can have about 10 endpoints in a small-scale project, going up to 40 or more on a higher scale.

It can get really overwhelming as a new developer joining the team or trying to keep up with a large amount of API in a project. GraphQL eliminates this problem by bringing all services under a single endpoint.

It can get really overwhelming as a new developer joining the team or trying to keep up with a large amount of API in a project. GraphQL eliminates this problem by bringing all services under a single endpoint.

What Is GraphQL?

Are you looking for something with fewer endpoints? Probably!

The Q and L in GraphQL stand for Query Language. In its most basic form, GraphQL is a language for performing queries on a GraphQL API, all with one endpoint. You ask for a specific field, and you get your result as easily as that.

GraphQL operates with two request types.

  • Queries are similar to a GET request.
  • Mutations (as the name implies) combine POST, PUT, PATCH, and REMOVE requests.  

Queries in GraphQL

Queries let you get particular information or services from an API. As with the GET request, you perform a query and get a definite result based on what you asked. You’ll learn all about queries in GraphQL. Also, you’ll learn how to use them with a GraphQL API.

Let’s use a public GraphQL API. I’ve checked out a few for this article and settled on one: Rick and Morty’s API. This API is based on the Rick and Morty television show.  

From this API, we’ll pull the data of everyone in the show named “Rick.” That means we’ll place a query request fetching the data of every character named Rick in the show and their current status—alive, dead, or unknown. I’ll guide you along the way.

Working With Flutter

Flutter is a framework created by Google. It lets developers create beautiful, scalable, and cross-platform applications, all from a single code base. So from this single code base, this example application would run on both your Android device and iOS device.  

Let’s get started. The first step is to create your default Flutter app.

 
 
flutter create graphql_full_guide

Next, add the Flutter GraphQL package.  

What does that do? It provides client-side support for GraphQL API. That means it’s got everything you’ll need to effectively run GraphQL in your app.  

In your pubspec.yaml file, add this line to the dependencies:

 
 
graphql_flutter: ^4.0.1

This adds the GraphQL package to the project as a dependency, so the application can use it. The code block below shows the command to fetch the dependency.

 
 
flutter pub get

Setting Up the GraphQL Client

Next, set up your GraphQL client. This comes from the qraphql_flutter package.

The GraphQLClient needs two things.

First is the GraphQL link you’re calling. For this article, it’s Rick and Morty’s API. Soon I’ll explain how to create the query.

The second is the cache. We’ll use the default in-memory database. Hive-Box is also a nice option for caching.

The code below shows the GraphQL client with the HTTP link and cache parameters passed in.

 
 
final HttpLink rickAndMortyHttpLink =
        HttpLink('https://rickandmortyapi.com/graphql');
    ValueNotifier<GraphQLClient> client = ValueNotifier(
      GraphQLClient(
        link: rickAndMortyHttpLink,
        cache: GraphQLCache(
          store: InMemoryStore(),
        ),
      ),
    );
return GraphQLProvider(
      client: client,
      child: MaterialApp(
        title: 'Material App',
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Complete GraphQl Guide'),
            backgroundColor: Colors.black,
          ),

Now the GraphQLClient is set.

Time to Wrap

The next thing to do is to wrap your MaterialApp with a GraphQLProvider. This is essential because it gives you access to widgets for performing queries and mutations. After that, pass the GraphQLClient that you created earlier to its client parameter. The code block below shows how to do just that.

 
 
return GraphQLProvider(
      client: client,
      child: MaterialApp(
        title: 'Material App',
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Complete GraphQl Guide'),
            backgroundColor: Colors.black,
          ),

Making Queries

Now you’re ready to start making queries to Rick and Morty’s API.  

The query we’ll make is to get all characters named Rick from the GraphQL API.  

Head over to the GraphQL API to check out the schema and docs.

screenshot of API
close look at screenshot graphql api
schema screenshot

In the image above, you can see the queries and their appropriate responses. Go through the schema to get an overall feel for the construction of the API. Remember, the aim is to get all characters named “rick.”

Next, create a query and pass in the name through the filter parameter the API provides. Then call the types needed in the result. You can see this in the code block below. The name “rick” is passed into the name parameter for the filter. In the results block, the name and status types are passed in. Therefore, the API returns the name and status.

 
 
const rickCharacters = '''
  query characters {
      characters(page: 1, filter: { name: "rick" }) {
        info {
          count
        }
        results {
          name
          status
        }
      }
  }
  ''';

The “Info” type as described by the schema provides information on the number of results you have.  

The next step is to get the results, which is the list of characters named “rick” from the schema. There are several parameters you can get or queries you can make as regards a particular character. These include name, status, gender, type, origin, and so on. Put as many parameters as you need inside this block.

Now back to your IDE. Make the same query in your code. Use the Query widget because you’re performing a query to your API.

Parameters of the Query Widget

The Query widget requires the following two parameters to be passed:

  • The options take in QueryOptions have a parameter named documentNode. It receives a GraphQL converter, also called a GQL converter, that parses or converts the string query (rickCharacters) into a document node.
  • The second parameter the Query widget takes in is a builder function. it provides you with your result and some handy methods to perform a fetchMore call and a refetch call.

The first thing to do in your builder function is to handle different states of the app. These include states like when it’s loading (you can set it to display a  circular progress indicator), errors, and so on.

 
 
body: Query(
            options: QueryOptions(
              document: gql(rickCharacters),
            ),
            builder: (result, {fetchMore, refetch}) {
              // If stements here to check handle different states;
              if (result.isLoading) {
                return const Center(
                  child: CircularProgressIndicator(),
                );
              }
              return Column(
                children: [
                  const SizedBox(
                    height: 8,
                  ),
                  const Text(
                    'Characters with name Rick',
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(
                    height: 8,
                  ),
                  Expanded(
                    child: ListView.builder(
                      itemCount: result.data['characters']['results'].length,
                      itemBuilder: (context, index) {
                        return CharactersResult(
                          result: result.data['characters']['results'],
                          index: index,
                        );
                      },
                    ),
                  ),
                ],
              );
            },
          ),

Creating a Custom Widget

Now let’s create your custom widget to display your data from the API.

custom widget flutter graphql

Run the app either on your emulator or phone. You’ll see it display the list of characters named “Rick” and their current status.  

Congratulations! You’ve just mastered how to make query requests in GraphQL. And you’ve successfully implemented it using the Rick and Morty GraphQL API as an example.

Mutations

Just as its literal meaning implies, mutations deal with making changes. PUT, POST, PATCH, and DELETE requests for REST API all fall under MUTATIONS in GraphQL API.

The GraphQLProvider gives you a mutation widget (just like the query widget). But rick API doesn’t allow mutation requests. So, we’ll use another API: GraphQLZero API. It’s basically an online, free-to-use GraphQL API for testing and prototyping.

 
 
final HttpLink almansiMeApi =
        HttpLink('https://graphqlzero.almansi.me/api');
    ValueNotifier<GraphQLClient> client = ValueNotifier(
      GraphQLClient(
        link: almansiMeApi,
        cache: GraphQLCache(
          store: InMemoryStore(),
        ),
      ),
    );
    String createMyPost = '''
mutation myTodo(\$title: String!, \$body: String!){
  createPost(
    input:{
    title: \$title
    body: \$body
  }
  ){
    id
    title
    body
  }
}
''';
    final postTitleController = TextEditingController();
    final postBodyController = TextEditingController();
    return GraphQLProvider(
      client: client,
      child: MaterialApp(
        title: 'Material App',
        home: Mutation(

Again, set up the project similar to that for query requests, with little adjustments as needed.  

Next, set up the GraphQLClient, and pass the HTTP link to it. Wrap the MaterialApp with the GraphQLProvider. This will provide you access to the mutation widget for making your mutations to the API.

Check Those Documents

Before moving further, check out the schema and documents for this API. Doing this will help you get a clearer understanding of what to expect.

document flutter graphql
schema flutter graphql
close screenshot of flutter graphql

Looking at your query, note the function parameters. They’re the values you want to change. In this instance, you’re creating a new Todo with your API.  

In the createPost function, there’s an input parameter into which you can pass values from your function parameter. Looking at the schema, you’ll see that the result of this createPost call is a Post item.  

How do you know the parameters of the Post item? Check the documents. You’ll see that they contain the id, title, body, comment, and user.

Note: the user is also a type on its own. However, you’ll need only the name and ID of the user—which will, in turn, be returned.

 
 
final HttpLink almansiMeApi =
        HttpLink('https://graphqlzero.almansi.me/api');
    ValueNotifier<GraphQLClient> client = ValueNotifier(
      GraphQLClient(
        link: almansiMeApi,
        cache: GraphQLCache(
          store: InMemoryStore(),
        ),
      ),
    );
    String createMyPost = '''
mutation myTodo(\$title: String!, \$body: String!){
  createPost(
    input:{
    title: \$title
    body: \$body
  }
  ){
    id
    title
    body
  }
}
''';
    final postTitleController = TextEditingController();
    final postBodyController = TextEditingController();
    return GraphQLProvider(
      client: client,
      child: MaterialApp(

Inserting the Mutation Widget

Moving forward, insert the Mutation widget. It takes in two parameters:  

  • options parameter
  • builder parameter

The options parameter accepts MutationOptions. Convert the mutation string (createMyPost) to a DocumentNode, and pass it to the MutationOptions.

 
 
options: MutationOptions(documentNode: gql(createMyPost)),

The builder function provides the runMutation and result parameters. You can use them to make the call and get the result.

Since you’ll need the title and body parameters to be passed into your mutation, let’s create a simple layout consisting of two text fields and assign a controller to each.  

Then, create a text button that allows you to run the runMutation function in its onTap parameter. Pass in the post title and body in the runMutation function.  

Now, look at the result displayed in the text field below it. You can use this result any way you like within the app. And that wraps up mutations in GraphQL!

 
 
home: Mutation(
          // ignore: deprecated_member_use
          options: MutationOptions(documentNode: gql(createMyPost)),
          builder: (runMutation, result) {
            return Scaffold(
              body: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    TextField(
                      controller: postTitleController,
                      decoration: const InputDecoration(
                        hintText: 'Post Title',
                        contentPadding: EdgeInsets.symmetric(horizontal: 8),
                      ),
                    ),
                    const SizedBox(height: 8),
                    TextField(
                      controller: postBodyController,
                      decoration: const InputDecoration(
                        hintText: 'Post Title',
                        contentPadding: EdgeInsets.symmetric(horizontal: 8),
                      ),
                    ),
                    TextButton(
                      onPressed: () {
                        runMutation({
                          'title': postTitleController.text,
                          'body': postBodyController.text,
                        });
                      },
                      child: const Text('Create Post'),
                    ),
                    Card(
                      child: Container(
                        padding: const EdgeInsets.all(8),
                        color: Colors.white,
                        child: Text(
                          result.data == null
                              ? '''Post details coming up shortly,'''
                                  ''' Kindly enter details and create a post'''
                              : result.data.toString(),
                        ),
                      ),
                    )
                  ],
                ),
              ),
            );
          },
        ),
screen capture of mutation in widget
screenshot of mutation in widget

Conclusion

Hooray! Give yourself a round of applause. You’ve mastered the basics of GraphQL.

With the knowledge you’ve gained from this article, you can now start making apps off GraphQL APIs. Start enjoying the world of a single endpoint.

You may also be interested in Waldo, the world’s first no-code testing platform. Check out the blog and the free trial.

This post was written by Imoh Promise Chinedu. Promise is a tech enthusiast focused on Cloud Native and Full Stack development using JavaScript and Golang.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.