# Dropbox

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to Dropbox by creating two API Groups — one for the RPC metadata host (api.dropboxapi.com) and one for the content upload host (content.dropboxapi.com). Dropbox v2 breaks the usual REST pattern: nearly every call is a POST with parameters in the JSON body, not the URL. The root path is an empty string, not a slash. A long-lived access token powers both groups.

## Dropbox in FlutterFlow: two API hosts, POST-only calls, and the empty-string root

Dropbox v2 breaks every expectation you have about REST APIs. Instead of GET /files?path=/Documents, every metadata operation is a POST with parameters inside the JSON body. Want to list a folder? POST to /2/files/list_folder with {"path":""}. Want to create a shared link? POST to /2/sharing/create_shared_link_with_settings with {"path":"/myfile.pdf"}. The URL path identifies the operation; the JSON body carries the arguments.

There is a second curveball: Dropbox splits its operations across two API hosts. Metadata calls — list, move, create folder, share — go to api.dropboxapi.com. File content operations — upload, download — go to content.dropboxapi.com. In FlutterFlow this means two separate API Groups, each with its own base URL and its own Authorization header bearing the same Bearer token. Mixing up the hosts is a fast path to mysterious 404 errors.

Dropbox's free plan gives 2 GB of storage. Dropbox Plus is approximately $11.99/month for 2 TB; Business plans start around $15/user/month (verify current pricing at dropbox.com/plans). Rate limits are per-app and per-user; Dropbox returns 429 with a Retry-After header when you exceed them — check current limits in the Dropbox API documentation. For anything beyond a personal demo app, generate short-lived tokens via a backend proxy rather than embedding a long-lived token in your compiled Flutter app.

## Before you start

- A Dropbox account (free at dropbox.com)
- A Dropbox App created at dropbox.com/developers/apps (choose Scoped access, App folder or Full Dropbox)
- A FlutterFlow project (API Calls available on all plans)
- A generated access token from the Dropbox App Console (for development) or a backend proxy for production
- Basic familiarity with FlutterFlow's API Calls panel and Action Flow Editor

## Step-by-step guide

### 1. Create a Dropbox App and generate an access token

Go to dropbox.com/developers/apps in your browser and click Create app. Choose Scoped access for the API type. For access type, choose Full Dropbox if your app needs to read or write anywhere in the user's Dropbox; choose App folder if you want to confine the app to a single dedicated folder (safer and simpler). Give the app a name and click Create app.

On the app's Settings page, scroll to the OAuth 2 section. Under Generated access token, click Generate to create a long-lived token for development. Copy this token — it is equivalent to a password and can read and modify your entire Dropbox (or your App folder, depending on the access type you chose).

In the Permissions tab, enable the specific permission scopes your app needs: files.metadata.read and files.metadata.write (to list and manage files), files.content.read and files.content.write (to download and upload), and sharing.write (to create shared links). Click Submit to save the permissions. Note: if you regenerate the token after changing permissions, the new token will reflect the updated scopes.

For production, a long-lived token embedded in a compiled Flutter app is extractable from the binary. For production use, implement OAuth 2.0 with short-lived tokens minted by a Firebase Cloud Function or Supabase Edge Function — the proxy holds your app secret and issues user-specific tokens. For this tutorial, the generated token is sufficient to wire up and test the integration.

**Expected result:** You have a Dropbox App with the required permission scopes enabled and a generated access token ready for use in FlutterFlow.

### 2. Create the DropboxRPC API Group for metadata operations

In the FlutterFlow left nav, click API Calls → + Add → Create API Group. Name the group DropboxRPC. Set the base URL to https://api.dropboxapi.com. In the Headers tab, add these two headers:
- Key: Authorization — Value: Bearer [token] (bind to an App State variable named dropboxToken)
- Key: Content-Type — Value: application/json

Now add your first API Call inside DropboxRPC. Click DropboxRPC → + Add API Call. Name it listFolder. Set the method to POST and the endpoint path to /2/files/list_folder. In the Request Body tab, set the body type to JSON and enter: {"path":"","recursive":false}. The empty string path lists the root of the Dropbox (or App folder). This is the most common gotcha — sending "/" instead of "" causes a 409 Conflict error from the Dropbox API.

In the Response & Test tab, click Test API Call. Dropbox returns a JSON object with an entries array of file and folder metadata. Click Generate JSON Path to create paths like $.entries[*].name, $.entries[*].path_lower, $.entries[*].id, and $.entries[*]['.tag'] (which is either file or folder). You will use these to populate a ListView.

Add a second API Call named createSharedLink. Method = POST, endpoint = /2/sharing/create_shared_link_with_settings. Body = {"path":"{{filePath}}","settings":{"requested_visibility":"public"}}. Add a filePath variable (String) in the Variables tab. In the response, the JSON path $.url gives you the shareable link.

```
// DropboxRPC API Group — reference config
{
  "base_url": "https://api.dropboxapi.com",
  "headers": {
    "Authorization": "Bearer {{dropboxToken}}",
    "Content-Type": "application/json"
  },
  "calls": [
    {
      "name": "listFolder",
      "method": "POST",
      "endpoint": "/2/files/list_folder",
      "body": { "path": "", "recursive": false },
      "note": "Root path is empty string, NOT slash"
    },
    {
      "name": "createSharedLink",
      "method": "POST",
      "endpoint": "/2/sharing/create_shared_link_with_settings",
      "body": {
        "path": "{{filePath}}",
        "settings": { "requested_visibility": "public" }
      }
    }
  ]
}
```

**Expected result:** A DropboxRPC API Group with listFolder and createSharedLink endpoints appears in the API Calls panel. Running a test returns the list of files in your Dropbox root.

### 3. Create the DropboxContent API Group for file upload

File content operations use a completely different host: content.dropboxapi.com. Create a second API Group in FlutterFlow: click API Calls → + Add → Create API Group. Name it DropboxContent. Set the base URL to https://content.dropboxapi.com. Add the same Authorization header (Bearer {{dropboxToken}} bound to App State).

Add one API Call named uploadFile. Set the method to POST and the endpoint to /2/files/upload. This call requires a special header instead of a JSON body: the Dropbox-API-Arg header carries the upload parameters as a JSON string, while the raw request body carries the file bytes.

In the Headers tab of this API Call, add:
- Key: Dropbox-API-Arg — Value: {"path":"/{{destinationPath}}","mode":"overwrite","autorename":true,"mute":false}
- Key: Content-Type — Value: application/octet-stream

In the Variables tab, add a destinationPath variable (String) for the target file path including the filename, for example /uploads/photo.jpg. The file bytes go in the binary request body.

This two-header pattern — metadata in Dropbox-API-Arg, bytes in the body — is unique to Dropbox and different from any other storage API. If you put upload parameters in the JSON body (as you would for a normal REST API), Dropbox ignores them and the upload fails silently or with an error.

```
// DropboxContent API Group — upload reference
{
  "base_url": "https://content.dropboxapi.com",
  "headers": {
    "Authorization": "Bearer {{dropboxToken}}"
  },
  "calls": [
    {
      "name": "uploadFile",
      "method": "POST",
      "endpoint": "/2/files/upload",
      "headers": {
        "Content-Type": "application/octet-stream",
        "Dropbox-API-Arg": "{\"path\":\"/{{destinationPath}}\",\"mode\":\"overwrite\",\"autorename\":true,\"mute\":false}"
      },
      "body_type": "binary"
    }
  ]
}
```

**Expected result:** A DropboxContent API Group with an uploadFile endpoint appears. The Dropbox-API-Arg header carries upload metadata while the binary body carries the file bytes.

### 4. Store the access token in App State and bind it to both API Groups

In FlutterFlow, navigate to App State in the left nav. Create a String variable named dropboxToken with a default value of empty. During development, you can temporarily set the default value to your generated access token to test API calls without building an auth flow. In production, this variable is populated by your OAuth proxy or by a manual sign-in flow.

Now go back to both API Groups — DropboxRPC and DropboxContent — and confirm that the Authorization header value uses {{dropboxToken}} as a variable reference (the chip icon), not a literal string. If you pasted the token directly into the header value as plain text, it will work for your account during development but will break for any other user. The variable binding ensures the header uses whoever's token is currently stored.

Create an App State variable named dropboxIsAuthenticated (Boolean, default false). Set this to true after a successful token validation call. On your home screen, use a Conditional Widget to show either the Dropbox sign-in button (when dropboxIsAuthenticated is false) or the file browser (when true).

For a minimal sign-in flow without a full backend proxy, you can redirect users to the Dropbox OAuth authorization URL in a WebView and capture the token from the redirect. However, this requires your Dropbox App secret to be accessible client-side during the code exchange, which is insecure for public apps. For any app with more than a few users, use a backend proxy.

**Expected result:** Both API Groups read the dropboxToken from App State dynamically. The home screen conditionally shows the file browser or sign-in button based on the dropboxIsAuthenticated state.

### 5. Build the file browser ListView and wire upload and share actions

On your main page, add a ListView widget. Set its Backend Query to API Call → DropboxRPC → listFolder. The list data source should use the $.entries JSON path (the array of file and folder items). Inside the ListView item, add a Row with a file icon on the left and a Column on the right with two Text widgets — one for the file name ($.name) and one for the file type indicator ($.[".tag"], which is either file or folder).

