Both GraphQL and Flutter introduced a new style of software development when they were first released. GraphQL enabled developers to fetch data in their desired shape and format. Flutter made it possible to build a mobile app in one language and cross-compile it for other platforms.
Combining these two revolutionary technologies opens yet another world of possibilities.
In this tutorial, we’ll demonstrate how to use GraphQL in a Flutter app, including how to make a query, make a mutation, and set up a subscription in a Flutter app using the graphql_flutter plugin. We’ll also show you how to consume GraphQL endpoints from a Flutter app.
Here’s what we’ll cover:
Developed by Facebook in 2012 and released to the public in 2015, GraphQL was designed to revolutionize the way data is served from the backend. With GraphQL, you can state the structure of the data you want.
For example, let’s say we have the following table model in our database:
Food { name description price chef origin }
We have a model for Food
. The fields represent the properties of the food:
name
property is the name of the food,description
describes the food in totoprice
represents the selling price of the foodchef
holds the name of the chef that cooked the foodorigin
states the history of the foodUsing REST, we can fetch foods like this:
/GET localhost:8080/foods [ { name: "Joolof Rice", desciption: "A spicy food", price: "$50", chef: "nnamdi", origin: "West Africa" } ]
As you can see, with REST, all the properties of each food are returned, whether we need them or not. We might only need to use the name
and price
propertied in our frontend, but all of the properties were returned.
GraphQL helps us avoid this redundancy. With GraphQL, we can state the properties we want to be returned, like so:
query foods { food { name price } }
We’re telling the server that we only need the name
and price
properties from the Food
table. It gives it to us exactly as we need:
{ "data": [ { "name": "Joolof Rice", "price": "$50" } ] }
GraphQL is used by thousands of companies today. It has other benefits that make it a go-to choice, including:
GraphQL is a backend technology while Flutter is a frontend SDK that is used to build mobile apps. With mobile apps, we fetch the data displayed on the mobile app from a backend. Since GraphQL is a backend we can make our Flutter fetch data from a GraphQL backend.
It’s quite easy to build a Flutter app that fetches data from a GraphQL backend. You just need to make an HTTP request from the Flutter app, then use the returned data to set up the UI and display them.
The new graphql_flutter plugin provides APIs and widgets that enable you to fetch and use data from a GraphQL backend with ease.
The new graphql_flutter plugin provides APIs and widgets that enable you to fetch and use data from a GraphQL backend with ease.
As the name implies, graphql_flutter is a GraphQL client for Flutter. It exports widgets and providers that can be used to fetch data from a GraphQL backend, including:
HttpLink
— This is used to set the endpoint or URL of the backendGraphQLClient
— This class is used to fetch the query/mutation from a GraphQL endpoint and also to connect to a GraphQL serverGraphQLCache
— This class is used to cache our queries and mutations. It has options store
where we pass to it the type of store in its caching operationGraphQLProvider
— This widget wraps the whole graphql_flutter widgets so they can make queries/mutations. The GraphQL client to use is passed to this widget. This client is what this provider makes available to all widgets in its treeQuery
— This widget is used to make a query to a GraphQL backendMutation
— This widget is used to make a mutation to a GraphQL backendSubscription
— This widget is used to set up a subscriptionTo use the graphql_flutter package, we have to create a Flutter project:
flutter create flutter_graphql cd flutter_graphql
Next, install the graphql_flutter
package:
flutter pub add graphql_flutter
The above code will install the graphql_flutter package. This will add the package graphql_flutter
to the dependencies section of your pubspec.yaml
file:
dependencies: graphql_flutter: ^5.0.0
To use the widgets, we have to import the package like this:
import 'package:graphql_flutter/graphql_flutter.dart';
First of all, before we begin making GraphQL queries and mutations, we have to wrap our root widget with GraphQLProvider
. The GraphQLProvider
must be provided a GraphQLClient
instance to its client
property.
GrpahQLProvider( client: GraphQLClient(...) )
The GraphQLClient
is provided with the GraphQL server URL and a caching mechanism.
final httpLink = HttpLink(uri: "http://10.0.2.2:4000/"); ValueNotifier<GraphQLClient> client = ValueNotifier( GraphQLClient( cache: InMemoryCache(), link: httpLink ) );
The URL of the GraphQL server is created using HttpLink
. The instance of the HttpLink
is passed to the GraphQLClient
in a link
property, which tells the GraphQLClient the URL of the GraphQL endpoint.
The cache
passed to GraphQLClient tells it the cache mech to use. The InMemoryCache
instance makes use of an in-memory database to persist or store caches.
The instance of the GraphQLClient
is passed to a ValueNotifier
. This ValueNotifer
is used to hold a single value and has listeners that notify when the single value changes. graphql flutter uses this to notify its widgets when the data from a GraphQL endpoint changes, which helps keep graphql flutter reactive.
Now, we’ll wrap our MaterialApp
widget with GraphQLProvider
:
void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return GraphQLProvider( client: client, child: MaterialApp( title: 'GraphQL Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage( title: 'GraphQL Demo' ), ) ); } }
To make a query using the graphql_flutter
package, we’ll use the Query
widget.
class MyHomePage extends StatelessWidget { @override Widget build(BuildContext) { return Query( options: QueryOptions( document: gql(readCounters), variables: { 'counterId': 23, }, pollInterval: Duration(seconds: 10), ), builder: (QueryResult result, { VoidCallback refetch, FetchMore fetchMore }) { if (result.hasException) { return Text(result.exception.toString()); } if (result.isLoading) { return Text('Loading'); } // it can be either Map or List List counters = result.data['counter']; return ListView.builder( itemCount: repositories.length, itemBuilder: (context, index) { return Text(counters\[index\]['name']); }); }, ) } }
Here, the Query
widget encloses the ListView
widget where we will render the list of counters to fetch from our GraphQL server. So the Query
widget must wrap the widget where you want to display the data fetched by the Query
widget.
The Query
widget must not be the top-most widget in a tree. It can be placed anywhere else provided the widget that will make use of its data is underneath or wrapped by it.
Also, the Query
widget has two properties passed to it: options
and builder
.
options
options: QueryOptions( document: gql(readCounters), variables: { 'conuterId': 23, }, pollInterval: Duration(seconds: 10), ),
The option
property is where the configuration of the query is passed to the Query
widget. This options
prop is an instance of the QueryOptions
. The QueryOptions
class exposes properties we use to set options for the Query
widget.
The document
property is used to set the query string or to pass in the query we want the Query
widget to perform. Here, we passed in the readCounters
string:
final String readCounters = """ query readCounters(\$counterId: Int!) { counter { name id } } """;
The variables
property is where the query variables are sent to the Query
widget. We have 'counterId': 23,
there. This will be passed in place of $counterId
in the readCounters
query string.
The pollInterval
is the time interval during which the Query
widget will poll or refresh the query data. The time is set to 10 seconds, so after every 10 seconds, the Query
widget will perform HTTP requests to refresh the query data.
builder
The builder
property is a function. The function is called when the Query
widget makes an HTTP request to the GraphQL server endpoint. The builder
function is called by the Query
widget with the data from the query, a function that is used to refetch the data, and a function that is used for pagination. This is used to fetch more data.
The builder
function returns widgets below the Query
widget. The result
arg is an instance of the QueryResult
. The QueryResult
has properties that we can use to know the state of the query and the data returned by the Query
widget.
QueryResult.hasException
is set if the query encounters an error.QueryResult.isLoading
is set if the query is still in progress. We can use this property to display a UI progress to our users to tell them that something is on the wayQueryResult.data
holds the data returned by the GraphQL endpointLet’s see how to use the Mutation
widget in graphql_flutter to make mutation queries.
The Mutation
widget is used like this:
Mutation( options: MutationOptions( document: gql(addCounter), update: (GraphQLDataProxy cache, QueryResult result) { return cache; }, onCompleted: (dynamic resultData) { print(resultData); }, ), builder: ( RunMutation runMutation, QueryResult result, ) { return FlatButton( onPressed: () => runMutation({ 'counterId': 21, }), child: Text('Add Counter') ); }, );
Just like the Query
widget, the Mutation
widget takes some properties.
options
is an instance of the MutationOptions
class. This is where the mutation string and other configurations occurdocument
is used to set the mutation string. Here we have an addCounter
mutation passed to the document
. It will be run by the Mutation
widgetupdate
is called when we want to update the cache. The update
function is called with the previous cache (cache
) and the result of the mutation result
. Anything returned from the update
becomes the new value of the cache. Here, we are updating the cache based on the resultsonCompleted
is called when the mutations have been called on the GraphQL endpoint. Then the onCompleted
function is called with the mutation resultbuilder
is used to return the widget that will be under the Mutation
widget tree. This function is called with a RunMutation
instance, runMutation
, and a QueryResult
instance, result
.runMutation
is used to run the mutation in this Mutation
widget. Whenever it is called, the Mutattion
widget triggers the mutation. This runMutation
function is passed the mutation variables as parameters. Here, the runMutation
is called with the counterId
variable, 21
Now, when the mutation from the Mutation
is complete, the builder
is called so the Mutation
rebuilds its tree. runMutation
and the result of the mutation is passed to the builder
function.
Subscriptions in GraphQL are like an event system listening in on a WebSocket and calling a function when an event is emitted into the stream.
The WebSocket is opened from the client to the GraphQL server. Whenever the server emits an event from its end, the event is passed to the WebSocket. So this is real-time stuff.
In Flutter, the graphql_flutter
plugin utilizes WebSockets and Dart stream to open and provide real-time updates from the server.
graphql_flutter has a Subscription
widget we can use to open a real-time connection and comms to a GraphQL server.
Let’s see how we can use the Subscription
widget to set up real-time connection in our Flutter app. First we define our subscription string:
final counterSubscription = ''' subscription counterAdded { counterAdded { name id } } ''';
This subscription will give us a real-time update when a new counter is added to our GraphQL server.
Subscription( options: SubscriptionOptions( document: gql(counterSubscription), ), builder: (result) { if (result.hasException) { return Text("Error occured: " + result.exception.toString()); } if (result.isLoading) { return Center( child: const CircularProgressIndicator(), ); } return ResultAccumulator.appendUniqueEntries( latest: result.data, builder: (context, {results}) => ... ); } ),
We see that the Subscription
widget has many properties:
options
holds the configuration for the Subscription
widget document
holds the subscription string builder
returns the widget tree of the Subscription
widget.
The builder
function is called with the result of the subscription. The result
has some useful properties:
result.hasException
is set if the Subscription
widget encounters an error polling for updates from the GraphQL server result.isLoading
is set if the polling from the server is in progress
We have this ResultAccumulator.appendUniqueEntries()
call. According to graphql_flutter’s pub.dev page, ResultAccumulator
is a provided helper widget for collating subscription results.
We covered a lot in this Flutter and GraphQL tutorial. We started by introducing, in a nutshell, what GraphQL is and how it works. Then, we introduced graphql_flutter and demonstrated with examples how to make queries, mutations, and subscriptions from a Flutter app.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic GraphQL requests to quickly understand the root cause. In addition, you can track Apollo client state and inspect GraphQL queries' key-value pairs.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
One Reply to "Using GraphQL with Flutter: A tutorial with examples"
Hello,
I’m trying to avoid using packages as much as possible, so I’m trying to create a model class in Flutter to pull the data from my Strapi GraphQL endpoint. But that turned out to be more difficult than I thought, because in Strapi (v4) GraphQL I have “data” and “attributes” everywhere. Is there a tutorial or documentation I can learn from without using a GraphQL or Fairy Package to connect my Strapi/GraphQL to my Flutter app?