Flutter: How to backup user data on Google drive like WhatsApp does?

Viewed 2904

I want to to backup data in Backups on user Google Drive account and restore them.

I have seen apps, like WhatsApp that allow users to login through google drive and do periodic backup to the user cloud.

I don't want to use firebase cloud since the data is access by the user himself and not other users and it will be costly if the data is large. Is there any available package can do this? Or tutorial otherwise how to achieve this in flutter?

2 Answers

Step 1

You need to have already created a Google Firebase Project, and enable Google Drive API from Google Developer Console. Note that you need to select the same project in Google Developer Console as you have created in Google Firebase.

Step 2

you need to log in with google to get googleSignInAccount and use dependencies

 googleapis: ^0.54.0  
 googleapis_auth: ^0.2.11 



class GoogleHttpClient extends IOClient {  
 Map<String, String> _headers;  
 GoogleHttpClient(this._headers) : super();  
 @override  
 Future<http.StreamedResponse> send(http.BaseRequest request) =>  
     super.send(request..headers.addAll(_headers));  
 @override  
 Future<http.Response> head(Object url, {Map<String, String> headers}) =>  
     super.head(url, headers: headers..addAll(_headers));  
}

Upload File to Google Drive

_uploadFileToGoogleDrive() async {  
   var client = GoogleHttpClient(await googleSignInAccount.authHeaders);  
   var drive = ga.DriveApi(client);  
   ga.File fileToUpload = ga.File();  
   var file = await FilePicker.getFile();  
   fileToUpload.parents = ["appDataFolder"];  
   fileToUpload.name = path.basename(file.absolute.path);  
   var response = await drive.files.create(  
     fileToUpload,  
     uploadMedia: ga.Media(file.openRead(), file.lengthSync()),  
   );  
   print(response);  
 } 

Download Google Drive File

Future<void> _downloadGoogleDriveFile(String fName, String gdID) async {  
   var client = GoogleHttpClient(await googleSignInAccount.authHeaders);  
   var drive = ga.DriveApi(client);  
   ga.Media file = await drive.files  
       .get(gdID, downloadOptions: ga.DownloadOptions.FullMedia);  
   print(file.stream);  
   
   final directory = await getExternalStorageDirectory();  
   print(directory.path);  
   final saveFile = File('${directory.path}/${new DateTime.now().millisecondsSinceEpoch}$fName');  
   List<int> dataStore = [];  
   file.stream.listen((data) {  
     print("DataReceived: ${data.length}");  
     dataStore.insertAll(dataStore.length, data);  
   }, onDone: () {  
     print("Task Done");  
     saveFile.writeAsBytes(dataStore);  
     print("File saved at ${saveFile.path}");  
   }, onError: (error) {  
     print("Some Error");  
   });  
 } 

Its easy but you have to go through a series of Steps to complete it.

1. Enable Google Drive API

click on the link to google console : https://console.developers.google.com/apis/library?project=diary-app-339509

  • Create a new project and enable Drive API on the project.
  • Create an OAuth client Id for android and IOS
  • You will have to fill a lot of details to create it , read through the page and fill it
  • copy Client ID for future use

create project and enable API

Create OAuth client id

form to fill to make OAuth client id

2. Make project available for external use .

for testing add test user email address

Now comes the coding part on flutter

3. Add the following packages to pubspec.yaml

  googleapis: ^7.0.0
  googleapis_auth: ^1.3.0
  flutter_secure_storage: ^5.0.2
  url_launcher: ^6.0.0-nullsafety

4. Get authentication from google and store the Auth details for future auths

Use the following class for storing Auth Details

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:googleapis_auth/auth_io.dart';

class SecureStorage {
  final storage = FlutterSecureStorage();

  //Save Credentials
  Future saveCredentials(AccessToken token, String refreshToken) async {
    print(token.expiry.toIso8601String());
    await storage.write(key: "type", value: token.type);
    await storage.write(key: "data", value: token.data);
    await storage.write(key: "expiry", value: token.expiry.toString());
    await storage.write(key: "refreshToken", value: refreshToken);
  }

  //Get Saved Credentials
  Future<Map<String, dynamic>?> getCredentials() async {
    var result = await storage.readAll();
    if (result.isEmpty) return null;
    return result;
  }

  //Clear Saved Credentials
  Future clear() {
    return storage.deleteAll();
  }
}

5.Finally use this class for saving your file to Drive

   import 'dart:io';
    import 'package:googleapis/drive/v3.dart' as ga;
    import 'package:googleapis_auth/auth_io.dart';
    import 'package:http/http.dart' as http;
    import 'package:path/path.dart' as p;
    import 'package:path_provider/path_provider.dart';
    import 'package:personal_diary/app/utils/secure_storage.dart';
    import 'package:url_launcher/url_launcher.dart';
    
    const _clientId = "379503391554-6d33j9e6lan7lg0ac7sj81026u8nigs8.apps.googleusercontent.com";
    const _scopes = ['https://www.googleapis.com/auth/drive.file'];
    
    class GoogleDrive {
      final storage = SecureStorage();
      //Get Authenticated Http Client
      Future<http.Client> getHttpClient() async {
        //Get Credentials
        var credentials = await storage.getCredentials();
        if (credentials == null) {
          //Needs user authentication
          var authClient = await clientViaUserConsent(
              ClientId(_clientId),_scopes, (url) {
            //Open Url in Browser
            launch(url);
          });
          //Save Credentials
          await storage.saveCredentials(authClient.credentials.accessToken,
              authClient.credentials.refreshToken!);
          return authClient;
        } else {
          print(credentials["expiry"]);
          //Already authenticated
          return authenticatedClient(
              http.Client(),
              AccessCredentials(
                  AccessToken(credentials["type"], credentials["data"],
                      DateTime.tryParse(credentials["expiry"])!),
                  credentials["refreshToken"],
                  _scopes));
        }
      }
    
    // check if the directory forlder is already available in drive , if available return its id
    // if not available create a folder in drive and return id
    //   if not able to create id then it means user authetication has failed
      Future<String?> _getFolderId(ga.DriveApi driveApi) async {
        final mimeType = "application/vnd.google-apps.folder";
        String folderName = "personalDiaryBackup";
    
        try {
          final found = await driveApi.files.list(
            q: "mimeType = '$mimeType' and name = '$folderName'",
            $fields: "files(id, name)",
          );
          final files = found.files;
          if (files == null) {
            print("Sign-in first Error");
            return null;
          }
    
          // The folder already exists
          if (files.isNotEmpty) {
            return files.first.id;
          }
    
          // Create a folder
          ga.File folder = ga.File();
          folder.name = folderName;
          folder.mimeType = mimeType;
          final folderCreation = await driveApi.files.create(folder);
          print("Folder ID: ${folderCreation.id}");
    
          return folderCreation.id;
        } catch (e) {
          print(e);
          return null;
        }
      }
    
    
      uploadFileToGoogleDrive(File file) async {
        var client = await getHttpClient();
        var drive = ga.DriveApi(client);
        String? folderId =  await _getFolderId(drive);
        if(folderId == null){
          print("Sign-in first Error");
        }else {
          ga.File fileToUpload = ga.File();
          fileToUpload.parents = [folderId];
          fileToUpload.name = p.basename(file.absolute.path);
          var response = await drive.files.create(
            fileToUpload,
            uploadMedia: ga.Media(file.openRead(), file.lengthSync()),
          );
          print(response);
        }
    
      }
    
      Future<void> _downloadGoogleDriveFile(String fName, String gdID) async {
        var client = await getHttpClient();
        var drive = ga.DriveApi(client);
        ga.Media file = await drive.files.get(gdID, downloadOptions: ga.DownloadOptions.FullMedia);
        print(file.stream);
    
        final directory = await getExternalStorageDirectory();
        print(directory);
        final saveFile = File('$directory/${DateTime.now().millisecondsSinceEpoch}$fName');
        List<int> dataStore = [];
        file.stream.listen((data) {
          print("DataReceived: ${data.length}");
          dataStore.insertAll(dataStore.length, data);
        }, onDone: () {
          print("Task Done");
          saveFile.writeAsBytes(dataStore);
          print("File saved at ${saveFile.path}");
        }, onError: (error) {
          print("Some Error");
        });
      }
    
    }
Related