• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

Implementing GraphQL with Flutter: Everything you need to know

Azharuddin T.E.M

Flutter

Thinking about using GraphQL but unsure where to start? 

This is a concise tutorial based on our experience using GraphQL. You will learn how to use GraphQL in a Flutter app, including how to create a query, a mutation, and a subscription using the graphql_flutter plugin. Once you've mastered the fundamentals, you can move on to designing your own workflow.

Key topics and takeaways:

* GraphQL

* What is graphql_flutter?

* Setting up graphql_flutter and GraphQLProvider

* Queries

* Mutations

* Subscriptions

GraphQL

Looking to call multiple endpoints to populate data for a single screen? Wish you had more control over the data returned by the endpoint? Is it possible to get more data with a single endpoint call, or does the call only return the necessary data fields?

Follow along to learn how to do this with GraphQL. GraphQL’s goal was to change the way data is supplied from the backend, and it allows you to specify the data structure you want.

Let's imagine that we have the table model in our database that looks like this:

Movie {

 title

 genre

 rating

 year

}

These fields represent the properties of the Movie Model:

  • title property is the name of the Movie,
  • genre describes what kind of movie
  • rating represents viewers interests
  • year states when it is released

We can get movies like this using REST:

/GET localhost:8080/movies
view raw .txt hosted with ❤ by GitHub

[
{
"title": "The Godfather",
"genre": "Drama",
"rating": 9.2,
"year": 1972
}
]
view raw .json hosted with ❤ by GitHub

As you can see, whether or not we need them, REST returns all of the properties of each movie. In our frontend, we may just need the title and genre properties, yet all of them were returned.

We can avoid redundancy by using GraphQL. We can specify the properties we wish to be returned using GraphQL, for example:

query movies { Movie {   title   genre }}
view raw .dart hosted with ❤ by GitHub

We're informing the server that we only require the movie table's title and genre properties. It provides us with exactly what we require:

{
"data": [
{
"title": "The Godfather",
"genre": "Drama"
}
]
}
view raw .json hosted with ❤ by GitHub

GraphQL is a backend technology, whereas Flutter is a frontend SDK for developing mobile apps. We get the data displayed on the mobile app from a backend when we use mobile apps.

It's simple to create a Flutter app that retrieves data from a GraphQL backend. Simply make an HTTP request from the Flutter app, then use the returned data to set up and display the UI.

The new graphql_flutter plugin includes APIs and widgets for retrieving and using data from GraphQL backends.

What is graphql_flutter?

The new graphql_flutter plugin includes APIs and widgets that make it simple to retrieve and use data from a GraphQL backend.

graphql_flutter, as the name suggests, is a GraphQL client for Flutter. It exports widgets and providers for retrieving data from GraphQL backends, such as:

  • HttpLink — This is used to specify the backend's endpoint or URL.
  • GraphQLClient — This class is used to retrieve a query or mutation from a GraphQL endpoint as well as to connect to a GraphQL server.
  • GraphQLCache — We use this class to cache our queries and mutations. It has an options store where we pass the type of store to it during its caching operation.
  • GraphQLProvider — This widget encapsulates the graphql flutter widgets, allowing them to perform queries and mutations. This widget is given to the GraphQL client to use. All widgets in this provider's tree have access to this client.
  • Query — This widget is used to perform a backend GraphQL query.
  • Mutation — This widget is used to modify a GraphQL backend.
  • Subscription — This widget allows you to create a subscription.

Setting up graphql_flutter and GraphQLProvider

Create a Flutter project:

flutter create flutter_graphqlcd flutter_graphql
view raw .sh hosted with ❤ by GitHub

Next, install the graphql_flutter package:

flutter pub add graphql_flutter
view raw .sh hosted with ❤ by GitHub

The code above will set up the graphql_flutter package. This will include the graphql_flutter package in the dependencies section of your pubspec.yaml file:

dependencies:graphql_flutter: ^5.0.0
view raw .yaml hosted with ❤ by GitHub

To use the widgets, we must import the package as follows:

import 'package:graphql_flutter/graphql_flutter.dart';
view raw .dart hosted with ❤ by GitHub

Before we can start making GraphQL queries and mutations, we must first wrap our root widget in GraphQLProvider. A GraphQLClient instance must be provided to the GraphQLProvider's client property.