Add an icon button at the end of each row for sharing. In its action flow, add a Backend/API Call action → DropboxRPC → createSharedLink, binding filePath to the current list item's $.path_lower. After the call completes, add a Copy to Clipboard action with the value bound to the API response JSON path $.url. Add a Snackbar action to show Shareable link copied to clipboard.

Above the ListView, add a Button labeled Upload File. Its action flow should: 1) Launch a File Picker action (FlutterFlow Action Flow → Utilities → Pick Files) to let the user select a file — this gives you the file bytes and name. 2) Set Page State variables for fileBytes and fileName. 3) Call DropboxContent → uploadFile, binding the destinationPath variable to /[fileName] and the binary body to fileBytes. 4) On success, run the listFolder backend query again to refresh the list.

For empty folders, check the $.has_more field in the listFolder response — if true, you need to call /2/files/list_folder/continue with the cursor from $.cursor to fetch more entries. This pagination is only needed if the folder has more than 2,000 files.

**Expected result:** A ListView shows the root Dropbox contents with file/folder names. Users can tap Share to copy a shareable link, and the Upload button lets them pick and send a file to Dropbox.

### 6. Test thoroughly and secure the token for production

Test your integration by running the listFolder API Call directly from the API Calls panel → Test tab with your dropboxToken value pre-filled. This confirms the response shape before you wire it to a ListView. Common issues to catch here: the 409 error if the root path is / instead of empty string, and the 400 error if you accidentally send parameters in a query string instead of the JSON body.

For the upload flow, test with a small file (a few KB JPEG) first. Check the Dropbox web interface to confirm the file appeared in the expected location. Then test with a larger file to confirm the binary body and Dropbox-API-Arg header are wired correctly.

For production security: a long-lived access token embedded in a compiled Flutter app can be extracted from the APK or IPA by a determined user. For apps with real end-users, implement short-lived token issuance via a Firebase Cloud Function or Supabase Edge Function that holds your Dropbox app secret and issues user-specific tokens through the standard Dropbox OAuth 2.0 authorization code flow. The proxy calls https://api.dropbox.com/oauth2/token with the client secret and returns only the short-lived access_token to FlutterFlow.

```
// Example: Supabase Edge Function for Dropbox token exchange
// supabase/functions/dropbox-auth/index.ts
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';

const APP_KEY = Deno.env.get('DROPBOX_APP_KEY')!;
const APP_SECRET = Deno.env.get('DROPBOX_APP_SECRET')!;

serve(async (req) => {
  const { code, redirect_uri } = await req.json();
  const credentials = btoa(`${APP_KEY}:${APP_SECRET}`);

  const res = await fetch('https://api.dropbox.com/oauth2/token', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${credentials}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      code,
      grant_type: 'authorization_code',
      redirect_uri,
    }).toString(),
  });

  const data = await res.json();
  // Return only the access_token; store refresh_token server-side
  return new Response(
    JSON.stringify({ access_token: data.access_token }),
    { headers: { 'Content-Type': 'application/json' } }
  );
});
```

**Expected result:** The integration works end-to-end: files list from Dropbox, uploads succeed, and shared links are generated. Production token handling is secured via a backend proxy.

## Best practices

- Always use an empty string for the root path in list_folder body — never a forward slash — this is the single most common Dropbox v2 mistake.
- Keep the two API Groups (DropboxRPC and DropboxContent) clearly separated; mixing up the base URLs causes 404 errors that are hard to debug.
- For production apps, use a backend proxy (Firebase Cloud Function or Supabase Edge Function) to exchange the OAuth code for tokens — never embed your Dropbox app secret in a Flutter app.
- Use the App folder access type when your app only needs its own dedicated storage — it prevents the token from accessing other content in the user's personal Dropbox.
- Handle the shared_link_already_exists 409 by falling back to /2/sharing/get_shared_links rather than failing with an error message.
- Check the $.has_more field on listFolder responses — if true, use the $.cursor with list_folder/continue to fetch all entries in large folders.
- Test all API Calls using the FlutterFlow API Calls test tab before wiring them to widgets — it saves time compared to debugging inside a running app.

## Use cases

### Real estate app that syncs property photos to Dropbox

A FlutterFlow app for real estate agents to photograph properties on their phone and upload directly to a Dropbox folder named after the listing ID. The app also lists existing photos for that listing and generates shareable Dropbox links to send to clients via messaging.

Prompt example:

```
Build a property photo management screen where agents select a listing, see existing Dropbox photos in a grid, and tap a camera button to take and upload a new photo to the correct listing folder.
```

