Feb 9, 2021

Accessing Google Services Using Flutter

I’ve been interested with Flutter for a couple of years and have written some simple apps to try it out. One of the apps has basic export functionality that just writes a file to the application directory, which can be hard to find for the average user. I started wondering if I could write the file out to Google Drive so then it could be saved out “in the cloud”.

I created a sample project here: https://github.com/brendonanderson/google_drive. After cloning this project, there is a fair amount of setup required to get it to work. The README in the repository goes into detail what to do and there are a number of tutorials online on how to create a Client ID for an application if you get stuck.

The high level steps are:

  1. Create a project in the Google Developer Console.
  2. Select the APIs you want to use (Google Drive in this case).
  3. Setup the OAuth consent screen.
  4. Create credentials (Client ID in this case).
  5. Add the credentials to the project.

For my first attempt at making the authentication part of the process work, I used a library called url_launcher. You give it the url to start the OAuth process, it opens it up in a browser, and you go through the consent process. However, once you have completed the consent process, there isn’t a way to have it redirect back to your app (at least on Android). I had to find another solution.

For my second attempt, I found the library flutter_web_auth. This had a much better experience. After the consent screen, it redirects the user back to the application. On Android it does this using an intent filter. See the AndroidManifest.xml in the repository for an example.

After receiving the tokens back, I used flutter_secure_storage to store them. This works like the shared_preferences library but encrypts the data. That way, after the user has been away from the app for some time, the app can use the refresh token provided to get a new access token and not have to go through the authentication process again. Note: Be sure to read about this library and its problems with automatic backups here and here if you decide to use this library in a production application.

The Dart library for interacting with Google services expects an AuthClient from the Google API Auth library to make the calls to those services. One of the implementations is AutoRefreshingAuthClient which handles an expired access token automatically by using the refresh token to get another access token. Here is an example on how to create the AutoRefreshingAuthClient:

//pull data out of secure storage
String data = await _storage.read(key: 'accessToken');
String type = await _storage.read(key: 'type');
String expiry = await _storage.read(key: 'expiry');
String refreshToken = await _storage.read(key: 'refreshToken');
//create the access token (even if it's expired)
AccessToken accessToken = AccessToken(type, data, DateTime.tryParse(expiry));
//use the refresh token here. Refresh tokens do not expire (for the most part).
AccessCredentials creds = await refreshCredentials(
     AccessCredentials(accessToken, refreshToken, scopes),  

http.Client c = http.Client();
//create the AutoRefreshingAuthClient using previous 
AuthClient authClient = autoRefreshingClient(_clientId, creds, c);

Once you have an AuthClient, you can use it to interact with Google services. Here I use it to create a file out on Google Drive:

//Use the AuthClient to create the class that interacts with 
//Google Drive
DriveApi driveApi = DriveApi(authClient);

//this is metadata about the file
File file = File();
file.description = 'Test File';
file.name = 'testfile.txt';

//create data in a stream to be written to the file
StreamController<List<int>> sc = StreamController();
List<int> data = utf8.encode('file contents');

Media media = Media(sc.stream, data.length);
//create the file with our data
await driveApi.files.create(file, uploadMedia: media);

At this point, a new file should be out in your Google Drive! Accessing other Google services should be similar to this as long as you have setup the correct scopes and API access in the Google Developer Console. Check out the repo to see how it all fits together.

About the Author

Brendon Anderson profile.

Brendon Anderson

Sr. Consultant

Brendon has over 15 years of software development experience at organizations large and small.  He craves learning new technologies and techniques and lives in and understands large enterprise application environments with complex software and hardware architectures.

Leave a Reply

Your email address will not be published.

Related Blog Posts
Building Better Data Visualization Experiences: Part 1 of 2
Through direct experience with data scientists, business analysts, lab technicians, as well as other UX professionals, I have found that we need a better understanding of the people who will be using our data visualization products in order to build them. Creating a product utilizing data with the goal of providing insight is fundamentally different from a typical user-centric web experience, although traditional UX process methods can help.
Kafka Schema Evolution With Java Spring Boot and Protobuf
In this blog I will be demonstrating Kafka schema evolution with Java, Spring Boot and Protobuf.  This app is for tutorial purposes, so there will be instances where a refactor could happen. I tried to […]
Redis Bitmaps: Storing state in small places
Redis is a popular open source in-memory data store that supports all kinds of abstract data structures. In this post and in an accompanying example Java project, I am going to explore two great use […]
Let’s build a WordPress & Kernel updated AMI with Packer
First, let’s start with What is an AMI? An Amazon Machine Image (AMI) is a master image for the creation of virtual servers in an AWS environment. The machine images are like templates that are configured with […]