GraphQLProvider( client: GraphQLClient(...))
view raw .dart hosted with ❤ by GitHub

The GraphQLClient includes the GraphQL server URL as well as a caching mechanism.

final httpLink = HttpLink(uri: "http://10.0.2.2:8000/");‍ValueNotifier<GraphQLClient> client = ValueNotifier( GraphQLClient(   cache: InMemoryCache(),   link: httpLink ));
view raw .dart hosted with ❤ by GitHub

HttpLink is used to generate the URL for the GraphQL server. The GraphQLClient receives the instance of the HttpLink in the form of a link property, which contains the URL of the GraphQL endpoint.

The cache passed to GraphQLClient specifies the cache mechanism to be used. To persist or store caches, the InMemoryCache instance makes use of an in-memory database.

A GraphQLClient instance is passed to a ValueNotifier. This ValueNotifer holds a single value and has listeners that notify it when that value changes. This is used by graphql_flutter to notify its widgets when the data from a GraphQL endpoint changes, which helps graphql_flutter remain responsive.

We'll now encase our MaterialApp widget in 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'),       )); }}
view raw .dart hosted with ❤ by GitHub

Queries

We'll use the Query widget to create a query with the graphql_flutter package.

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']);           });     },) }}
view raw .dart hosted with ❤ by GitHub

The Query widget encloses the ListView widget, which will display the list of counters to be retrieved from our GraphQL server. As a result, the Query widget must wrap the widget where the data fetched by the Query widget is to be displayed.

The Query widget cannot be the tree's topmost widget. It can be placed wherever you want as long as the widget that will use its data is underneath or wrapped by it.

In addition, two properties have been passed to the Query widget: options and builder.

options

options: QueryOptions( document: gql(readCounters), variables: {   'conuterId': 23, }, pollInterval: Duration(seconds: 10),),
view raw .dart hosted with ❤ by GitHub

The option property is where the query configuration is passed to the Query widget. This options prop is a QueryOptions instance. The QueryOptions class exposes properties that we use to configure the Query widget.

The query string or the query to be conducted by the Query widget is set or sent in via the document property. We passed in the readCounters string here:

final String readCounters = """query readCounters(\$counterId: Int!) {   counter {       name       id   }}""";
view raw .dart hosted with ❤ by GitHub

The variables attribute is used to send query variables to the Query widget. There is a 'counterId': 23 there. In the readCounters query string, this will be passed in place of $counterId.

The pollInterval specifies how often the Query widget polls or refreshes the query data. The timer is set to 10 seconds, so the Query widget will perform HTTP requests to refresh the query data every 10 seconds.

builder

A function is the builder property. When the Query widget sends an HTTP request to the GraphQL server endpoint, this function is called. The Query widget calls the builder function with the data from the query, a function to re-fetch the data, and a function for pagination. This is used to get more information.

The builder function returns widgets that are listed below the Query widget. The result argument is a QueryResult instance. The QueryResult class has properties that can be used to determine the query's current state and the data returned by the Query widget.

  • If the query encounters an error, QueryResult.hasException is set.
  • If the query is still in progress, QueryResult.isLoading is set. We can use this property to show our users a UI progress bar to let them know that something is on its way.
  • The data returned by the GraphQL endpoint is stored in QueryResult.data.

Mutations

Let's look at how to make mutation queries with the Mutation widget in graphql_flutter.

The Mutation widget is used as follows:

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')); },);
view raw .dart hosted with ❤ by GitHub

The Mutation widget, like the Query widget, accepts some properties.

  • options is a MutationOptions class instance. This is the location of the mutation string and other configurations.
  • The mutation string is set using a document. An addCounter mutation has been passed to the document in this case. The Mutation widget will handle it.
  • When we want to update the cache, we call update. The update function receives the previous cache (cache) and the outcome of the mutation. Anything returned by the update becomes the cache's new value. Based on the results, we're refreshing the cache.
  • When the mutations on the GraphQL endpoint have been called, onCompleted is called. The onCompleted function is then called with the mutation result builder to return the widget from the Mutation widget tree. This function is invoked with a RunMutation instance, runMutation, and a QueryResult instance result.
  • The Mutation widget's mutation is executed using runMutation. The Mutation widget causes the mutation whenever it is called. The mutation variables are passed as parameters to the runMutation function. The runMutation function is invoked with the counterId variable, 21.

