Using the graphql_flutter Package in Flutter

GraphQL is a query language for your API and a server-side runtime for executing those queries by using a type system you define for your data. It’s an alternative to REST, offering more efficiency and flexibility. graphql_flutter is a powerful package that enables you to integrate GraphQL seamlessly into your Flutter applications. This article guides you through using the graphql_flutter package in Flutter, including setup, querying, mutation, and subscription examples.

What is GraphQL?

GraphQL provides a more efficient, powerful, and flexible alternative to REST APIs. Clients can request specific data they need, reducing over-fetching and improving performance. Key features include:

  • Schema: Defines the data structure, types, and operations.
  • Queries: Requests to fetch data.
  • Mutations: Requests to modify data.
  • Subscriptions: Real-time data updates.

Why Use graphql_flutter?

graphql_flutter is a Flutter package designed to facilitate the integration of GraphQL into your applications. Its benefits include:

  • Easy GraphQL API integration
  • Efficient data fetching and manipulation
  • Support for queries, mutations, and subscriptions
  • Caching and state management features

Setup

Step 1: Add Dependencies

Add graphql_flutter to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  graphql_flutter: ^5.1.0 # Use the latest version

Run flutter pub get to install the package.

Step 2: Configure the GraphQL Client

Create a GraphQL client with the endpoint of your GraphQL API.

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

void main() {
  final HttpLink httpLink = HttpLink(
    'https://your-graphql-endpoint.com/graphql',
  );

  final ValueNotifier client = ValueNotifier(
    GraphQLClient(
      link: httpLink,
      cache: GraphQLCache(store: InMemoryStore()),
    ),
  );

  var app = MaterialApp(
    title: 'Flutter GraphQL Demo',
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: GraphQLProvider(
      client: client,
      child: const MyApp(),
    ),
  );

  runApp(app);
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GraphQL Flutter Demo'),
      ),
      body: Center(
        child: Text('GraphQL Ready!'),
      ),
    );
  }
}

Here, GraphQLProvider makes the GraphQL client available to all widgets in your app.

Making Queries

Basic Query Example

Fetch data using the Query widget. Define your GraphQL query string and handle the data accordingly.

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

class Posts extends StatelessWidget {
  const Posts({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const String readPosts = """
      query ReadPosts {
        posts {
          id
          title
          body
        }
      }
    """;

    return Query(
      options: QueryOptions(
        document: gql(readPosts),
      ),
      builder: (QueryResult result, {Refetch? refetch, FetchMore? fetchMore}) {
        if (result.hasException) {
          return Text(result.exception.toString());
        }

        if (result.isLoading) {
          return const Center(child: CircularProgressIndicator());
        }

        List? posts = result.data?['posts'];
        if (posts == null) {
          return const Text('No posts');
        }

        return ListView.builder(
          itemCount: posts.length,
          itemBuilder: (context, index) {
            final post = posts[index];
            return ListTile(
              title: Text(post['title']),
              subtitle: Text(post['body']),
            );
          },
        );
      },
    );
  }
}

In this example:

  • A GraphQL query readPosts is defined to fetch posts.
  • The Query widget executes the query.
  • The builder function handles loading, errors, and data display.

Making Mutations

Basic Mutation Example

Use the Mutation widget to update or create data. Define the mutation and handle the result.

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

class CreatePost extends StatelessWidget {
  const CreatePost({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const String createPostMutation = """
      mutation CreatePost($title: String!, $body: String!) {
        createPost(title: $title, body: $body) {
          id
          title
          body
        }
      }
    """;

    final TextEditingController titleController = TextEditingController();
    final TextEditingController bodyController = TextEditingController();

    return Mutation(
      options: MutationOptions(
        document: gql(createPostMutation),
        onCompleted: (dynamic result) {
          print('Mutation completed: $result');
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Post created!')),
          );
        },
      ),
      builder: (RunMutation runMutation, QueryResult? result) {
        return Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              TextField(
                controller: titleController,
                decoration: const InputDecoration(labelText: 'Title'),
              ),
              TextField(
                controller: bodyController,
                decoration: const InputDecoration(labelText: 'Body'),
              ),
              ElevatedButton(
                onPressed: () {
                  runMutation({
                    'title': titleController.text,
                    'body': bodyController.text,
                  });
                },
                child: const Text('Create Post'),
              ),
              if (result?.isLoading ?? false)
                const CircularProgressIndicator(),
            ],
          ),
        );
      },
    );
  }
}

In this example:

  • A mutation createPostMutation is defined to create a new post.
  • The Mutation widget provides the runMutation function.
  • Text fields capture user input for the title and body.
  • The runMutation function is called when the button is pressed, sending variables to the GraphQL server.

Making Subscriptions

Basic Subscription Example

Use the Subscription widget to receive real-time updates from the server. Here’s an example of how to use subscriptions to listen for new comments:

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

class NewComments extends StatelessWidget {
  const NewComments({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const String newCommentsSubscription = """
      subscription NewComments {
        newComment {
          id
          text
        }
      }
    """;

    return Subscription(
      options: SubscriptionOptions(
        document: gql(newCommentsSubscription),
      ),
      builder: (QueryResult result) {
        if (result.hasException) {
          return Text(result.exception.toString());
        }

        if (result.isLoading) {
          return const CircularProgressIndicator();
        }

        final comment = result.data?['newComment'];
        if (comment == null) {
          return const Text('No new comments');
        }

        return ListTile(
          title: Text(comment['text']),
        );
      },
    );
  }
}

In this example:

  • A subscription newCommentsSubscription is defined to listen for new comments.
  • The Subscription widget listens for real-time updates.
  • The builder function updates the UI with the new comment.

Advanced Usage

Caching

graphql_flutter includes a caching mechanism. You can configure it as part of your GraphQLClient:

final ValueNotifier client = ValueNotifier(
  GraphQLClient(
    link: httpLink,
    cache: GraphQLCache(store: HiveStore()), // Or InMemoryStore()
  ),
);

Use HiveStore or InMemoryStore to configure caching behavior.

Error Handling

Handle GraphQL errors gracefully within the builder functions of Query, Mutation, and Subscription widgets:

builder: (QueryResult result, {Refetch? refetch, FetchMore? fetchMore}) {
  if (result.hasException) {
    return Text('Error: ${result.exception}');
  }
  // ...
}

Conclusion

Integrating GraphQL into Flutter using the graphql_flutter package allows you to build efficient and flexible applications. With the ability to fetch precise data using queries, manipulate data with mutations, and receive real-time updates with subscriptions, graphql_flutter empowers you to create powerful and reactive applications. This comprehensive guide provided setup instructions, query examples, mutation implementation, and subscription handling, equipping you with the knowledge to start leveraging GraphQL in your Flutter projects.