Flutter
GraphQL
Offline Database
11 min
implementing the offline first database using the graphql api introduction since you are here, you must have gone through the rest of the tutorials and are familiar with executing graphql queries and mutations to fetch and mutate the data in this docs, we are going to explore how to implement an offline first user interface with flutter and graphql goals understand internal architecture of the flutter graphql offline client allowing application to run graphql queries even though application is offline implement offline data persistence prerequisites we require that the user has some basic understanding of dart and flutter though not necessary, the graphql cookbook will be useful in understanding some of the graphql concepts we require that you have completed the prerequisite topic flutter graphql setup and have previous code setup and back4app backend implemented 1 setting up offline cache flutter graphql client supports “offline queries” by default, that is it will not throw errors if we query some graphql data when offline and would fetch the data from cache we have to note that this is different from persisting the cache across app sessions and specifically the flutter graphql client does not have cache persistence to disk so if the app closed from the system tray and reopened the data would still need to be fetched to enable the same we have to enable the offline cache go to main dart 1 return materialapp( 2 home graphqlprovider( 3 child cacheprovider( // cache provider widget that provides the offline queries support 4 child myhomepage(), 5 ), 6 client client, 7 ), 8 ); 2 setting up stored preferences one caveat while using the flutter graphql client is that it does not store any cache of its own when the application is closed, nor does it hydrate the cache when the application is opened again for implementing the same we would be leveraging the flutter shared prefrencces library it wraps platform specific persistent storage for simple data (nsuserdefaults on ios and macos, sharedpreferences on android, etc ), essentially allowing to store data offline in a very simple manner for installing the library please add in the pubspec yml https //github com/templates back4app/flutter graphql/blob/772c058c74d870798af1cce7a29a5046f9dda456/pubspec yaml#l34 file in main dart add the following 1 import 'package\ shared preferences/shared preferences dart'; 2 3 class sharedpreferenceshelper { 4 static final string offline cache key = 'programminglanguagelistresponse'; 5 6 static future\<programminglanguagelist> getcache() async { 7 final sharedpreferences prefs = await sharedpreferences getinstance(); 8 final cache = prefs getstring( offline cache key); 9 final offlinedata = 10 cache != null ? programminglanguagelistfromjson(cache) null; 11 12 return offlinedata; 13 } 14 15 static future\<bool> setcache(dynamic value) async { 16 final sharedpreferences prefs = await sharedpreferences getinstance(); 17 18 return prefs setstring( offline cache key, jsonencode(value)); 19 } 20 } shared preferences library stores data in a key value form where value gets stringified into a json string we will need to parse this data to our data model 3 parsing the locally stored data we will create a new file called programing languages model dart programing languages model dart which will store the parsing logic we will generate this logic by pasting our graphql response in the json to dart model converter at https //app quicktype io/ https //app quicktype io/ we will copy the generated code and create a file programing languages model dart programing languages model dart https //github com/templates back4app/flutter graphql/blob/flutter graphql offline/lib/programing languages model dart https //github com/templates back4app/flutter graphql/blob/flutter graphql offline/lib/programing languages model dart 4 integrating offline storage logic if the data does not exist we would be using the data from shared preferences if the data is also not in the shared preferences we would simply show a loading icon we will now implement changes to integrate all the changes together, in the build method of our myhomepagestate myhomepagestate we would change our build method we would use the futurebuilder futurebuilder widget to consume data from the sharedpreferenceshelper class 1 return futurebuilder\<programminglanguagelist>( 2 future sharedpreferenceshelper getcache(), 3 builder (prefs, snapshot) { 4 final offlinedata = snapshot data; 5 if (!snapshot haserror) { 6 return safearea( 7 … using the futurebuilder widget allows us to write code without having to use state it is a relatively quick process to get the data from shared preferences we could also show a loader while we are initialising the shared preferences and are getting data from an offline store we now use this offline data object and render while data from graphql is not available we will also refactor the code a little bit following will be our code for the query query https //github com/templates back4app/flutter graphql/blob/flutter graphql offline/lib/main dart widget 1 body query( 2 options queryoptions( 3 documentnode gql(query), 4 ), 5 builder ( 6 queryresult result, { 7 refetch refetch, 8 fetchmore fetchmore, 9 }) { 10 final data = result data == null 11 ? offlinedata 12 programminglanguagelistfromjson( 13 jsonencode(result data)); 14 if (data == null) { 15 return center( 16 child text( 17 "loading ", 18 style textstyle(fontsize 20 0), 19 )); 20 } else { 21 sharedpreferenceshelper setcache(data); 22 return listview\ builder( 23 itembuilder (buildcontext context, int index) { 24 if (index == 0) { 25 return center( 26 child raisedbutton( 27 onpressed refetch, 28 child result loading == true 29 ? text("loading ") 30 text("refetch"), 31 ), 32 ); 33 } 34 return listtile( 35 title text(data programminglanguages 36 edges\[index 1] node name), 37 trailing text(data programminglanguages 38 edges\[index 1] node stronglytyped 39 ? "strongly typed" 40 "weekly typed"), 41 ); 42 }, 43 itemcount data programminglanguages edges length + 1, 44 ); 45 } 46 }, 47 ), 48 ), we should get the following conclusion we are now able to ensure a very good mobile experience by storing the data offline and revalidating the data when the application gets connected to the internet also, one important aspect that is enhancing the user experience is that the flutter graphql client caches the old response and while sending a new request automatically because of which we don’t have to keep showing clumsy loading screens, while re fetching data the code for the article is available at https //github com/templates back4app/flutter graphql/tree/flutter graphql offline https //github com/templates back4app/flutter graphql/tree/flutter graphql offline