### Creative agency client portal with file delivery

A FlutterFlow app where a design agency delivers completed files to clients via a shared Dropbox folder. The app browses folders scoped to each client, displays file names and modification dates in a list, and lets clients download files directly or copy shareable links.

Prompt example:

```
Create a client portal that shows a list of delivered files from a Dropbox folder, with a download button for each file and a share button that copies the Dropbox link to the clipboard.
```

### Document collection app for HR onboarding

A FlutterFlow app that guides new employees through uploading required HR documents — ID scan, proof of address, signed contract — to their personal Dropbox subfolder. Each upload confirmation is stored in Firestore, and HR can browse a folder per employee from their own Dropbox Business account.

Prompt example:

```
Build an onboarding checklist app where employees can tap each document type, pick a file from their device, upload it to their named Dropbox subfolder, and see a green check mark when the upload succeeds.
```

## Troubleshooting

### listFolder returns Error in call: 409 Conflict with error_summary: path/not_found

Cause: The root folder path was set to / instead of an empty string. Dropbox v2 uses an empty string to represent the root, not a forward slash.

Solution: Open DropboxRPC → listFolder → Request Body and confirm the path value is "" (empty string), not "/". Change any "/" to "" and re-test.

### All API Calls return 400 Bad Request and the error mentions invalid_request_error

Cause: Parameters were passed as URL query strings instead of in the JSON body. Dropbox v2 ignores query string parameters on RPC endpoints.

Solution: In the FlutterFlow API Call, confirm the Request Body type is JSON and all parameters (path, mode, etc.) are inside the body JSON, not appended to the endpoint URL as ?param=value.

### uploadFile call returns 409 or 400 and the file does not appear in Dropbox

Cause: The upload call was sent to the RPC host (api.dropboxapi.com) instead of the Content host (content.dropboxapi.com), or the Dropbox-API-Arg header was missing or malformed.

Solution: Confirm the uploadFile call is inside the DropboxContent API Group (base URL: https://content.dropboxapi.com), not the DropboxRPC group. Verify the Dropbox-API-Arg header is present and contains valid JSON with the path field including a leading slash, for example /uploads/myfile.pdf.

### createSharedLink returns 409 with shared_link_already_exists error

Cause: Dropbox only allows one shared link per file with the same settings. If a shared link already exists for that file, the create call fails.

Solution: Switch to the /2/sharing/get_shared_links endpoint instead, which returns the existing shared link for a file path without creating a new one. Alternatively, catch the 409 error in your action flow and call get_shared_links as a fallback.

## Frequently asked questions

### Why does Dropbox use POST for everything instead of GET for read operations?

Dropbox v2 was designed so that all parameters live in the request body for consistency and to avoid URL length limits on complex queries. Even list_folder (which reads data) is a POST. FlutterFlow handles this fine — just make sure the body is set to JSON type with the correct parameter structure in every API Call.

### Does Dropbox work in FlutterFlow web builds, or does it have CORS issues?

Dropbox's metadata API (api.dropboxapi.com) supports CORS and works in web builds. The content API (content.dropboxapi.com) also supports CORS for upload. So both API Groups should work in a FlutterFlow web build. If you see XMLHttpRequest errors in browser preview, check that both API Groups have the Content-Type header set correctly and the Authorization header is populated.

### Can I list only a specific subfolder instead of the entire Dropbox root?

Yes. In the listFolder API Call body, change the path value from an empty string to the folder path, such as /photos or /client-documents/acme-corp. The path must include a leading slash (unlike the root, which is an empty string). You can make this dynamic by turning path into a Variable in the Variables tab and binding it from a page state variable.

### What is the file size limit for uploads through the Dropbox v2 upload endpoint?

The /2/files/upload endpoint supports files up to 150 MB. For files over 150 MB, Dropbox requires the chunked upload session flow using /2/files/upload_session/start, /2/files/upload_session/append, and /2/files/upload_session/finish. Most mobile use cases stay well under 150 MB, so the single-call upload endpoint is sufficient.

### How do I make the app work for other users' Dropbox accounts, not just mine?

You need to implement the Dropbox OAuth 2.0 authorization code flow. In FlutterFlow, add a Launch URL action that opens the Dropbox authorization URL (https://www.dropbox.com/oauth2/authorize?client_id=YOUR_APP_KEY&response_type=code&redirect_uri=YOUR_PROXY_URL). A backend function exchanges the code for a user-specific access token using your app secret. The token goes into the dropboxToken App State variable, and from that point the app uses that user's Dropbox, not yours.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/dropbox
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/dropbox