When the Mutation's mutation is finished, the builder is called, and the Mutation rebuilds its tree. runMutation and the mutation result are passed to the builder function.

Subscriptions

Subscriptions in GraphQL are similar to an event system that listens on a WebSocket and calls a function whenever an event is emitted into the stream.

The client connects to the GraphQL server via a WebSocket. The event is passed to the WebSocket whenever the server emits an event from its end. So this is happening in real-time.

The graphql_flutter plugin in Flutter uses WebSockets and Dart streams to open and receive real-time updates from the server.

Let's look at how we can use our Flutter app's Subscription widget to create a real-time connection. We'll start by creating our subscription string:

final counterSubscription = '''subscription counterAdded {   counterAdded {       name       id   }}''';
view raw .dart hosted with ❤ by GitHub

When we add a new counter to our GraphQL server, this subscription will notify us in real-time.

Subscription(   options: SubscriptionOptions(     document: gql(counterSubscription),   ),   builder: (result) {     if (result.hasException) {       return Text("Error occurred: " + result.exception.toString());     }‍     if (result.isLoading) {       return Center(         child: const CircularProgressIndicator(),       );     }‍     return ResultAccumulator.appendUniqueEntries(         latest: result.data,         builder: (context, {results}) => ...     );   }),
view raw .dart hosted with ❤ by GitHub

The Subscription widget has several properties, as we can see:

  • options holds the Subscription widget's configuration.
  • document holds the subscription string.
  • builder returns the Subscription widget's widget tree.

The subscription result is used to call the builder function. The end result has the following properties:

  • If the Subscription widget encounters an error while polling the GraphQL server for updates, result.hasException is set.
  • If polling from the server is active, result.isLoading is set.

The provided helper widget ResultAccumulator is used to collect subscription results, according to graphql_flutter's pub.dev page.

Conclusion

This blog intends to help you understand what makes GraphQL so powerful, how to use it in Flutter, and how to take advantage of the reactive nature of graphql_flutter. You can now take the first steps in building your applications with GraphQL!

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

Implementing GraphQL with Flutter: Everything you need to know

Thinking about using GraphQL but unsure where to start? 

This is a concise tutorial based on our experience using GraphQL. You will learn how to use GraphQL in a Flutter app, including how to create a query, a mutation, and a subscription using the graphql_flutter plugin. Once you've mastered the fundamentals, you can move on to designing your own workflow.

Key topics and takeaways:

* GraphQL

* What is graphql_flutter?

* Setting up graphql_flutter and GraphQLProvider

* Queries

* Mutations

* Subscriptions

GraphQL

Looking to call multiple endpoints to populate data for a single screen? Wish you had more control over the data returned by the endpoint? Is it possible to get more data with a single endpoint call, or does the call only return the necessary data fields?

Follow along to learn how to do this with GraphQL. GraphQL’s goal was to change the way data is supplied from the backend, and it allows you to specify the data structure you want.

Let's imagine that we have the table model in our database that looks like this:

Movie {

 title

 genre

 rating

 year

}

These fields represent the properties of the Movie Model:

  • title property is the name of the Movie,
  • genre describes what kind of movie
  • rating represents viewers interests
  • year states when it is released

We can get movies like this using REST:

/GET localhost:8080/movies
view raw .txt hosted with ❤ by GitHub

[
{
"title": "The Godfather",
"genre": "Drama",
"rating": 9.2,
"year": 1972
}
]
view raw .json hosted with ❤ by GitHub

As you can see, whether or not we need them, REST returns all of the properties of each movie. In our frontend, we may just need the title and genre properties, yet all of them were returned.

We can avoid redundancy by using GraphQL. We can specify the properties we wish to be returned using GraphQL, for example:

query movies { Movie {   title   genre }}
view raw .dart hosted with ❤ by GitHub

We're informing the server that we only require the movie table's title and genre properties. It provides us with exactly what we require:

{
"data": [
{
"title": "The Godfather",
"genre": "Drama"
}
]
}
view raw .json hosted with ❤ by GitHub

GraphQL is a backend technology, whereas Flutter is a frontend SDK for developing mobile apps. We get the data displayed on the mobile app from a backend when we use mobile apps.

