Integrate FlutterFlow with Google Drive API v3 using OAuth to access users' personal Drive. Set up a Cloud Function to handle the OAuth token exchange and file uploads. Use the API Group https://www.googleapis.com/drive/v3/ with a Bearer token for files.list, files.get, and files.delete operations. Always request the drive.file scope (app-created files only) rather than full drive access.
Access users' Google Drive files from FlutterFlow with OAuth and Drive API v3
Google Drive integration lets your FlutterFlow app read, upload, and manage files in a user's personal Google Drive — useful for document management apps, backup features, file-sharing workflows, and any app where users already store their data in Drive. Unlike Firebase Storage (which stores files in your app's bucket), Google Drive stores files in the user's own Google account. This requires OAuth authorization — the user grants your app specific permission to access their Drive. This tutorial walks through implementing the OAuth flow with the google_sign_in package, setting up the Drive API v3 endpoints in FlutterFlow, building a file browser ListView, handling uploads through a Cloud Function, and downloading files with Launch URL.
Prerequisites
- A FlutterFlow project with Firebase/Firestore connected (for storing file metadata)
- A Google Cloud project with the Google Drive API enabled (console.cloud.google.com → APIs & Services → Enable APIs)
- OAuth 2.0 credentials configured in Google Cloud Console for your app's bundle IDs
- FlutterFlow Pro plan for Custom Actions (required for OAuth flow)
- Basic understanding of Cloud Functions for the file upload step
Step-by-step guide
Enable Google Drive API and configure OAuth credentials
Enable Google Drive API and configure OAuth credentials
In Google Cloud Console (console.cloud.google.com), open your Firebase project (the same one linked to FlutterFlow). Go to APIs & Services → Library and search for Google Drive API — click Enable. Then go to APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client IDs. Create one client for iOS (enter your iOS bundle ID from FlutterFlow Settings → App Details) and one for Android (enter your package name and the SHA-1 fingerprint from your keystore). Download the GoogleService-Info.plist updates if prompted. Back in FlutterFlow, the google_sign_in package is already included when Firebase is connected — you do not need to add it to Pubspec Dependencies. Configure the OAuth consent screen: APIs & Services → OAuth consent screen, set the scopes to https://www.googleapis.com/auth/drive.file (app-created files) or https://www.googleapis.com/auth/drive.readonly (read-only browsing).
Expected result: Drive API is enabled, OAuth credentials created for both iOS and Android, and consent screen configured with the correct scope.
Implement the Google OAuth sign-in flow with google_sign_in
Implement the Google OAuth sign-in flow with google_sign_in
Create a Custom Action named signInWithGoogleDrive. In the Dart code: import 'package:google_sign_in/google_sign_in.dart'; then create a GoogleSignIn instance with the drive.file scope: final googleSignIn = GoogleSignIn(scopes: ['https://www.googleapis.com/auth/drive.file']); Call final account = await googleSignIn.signIn(); then final auth = await account!.authentication; Store auth.accessToken in FlutterFlow App State as a String named driveAccessToken. Also store account.email as driveUserEmail. Create a second Custom Action signOutGoogleDrive that calls googleSignIn.signOut() and clears the App State variables. Add a Sign in with Google button on your Drive integration page — On Tap calls signInWithGoogleDrive. Show a Conditional Visibility container with the file browser only when driveAccessToken is not empty.
1// Custom Action: signInWithGoogleDrive2import 'package:google_sign_in/google_sign_in.dart';34Future signInWithGoogleDrive() async {5 final googleSignIn = GoogleSignIn(6 scopes: [7 'https://www.googleapis.com/auth/drive.file',8 ],9 );1011 try {12 final account = await googleSignIn.signIn();13 if (account == null) return; // User cancelled1415 final auth = await account.authentication;16 final accessToken = auth.accessToken;1718 if (accessToken != null) {19 // Store in App State20 FFAppState().driveAccessToken = accessToken;21 FFAppState().driveUserEmail = account.email;22 }23 } catch (e) {24 print('Google Drive sign in error: $e');25 }26}2728// Custom Action: signOutGoogleDrive29Future signOutGoogleDrive() async {30 final googleSignIn = GoogleSignIn();31 await googleSignIn.signOut();32 FFAppState().driveAccessToken = '';33 FFAppState().driveUserEmail = '';34 FFAppState().driveFiles = [];35}Expected result: Users can sign in with their Google account and grant Drive access. The access token is stored in App State for API calls.
Configure the Drive API v3 API Group and list files endpoint
Configure the Drive API v3 API Group and list files endpoint
In FlutterFlow → API Manager → Add API Group, name it GoogleDriveAPI. Set the Base URL to https://www.googleapis.com/drive/v3. Under Headers, add Authorization with value Bearer [driveAccessToken] — use Set from Variable to reference the App State driveAccessToken variable. Add an endpoint named listFiles with Method GET and path /files. Add query parameters: q (file filter, e.g., trashed=false), fields (fields: 'files(id,name,mimeType,size,modifiedTime,webContentLink,webViewLink)'), orderBy (modifiedTime desc), pageSize (20). Leave the q parameter empty to list all non-trashed files, or set it to mimeType='application/vnd.google-apps.folder' to list only folders. Test the endpoint — it should return a files array. Create a listFilesByType endpoint with q parameter set to mimeType contains 'image' for image files, or mimeType='application/pdf' for PDFs.
Expected result: API Group is configured with Bearer token auth. listFiles endpoint returns the user's Drive files with metadata.
Build the file browser ListView with type icons
Build the file browser ListView with type icons
Create a DriveFileBrowser page. Add a Backend Query using the listFiles API endpoint, passing the driveAccessToken from App State as the Authorization header variable. Add a ListView bound to the query result's files array. Create a DriveFileRow Component with these elements: a Container (40×40, rounded, colored by file type) with an Icon inside — use Conditional Value to pick the icon based on mimeType: Icons.folder for vnd.google-apps.folder, Icons.image for image/, Icons.picture_as_pdf for application/pdf, Icons.description for vnd.google-apps.document, Icons.table_chart for vnd.google-apps.spreadsheet, Icons.insert_drive_file for everything else. Next to the icon Container: a Column with Text for name (bold, maxLines: 1, overflow: ellipsis) and a Row with Text for the formatted file size (Custom Function: bytes to KB/MB) and Text for modifiedTime (formatted with Custom Function). On the right: an IconButton (Icons.download) that triggers a Launch URL action with webContentLink. Add a Row at the top with a Search TextField — bind the q parameter to contain the search query.
Expected result: File browser displays the user's Drive files with type-appropriate icons, file size, modification date, and a download button.
Upload files to Google Drive via Cloud Function
Upload files to Google Drive via Cloud Function
Google Drive's multipart upload requires sending file content and metadata in a single request — this is difficult to do correctly from FlutterFlow's API Manager and requires handling multipart/form-data boundaries. Use a Cloud Function instead. Create a Cloud Function named uploadToDrive that accepts multipart form data: POST with fields accessToken (the user's Drive token), fileName, mimeType, and the file binary. The Cloud Function uses the googleapis Node.js library to authenticate with the user's token and call drive.files.create with the multipart upload. In FlutterFlow, add a UploadButton widget (FlutterFlowUploadButton) on the DriveFileBrowser page. After the upload completes and you have the file bytes and name, call the uploadToDrive Cloud Function endpoint via an API Call action in the Action Flow, passing the accessToken from App State and the uploaded file data.
1// Cloud Function: uploadToDrive (Node.js 20)2const functions = require('firebase-functions');3const { google } = require('googleapis');4const Busboy = require('busboy');56exports.uploadToDrive = functions.https.onRequest(async (req, res) => {7 res.set('Access-Control-Allow-Origin', '*');8 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }910 // Parse multipart form data11 const busboy = Busboy({ headers: req.headers });12 let accessToken = '';13 let fileName = 'upload';14 let mimeType = 'application/octet-stream';15 let fileBuffer = null;1617 busboy.on('field', (name, value) => {18 if (name === 'accessToken') accessToken = value;19 if (name === 'fileName') fileName = value;20 if (name === 'mimeType') mimeType = value;21 });2223 busboy.on('file', (name, file) => {24 const chunks = [];25 file.on('data', chunk => chunks.push(chunk));26 file.on('end', () => { fileBuffer = Buffer.concat(chunks); });27 });2829 busboy.on('finish', async () => {30 const auth = new google.auth.OAuth2();31 auth.setCredentials({ access_token: accessToken });32 const drive = google.drive({ version: 'v3', auth });3334 const response = await drive.files.create({35 requestBody: { name: fileName, mimeType },36 media: { mimeType, body: require('stream').Readable.from(fileBuffer) },37 fields: 'id, name, webViewLink',38 });3940 res.json({ success: true, file: response.data });41 });4243 busboy.end(req.rawBody);44});Expected result: Users can pick a file from their device in FlutterFlow and it uploads to their Google Drive. The file appears in the Drive file browser after refresh.
Complete working example
1OAuth Setup (one-time):2└── Google Cloud Console3 ├── Enable Google Drive API4 ├── OAuth 2.0 Credentials (iOS + Android)5 └── Consent Screen → Scope: drive.file67App State Variables:8├── driveAccessToken: String9├── driveUserEmail: String10└── driveFiles: List<JSON> (cache)1112Custom Actions:13├── signInWithGoogleDrive14│ └── GoogleSignIn(scopes: ['drive.file']).signIn()15│ └── Stores auth.accessToken → App State16└── signOutGoogleDrive17 └── googleSignIn.signOut() + clear App State1819API Group: GoogleDriveAPI20├── Base URL: https://www.googleapis.com/drive/v321├── Header: Authorization: Bearer [driveAccessToken]22├── listFiles: GET /files23│ ├── q: trashed=false (filter)24│ ├── fields: files(id,name,mimeType,size,modifiedTime,25│ │ webContentLink,webViewLink)26│ ├── orderBy: modifiedTime desc27│ └── pageSize: 2028└── deleteFile: DELETE /files/{fileId}2930DriveFileBrowser Page:31├── Conditional: Show sign-in button if driveAccessToken empty32├── SearchBar TextField → re-query with q filter33└── ListView (Backend Query: listFiles)34 └── DriveFileRow Component35 ├── Container (icon, colored by mimeType)36 │ └── Icon (Conditional Value by mimeType)37 ├── Column38 │ ├── Text: name (bold, ellipsis)39 │ └── Row: fileSize + modifiedDate40 └── IconButton: Download → Launch URL webContentLink4142Upload Flow:43├── FlutterFlowUploadButton → get file bytes + name44├── API Call: uploadToDrive Cloud Function45│ ├── accessToken (from App State)46│ ├── fileName47│ ├── mimeType48│ └── file bytes49└── Refresh file list after upload5051Cloud Function: uploadToDrive52└── Parses multipart form → drive.files.create()53 └── Returns { id, name, webViewLink }Common mistakes
Why it's a problem: Requesting the drive scope (full Google Drive access) instead of drive.file
How to avoid: Request drive.file for apps that only manage files they created, or drive.readonly for apps that only need to browse and download. If your app genuinely needs broader access, apply for verification at Google's OAuth verification process and provide a clear justification.
Why it's a problem: Making Drive API upload calls directly from FlutterFlow's API Manager using multipart upload
How to avoid: Handle all Drive file uploads through a Cloud Function that uses the googleapis Node.js library, which correctly constructs the multipart upload request. The FlutterFlow app sends the file bytes and metadata to the Cloud Function, which handles the Drive API call.
Why it's a problem: Not refreshing the OAuth access token when it expires
How to avoid: In your signInWithGoogleDrive Custom Action, check the token expiry by calling googleSignIn.isSignedIn() on app resume. If the token is expired or about to expire, call googleSignIn.signInSilently() to get a fresh token without showing the consent screen again. Store the refresh timestamp in App State.
Best practices
- Always request the minimum OAuth scope — use drive.file for app-created files only, not the full drive scope which triggers user distrust and Google verification requirements
- Handle the silent sign-in case: call googleSignIn.signInSilently() on app start so users who previously authorized do not see the sign-in button again
- Display a clear explanation of why your app needs Drive access before showing the OAuth consent screen — users are more likely to approve when they understand the benefit
- Use the webContentLink for direct file downloads and webViewLink to open files in Google Drive's viewer — webContentLink works for actual file downloads, webViewLink opens the Drive UI
- Cache the file list in App State and refresh only when the user explicitly pulls to refresh or uploads a new file — avoid re-fetching on every page navigation
- Store uploaded file IDs in Firestore linked to the user's account so your app can reference specific Drive files without needing to search by name
- Test on both iOS and Android — the google_sign_in OAuth flow has platform-specific behavior, especially around the consent screen appearance and account picker UI
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow app that needs to integrate with Google Drive API v3. Write me a Flutter Custom Action using the google_sign_in package that signs the user in with the drive.file OAuth scope and stores the access token in a variable. Also write a Firebase Cloud Function in Node.js that accepts an access token, file name, MIME type, and file bytes as multipart form data and uploads the file to the user's Google Drive using the googleapis library.
Create a file browser page connected to my GoogleDriveAPI API Group's listFiles endpoint. Display files in a ListView showing a type icon (different colors for folders, images, PDFs, and documents), the file name, file size in KB/MB, and last modified date. Add a download button that launches the webContentLink URL.
Frequently asked questions
What is the difference between Google Drive and Firebase Storage in FlutterFlow?
Firebase Storage stores files in your app's own Google Cloud bucket — it is your app's storage that you control and pay for. Google Drive stores files in the user's personal Google account — the user owns the files and they count against their Google storage quota, not yours. Use Firebase Storage for app-generated content (profile photos, user uploads that others see). Use Google Drive when users want to access their existing Drive files or sync with their personal storage.
Can I access files that the user did not create with my app using drive.file scope?
No. The drive.file scope only allows your app to see and manage files that were created or opened by your app. If you need to browse the user's entire Drive including pre-existing files, you need the drive.readonly scope (read-only) or full drive scope (read-write). Both require additional OAuth app verification from Google if you are publishing to production.
How do I show a folder structure instead of a flat file list?
Use the files.list endpoint with the query parameter q set to '{folderId}' in parents to list files in a specific folder. Start with the root folder by querying q=parents in 'root'. When the user taps a folder, pass its id as the new folder parent in the query. Store the navigation stack (array of {id, name} objects) in Page State to enable the breadcrumb back navigation. Use a Custom Action that updates the current folder ID in App State and triggers a re-fetch.
How do I handle file sharing from my FlutterFlow app?
Use the Drive permissions endpoint: POST /drive/v3/files/{fileId}/permissions with role: reader and type: anyone to make a file publicly accessible, or type: user with emailAddress to share with a specific person. Call this via your Cloud Function using the user's access token. After creating the permission, the file's webViewLink becomes shareable. Add a share icon to each DriveFileRow that calls the Cloud Function and then shows the webViewLink in a copy-to-clipboard dialog.
What happens to the OAuth token when the user logs out of the FlutterFlow app?
Clearing the App State driveAccessToken removes the token from your app, but the OAuth authorization remains active in Google's system. Call googleSignIn.disconnect() (not just signOut) to fully revoke your app's access to the user's Drive. Use signOut for regular logouts (user can re-authorize silently), use disconnect when the user explicitly wants to revoke access or unlink their Google Drive account.
Can RapidDev help build a full Google Drive file management app in FlutterFlow?
Yes. A production-grade Drive integration with folder navigation, file preview, offline access, file sharing with permission management, and real-time Drive change notifications requires Cloud Functions and Custom Actions beyond a basic integration. RapidDev can build and maintain the full Drive integration for your app.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation