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 therunMutation
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.