It's simple to create a Flutter app that retrieves data from a GraphQL backend. Simply make an HTTP request from the Flutter app, then use the returned data to set up and display the UI.

The new graphql_flutter plugin includes APIs and widgets for retrieving and using data from GraphQL backends.

What is graphql_flutter?

The new graphql_flutter plugin includes APIs and widgets that make it simple to retrieve and use data from a GraphQL backend.

graphql_flutter, as the name suggests, is a GraphQL client for Flutter. It exports widgets and providers for retrieving data from GraphQL backends, such as:

  • HttpLink — This is used to specify the backend's endpoint or URL.
  • GraphQLClient — This class is used to retrieve a query or mutation from a GraphQL endpoint as well as to connect to a GraphQL server.
  • GraphQLCache — We use this class to cache our queries and mutations. It has an options store where we pass the type of store to it during its caching operation.
  • GraphQLProvider — This widget encapsulates the graphql flutter widgets, allowing them to perform queries and mutations. This widget is given to the GraphQL client to use. All widgets in this provider's tree have access to this client.
  • Query — This widget is used to perform a backend GraphQL query.
  • Mutation — This widget is used to modify a GraphQL backend.
  • Subscription — This widget allows you to create a subscription.

Setting up graphql_flutter and GraphQLProvider

Create a Flutter project:

flutter create flutter_graphqlcd flutter_graphql
view raw .sh hosted with ❤ by GitHub

Next, install the graphql_flutter package:

flutter pub add graphql_flutter
view raw .sh hosted with ❤ by GitHub

The code above will set up the graphql_flutter package. This will include the graphql_flutter package in the dependencies section of your pubspec.yaml file:

dependencies:graphql_flutter: ^5.0.0
view raw .yaml hosted with ❤ by GitHub

To use the widgets, we must import the package as follows:

import 'package:graphql_flutter/graphql_flutter.dart';
view raw .dart hosted with ❤ by GitHub

Before we can start making GraphQL queries and mutations, we must first wrap our root widget in GraphQLProvider. A GraphQLClient instance must be provided to the GraphQLProvider's client property.

GraphQLProvider( client: GraphQLClient(...))
view raw .dart hosted with ❤ by GitHub

The GraphQLClient includes the GraphQL server URL as well as a caching mechanism.

final httpLink = HttpLink(uri: "http://10.0.2.2:8000/");‍ValueNotifier<GraphQLClient> client = ValueNotifier( GraphQLClient(   cache: InMemoryCache(),   link: httpLink ));
view raw .dart hosted with ❤ by GitHub

HttpLink is used to generate the URL for the GraphQL server. The GraphQLClient receives the instance of the HttpLink in the form of a link property, which contains the URL of the GraphQL endpoint.

The cache passed to GraphQLClient specifies the cache mechanism to be used. To persist or store caches, the InMemoryCache instance makes use of an in-memory database.

A GraphQLClient instance is passed to a ValueNotifier. This ValueNotifer holds a single value and has listeners that notify it when that value changes. This is used by graphql_flutter to notify its widgets when the data from a GraphQL endpoint changes, which helps graphql_flutter remain responsive.

We'll now encase our MaterialApp widget in 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'),       )); }}
view raw .dart hosted with ❤ by GitHub

Queries

We'll use the Query widget to create a query with the graphql_flutter package.

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']);           });     },) }}
view raw .dart hosted with ❤ by GitHub

The Query widget encloses the ListView widget, which will display the list of counters to be retrieved from our GraphQL server. As a result, the Query widget must wrap the widget where the data fetched by the Query widget is to be displayed.

The Query widget cannot be the tree's topmost widget. It can be placed wherever you want as long as the widget that will use its data is underneath or wrapped by it.

In addition, two properties have been passed to the Query widget: options and builder.

options

options: QueryOptions( document: gql(readCounters), variables: {   'conuterId': 23, }, pollInterval: Duration(seconds: 10),),
view raw .dart hosted with ❤ by GitHub

The option property is where the query configuration is passed to the Query widget. This options prop is a QueryOptions instance. The QueryOptions class exposes properties that we use to configure the Query widget.

The query string or the query to be conducted by the Query widget is set or sent in via the document property. We passed in the readCounters string here:

final String readCounters = """query readCounters(\$counterId: Int!) {   counter {       name       id   }}""";
view raw .dart hosted with ❤ by GitHub

The variables attribute is used to send query variables to the Query widget. There is a 'counterId': 23 there. In the readCounters query string, this will be passed in place of $counterId.

The pollInterval specifies how often the Query widget polls or refreshes the query data. The timer is set to 10 seconds, so the Query widget will perform HTTP requests to refresh the query data every 10 seconds.

builder

A function is the builder property. When the Query widget sends an HTTP request to the GraphQL server endpoint, this function is called. The Query widget calls the builder function with the data from the query, a function to re-fetch the data, and a function for pagination. This is used to get more information.

The builder function returns widgets that are listed below the Query widget. The result argument is a QueryResult instance. The QueryResult class has properties that can be used to determine the query's current state and the data returned by the Query widget.

  • If the query encounters an error, QueryResult.hasException is set.
  • If the query is still in progress, QueryResult.isLoading is set. We can use this property to show our users a UI progress bar to let them know that something is on its way.
  • The data returned by the GraphQL endpoint is stored in QueryResult.data.

Mutations

Let's look at how to make mutation queries with the Mutation widget in graphql_flutter.

The Mutation widget is used as follows:

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')); },);
view raw .dart hosted with ❤ by GitHub

The Mutation widget, like the Query widget, accepts some properties.

  • options is a MutationOptions class instance. This is the location of the mutation string and other configurations.
  • The mutation string is set using a document. An addCounter mutation has been passed to the document in this case. The Mutation widget will handle it.
  • When we want to update the cache, we call update. The update function receives the previous cache (cache) and the outcome of the mutation. Anything returned by the update becomes the cache's new value. Based on the results, we're refreshing the cache.
  • When the mutations on the GraphQL endpoint have been called, onCompleted is called. The onCompleted function is then called with the mutation result builder to return the widget from the Mutation widget tree. This function is invoked with a RunMutation instance, runMutation, and a QueryResult instance result.
  • The Mutation widget's mutation is executed using runMutation. The Mutation widget causes the mutation whenever it is called. The mutation variables are passed as parameters to the runMutation function. The runMutation function is invoked with the counterId variable, 21.

When the Mutation's mutation is finished, the builder is called, and the Mutation rebuilds its tree. runMutation and the mutation result are passed to the builder function.

Subscriptions

Subscriptions in GraphQL are similar to an event system that listens on a WebSocket and calls a function whenever an event is emitted into the stream.

The client connects to the GraphQL server via a WebSocket. The event is passed to the WebSocket whenever the server emits an event from its end. So this is happening in real-time.

The graphql_flutter plugin in Flutter uses WebSockets and Dart streams to open and receive real-time updates from the server.

Let's look at how we can use our Flutter app's Subscription widget to create a real-time connection. We'll start by creating our subscription string:

final counterSubscription = '''subscription counterAdded {   counterAdded {       name       id   }}''';
view raw .dart hosted with ❤ by GitHub

When we add a new counter to our GraphQL server, this subscription will notify us in real-time.

Subscription(   options: SubscriptionOptions(     document: gql(counterSubscription),   ),   builder: (result) {     if (result.hasException) {       return Text("Error occurred: " + result.exception.toString());     }‍     if (result.isLoading) {       return Center(         child: const CircularProgressIndicator(),       );     }‍     return ResultAccumulator.appendUniqueEntries(         latest: result.data,         builder: (context, {results}) => ...     );   }),
view raw .dart hosted with ❤ by GitHub

The Subscription widget has several properties, as we can see:

  • options holds the Subscription widget's configuration.
  • document holds the subscription string.
  • builder returns the Subscription widget's widget tree.

The subscription result is used to call the builder function. The end result has the following properties:

  • If the Subscription widget encounters an error while polling the GraphQL server for updates, result.hasException is set.
  • If polling from the server is active, result.isLoading is set.

The provided helper widget ResultAccumulator is used to collect subscription results, according to graphql_flutter's pub.dev page.

Conclusion

This blog intends to help you understand what makes GraphQL so powerful, how to use it in Flutter, and how to take advantage of the reactive nature of graphql_flutter. You can now take the first steps in building your applications with GraphQL!

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings