Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsCustom Action (Dart)

OmniFocus

OmniFocus has no public REST API and runs only on Apple platforms. You cannot connect FlutterFlow to it directly. The practical bridge is: iOS Shortcuts or Omni Automation exports OmniFocus tasks as JSON and POSTs to a Firebase Cloud Function, which stores tasks in Firestore; FlutterFlow reads from Firestore. For writing tasks from FlutterFlow, a Custom Action launches the OmniFocus URL scheme on Apple devices, or posts to OmniFocus Mail Drop for cross-platform creation.

What you'll learn

  • Why OmniFocus has no public REST API and what the realistic bridge alternatives are
  • How to set up a Firebase/Firestore backend to store OmniFocus task data for FlutterFlow to read
  • How to configure an iOS Shortcut or Omni Automation script to export tasks and POST to your Cloud Function webhook
  • How to write a FlutterFlow Custom Action using url_launcher to open the OmniFocus URL scheme on Apple devices
  • How to handle the Mail Drop alternative for cross-platform (non-Apple) task creation
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced19 min read2 hoursProductivityLast updated July 2026RapidDev Engineering Team
TL;DR

OmniFocus has no public REST API and runs only on Apple platforms. You cannot connect FlutterFlow to it directly. The practical bridge is: iOS Shortcuts or Omni Automation exports OmniFocus tasks as JSON and POSTs to a Firebase Cloud Function, which stores tasks in Firestore; FlutterFlow reads from Firestore. For writing tasks from FlutterFlow, a Custom Action launches the OmniFocus URL scheme on Apple devices, or posts to OmniFocus Mail Drop for cross-platform creation.

Quick facts about this guide
FactValue
ToolOmniFocus
CategoryProductivity
MethodCustom Action (Dart)
DifficultyAdvanced
Time required2 hours
Last updatedJuly 2026

The Honest OmniFocus + FlutterFlow Guide: Bridges, Not APIs

If you searched for 'OmniFocus API FlutterFlow' expecting a straightforward REST connection, this guide has important news: OmniFocus has no public REST API. It is a native Apple application that stores data locally in a proprietary database on macOS and iOS — there is no cloud endpoint you can call from FlutterFlow, and The Omni Group has not released a developer API that would allow it.

This does not mean the integration is impossible — it means you need a bridge. The practical pattern used by power users and small teams is: OmniFocus exports task data via iOS Shortcuts (built into iOS 16+) or Omni Automation scripts (built into OmniFocus 3+), which POST JSON to a Firebase Cloud Function webhook. That Cloud Function saves the tasks into Firestore. FlutterFlow connects natively to Firebase and can read, display, and filter those tasks in your app in real time. For writing tasks back from FlutterFlow into OmniFocus, a Custom Action using the Dart url_launcher package opens the OmniFocus URL scheme (omnifocus:///add?name=...) on Apple devices where OmniFocus is installed.

The OmniFocus pricing model is one-time purchase or subscription per Apple platform — verify current pricing at omnigroup.com. There is no API tier or developer plan. The only auth on this integration is whatever secret you configure in your own Cloud Function webhook to prevent unauthorized task injection.

Integration method

Custom Action (Dart)

OmniFocus has no public REST API and cannot be called directly from a FlutterFlow API Group. The integration uses a bridge pattern: iOS Shortcuts or Omni Automation exports tasks as JSON to a Firebase Cloud Function webhook, which stores them in Firestore; FlutterFlow reads tasks from Firestore via its native Firebase panel. For writing tasks back into OmniFocus from FlutterFlow, a Custom Action uses the url_launcher Dart package to open the omnifocus:///add URL scheme on Apple devices, or alternatively a Cloud Function sends an email to the OmniFocus Mail Drop address for cross-platform task creation.

Prerequisites

  • OmniFocus 3 or later installed on macOS and/or iOS (required on the Apple device that runs the Shortcut or Omni Automation export)
  • A Firebase project with Firestore enabled and FlutterFlow connected to it via Settings and Integrations → Firebase
  • Firebase Cloud Functions enabled (Blaze plan required for outbound HTTP — needed if using Mail Drop email relay)
  • iOS 16+ on the Apple device that will run the export Shortcut
  • Basic familiarity with FlutterFlow's Firebase panel, Custom Code, and Action Flow Editor

Step-by-step guide

1

Understand the bridge architecture before building

The most important step is setting the right expectation: there is no OmniFocus REST API. Searching the web, asking AI tools, or reading older blog posts may suggest that one exists — it does not. The Omni Group has published URL scheme documentation and Omni Automation (a JavaScript-based scripting layer inside the app), but neither exposes an HTTP endpoint that FlutterFlow can call over the internet. This integration therefore has two distinct data flows you need to design before touching any tool. Flow 1 is outbound from OmniFocus to FlutterFlow: OmniFocus (on your Mac or iPhone) runs a Shortcut or Omni Automation script that serializes your tasks as JSON and POSTs them to a Firebase Cloud Function webhook. The Cloud Function saves the data into Firestore. FlutterFlow reads from Firestore, where it has a native connection, and displays the tasks in your app. Flow 2 is inbound from FlutterFlow into OmniFocus: a FlutterFlow Custom Action uses the Dart url_launcher package to open the omnifocus:///add?name=TASK_TITLE URL scheme — this only works on macOS and iOS where OmniFocus is installed. For cross-platform inbound (Android users, web), a Cloud Function can send an email to the OmniFocus Mail Drop address associated with the account, which OmniFocus converts into an Inbox task. Sketch this architecture on paper before starting. You are building two systems (Firebase/Firestore and the Shortcut/Automation) in addition to the FlutterFlow app, and understanding which components connect where will save significant debugging time.

Pro tip: OmniFocus Automation documentation is at omni-automation.com. URL scheme reference is at help.omnigroup.com/omnifocus-url-schemes. Read these before building the Shortcut or Automation export — they define exactly what data can be serialized.

Expected result: You have a clear architecture diagram (even a rough sketch) showing: OmniFocus → Shortcut/Automation → Cloud Function → Firestore → FlutterFlow (read path), and FlutterFlow → Custom Action → URL scheme / Mail Drop → OmniFocus (write path).

2

Set up Firestore as the task storage layer and connect FlutterFlow to Firebase

In the Firebase console, open your project and navigate to Firestore Database. Click Create Database and choose production mode. Create a collection named omnifocus_tasks. The schema for each document should mirror the OmniFocus task data you plan to export: fields like id (string), name (string), notes (string), project (string), dueDate (timestamp), completionStatus (string), and syncedAt (timestamp). You can add these fields manually now, or let the Cloud Function create documents with these fields when the first Shortcut export runs. In FlutterFlow, go to Settings and Integrations → Firebase. Click Connect to Firebase and follow the prompts to link your Firebase project (you will need to download and upload the GoogleService-Info.plist for iOS and google-services.json for Android). Once connected, go to the Firestore panel in FlutterFlow's left nav. Click + Add Collection and point it to omnifocus_tasks. FlutterFlow will auto-detect the schema from existing documents, or let you define the fields manually if the collection is empty. Create a Firestore Data Type in FlutterFlow called OmniFocusTask with the same fields you defined in Firestore. This Data Type will be used to bind task data to ListView widgets in your app. Firestore real-time listeners in FlutterFlow mean the app will automatically refresh when new tasks are synced — no manual polling required.

firestore_schema.json
1// Firestore document schema for omnifocus_tasks collection
2// Each document represents one OmniFocus task
3{
4 "id": "abc123-omni-id", // OmniFocus internal task ID
5 "name": "Prepare Q3 report", // task title
6 "notes": "Include revenue...", // task notes
7 "project": "Work : Finance", // project path
8 "dueDate": "2026-08-01T09:00:00Z", // ISO timestamp
9 "completionStatus": "incomplete", // incomplete | complete
10 "flagged": false, // flagged/starred task
11 "syncedAt": "2026-07-10T08:30:00Z" // last sync timestamp
12}

Pro tip: Add a syncedAt timestamp field to every Firestore document. This lets you show users when their OmniFocus data was last exported, and lets the Cloud Function upsert (update or insert) documents by matching on the OmniFocus task id rather than creating duplicates on each sync.

Expected result: A Firestore collection named omnifocus_tasks exists in your Firebase project. FlutterFlow's Firebase panel shows the collection and its schema, and the OmniFocusTask Data Type is defined in FlutterFlow.

3

Create a Firebase Cloud Function webhook to receive OmniFocus task exports

The Cloud Function acts as the secure receiver for task data pushed from the iOS Shortcut or Omni Automation script on your Apple device. In the Firebase console, open Functions and create an HTTP-triggered function named omnifocusSync. The function receives a POST request containing an array of OmniFocus task objects as JSON, validates an optional webhook secret header for security (so not just anyone can inject tasks into your Firestore), and writes each task as a document in the omnifocus_tasks collection using the OmniFocus task id as the document ID (enabling upsert behavior on repeat syncs). Deploy the function. You will receive an HTTPS URL such as https://us-central1-YOUR_PROJECT.cloudfunctions.net/omnifocusSync. Copy this URL — you will paste it into the iOS Shortcut in the next step. For the webhook secret: generate a random string (e.g., 32 hex characters from a password manager). Store it in Firebase Functions config: firebase functions:config:set omnifocus.secret=YOUR_SECRET. In the Cloud Function, read this secret from config and compare it against a custom header the Shortcut sends (e.g., X-Webhook-Secret). Reject requests with a 401 if the header is missing or wrong. This prevents unauthorized Firestore writes if the Cloud Function URL is ever discovered by others.

index.js
1// Firebase Cloud Function — OmniFocus task sync webhook
2// File: functions/index.js
3const functions = require('firebase-functions');
4const admin = require('firebase-admin');
5admin.initializeApp();
6
7const db = admin.firestore();
8
9exports.omnifocusSync = functions.https.onRequest(async (req, res) => {
10 if (req.method !== 'POST') {
11 res.status(405).send('Method Not Allowed');
12 return;
13 }
14
15 // Validate webhook secret
16 const expectedSecret = functions.config().omnifocus.secret;
17 const incomingSecret = req.headers['x-webhook-secret'];
18 if (!expectedSecret || incomingSecret !== expectedSecret) {
19 res.status(401).json({ error: 'Unauthorized' });
20 return;
21 }
22
23 const tasks = req.body.tasks;
24 if (!Array.isArray(tasks)) {
25 res.status(400).json({ error: 'Expected { tasks: [...] }' });
26 return;
27 }
28
29 const batch = db.batch();
30 const syncedAt = new Date().toISOString();
31
32 for (const task of tasks) {
33 if (!task.id) continue;
34 const ref = db.collection('omnifocus_tasks').doc(String(task.id));
35 batch.set(ref, {
36 id: task.id,
37 name: task.name || '',
38 notes: task.notes || '',
39 project: task.project || '',
40 dueDate: task.dueDate || null,
41 completionStatus: task.completionStatus || 'incomplete',
42 flagged: task.flagged || false,
43 syncedAt: syncedAt
44 }, { merge: true });
45 }
46
47 await batch.commit();
48 res.status(200).json({ synced: tasks.length });
49});

Pro tip: Use batch.set with { merge: true } rather than batch.create so that re-running the Shortcut export does not fail on already-existing documents. The task id from OmniFocus becomes the Firestore document ID, making each export idempotent.

Expected result: The Cloud Function is deployed and its HTTPS URL is copied. A test POST to the URL with valid JSON and the correct X-Webhook-Secret header returns { synced: N } and creates documents in the omnifocus_tasks Firestore collection.

4

Build an iOS Shortcut or Omni Automation script to export tasks

On your iPhone or Mac where OmniFocus is installed, open the Shortcuts app (iOS 16+ or macOS 12+). Create a new Shortcut named Export OmniFocus Tasks. The Shortcut will use OmniFocus's built-in Shortcuts actions to fetch tasks and convert them to JSON. In the Shortcut editor, add the action Find Tasks in OmniFocus. Configure it to find incomplete tasks (you can filter by project, flag, or due date — start with all incomplete tasks for simplicity). Next, add a Repeat with Each action over the task list. Inside the loop, add a Get Details of OmniFocus Task action to extract the fields you want: Name, Notes, Project, Due Date, Completion, ID, Flagged. Collect these fields into a Dictionary variable per task, then add each dictionary to an accumulating List variable. After the loop, add a Get Contents of URL action (this is how Shortcuts makes HTTP requests). Set the URL to your Cloud Function URL from Step 3. Set Method to POST. Add a header row: X-Webhook-Secret with your secret value. Set the request body type to JSON and pass the task list as { tasks: [...] }. For automated syncing without manually running the Shortcut each time, add a Shortcut Automation in the Shortcuts app: set it to run on a time trigger (e.g., every morning at 7am) or on the action Open App for OmniFocus. This keeps your Firestore data reasonably fresh without any manual effort. On macOS, you can also use OmniFocus's built-in Omni Automation (Automation menu → New Plug-In or Run Script) with a JavaScript snippet that calls the same Cloud Function URL via fetch().

omnifocus_export.js
1// Omni Automation JavaScript — runs inside OmniFocus on macOS or iOS
2// Automation menu -> New Plug-In Script (or paste in the console)
3// Exports all incomplete tasks to Firebase Cloud Function
4
5(async () => {
6 const tasks = flattenedTasks.filter(t => !t.completed);
7 const payload = tasks.map(t => ({
8 id: t.id.primaryKey,
9 name: t.name,
10 notes: t.note || '',
11 project: t.containingProject ? t.containingProject.name : '',
12 dueDate: t.dueDate ? t.dueDate.toISOString() : null,
13 completionStatus: t.completed ? 'complete' : 'incomplete',
14 flagged: t.flagged
15 }));
16
17 const url = URL.fromString('https://us-central1-YOUR_PROJECT.cloudfunctions.net/omnifocusSync');
18 const request = new URL.FetchRequest();
19 request.method = 'POST';
20 request.headers = {
21 'Content-Type': 'application/json',
22 'X-Webhook-Secret': 'YOUR_WEBHOOK_SECRET'
23 };
24 request.bodyString = JSON.stringify({ tasks: payload });
25
26 const response = await URL.fetch(url, request);
27 console.log('Synced:', response.bodyString);
28})();

Pro tip: Replace YOUR_PROJECT and YOUR_WEBHOOK_SECRET with your actual values before running. The Omni Automation fetch API is available in OmniFocus 3.10+ on macOS. On iOS, use the Shortcuts approach instead since JavaScript console execution is limited on mobile.

Expected result: Running the Shortcut or Omni Automation script successfully POSTs task data to the Cloud Function, which returns a synced count. Tasks appear in the omnifocus_tasks Firestore collection visible in the Firebase console.

5

Build the FlutterFlow task list UI and read from Firestore

In your FlutterFlow project, create a new Page named My OmniFocus Tasks. In the widget tree, add a Column filling the screen. Inside, add a Text widget at the top displaying the last sync time (you will bind this to the most recent syncedAt value from Firestore). Below, add a ListView widget. Click the ListView and set Generate Children from Variable to a Backend Query against Firestore. Choose the omnifocus_tasks collection. Add a filter: completionStatus == 'incomplete' to show only open tasks. Add an order by: dueDate ascending to show soonest-due tasks first. FlutterFlow will automatically set up a real-time Firestore listener, so the list updates immediately when the Shortcut syncs new data. For each child item in the ListView, add a Card. Inside: a Row at the top with a Text widget bound to OmniFocusTask.name (in bold, font size 16) and a Chip widget for the project name bound to OmniFocusTask.project. Below, a Text widget showing the due date formatted as a readable string, using FlutterFlow's date format action. Add a Conditional Styling rule to the Card's left border: if the dueDate is in the past and completionStatus is incomplete, show a red left border (overdue indicator). Add a Floating Action Button to the page. This button will navigate to a Create Task page (built in the next step). The overall page now shows a live, refreshing task list synchronized from OmniFocus via your Firestore bridge.

Pro tip: Add a last-synced banner at the top of the page showing the most recent syncedAt timestamp from any task in the collection. Users will want to know if the data is stale — if the Shortcut has not run in 24 hours, the displayed tasks may not reflect reality.

Expected result: The FlutterFlow task list page displays OmniFocus tasks read from Firestore, grouped by project, sorted by due date, with overdue tasks highlighted. The list refreshes in real time when new tasks are synced.

6

Add a Custom Action to create tasks in OmniFocus via URL scheme and Mail Drop

To create tasks from FlutterFlow back into OmniFocus, you need a Custom Action for Apple platforms and a Mail Drop fallback for Android or web users. Custom Actions that use url_launcher do not run in the FlutterFlow web/Run Mode preview — you must test this on a real iOS or macOS device build. In FlutterFlow, go to left nav → Custom Code → + Add → Action. Name it LaunchOmniFocusTask. In the Dependencies field, add the pub.dev package url_launcher with a recent version (check pub.dev for the latest stable version number). Write the Dart action as shown below: it takes taskName (String) and taskNote (String) as input parameters, encodes them as URL components, and launches the omnifocus:///add URL scheme. On the Create Task page, add a Form with a TextField for task name and a multi-line TextField for notes. Add a Submit button. In the button's Action Flow, first check the device platform using an If/Else Conditional Action: if Platform is iOS or macOS, call the LaunchOmniFocusTask Custom Action with the form field values as parameters. If the platform is Android or Web, call a Cloud Function via an API Call action that sends an email to the OmniFocus Mail Drop address — the email subject becomes the task name and the body becomes the notes. For the Mail Drop route: go to OmniFocus settings on any Apple device, navigate to Mail Drop, and copy the unique email address. Paste it into your Cloud Function as a config value. The Cloud Function sends an email via an SMTP service (for example, using Firebase's recommended Trigger Email extension, which connects to an email provider). Do not embed the Mail Drop address in the client app — keep it in the Cloud Function config.

custom_action.dart
1// FlutterFlow Custom Action — LaunchOmniFocusTask
2// Dependency: url_launcher (add exact version from pub.dev)
3// Arguments: taskName (String), taskNote (String)
4// Returns: void
5
6import 'package:flutter/material.dart';
7import 'package:url_launcher/url_launcher.dart';
8import 'dart:io' show Platform;
9
10Future<void> launchOmniFocusTask(
11 String taskName,
12 String taskNote,
13) async {
14 // Guard: OmniFocus only exists on Apple platforms
15 if (!Platform.isIOS && !Platform.isMacOS) {
16 debugPrint('OmniFocus URL scheme only works on iOS and macOS');
17 return;
18 }
19
20 final encodedName = Uri.encodeComponent(taskName);
21 final encodedNote = Uri.encodeComponent(taskNote);
22
23 // OmniFocus URL scheme: https://help.omnigroup.com/omnifocus-url-schemes
24 final uri = Uri.parse(
25 'omnifocus:///add?name=$encodedName&note=$encodedNote',
26 );
27
28 if (await canLaunchUrl(uri)) {
29 await launchUrl(uri);
30 } else {
31 debugPrint('Could not launch OmniFocus URL scheme — is OmniFocus installed?');
32 }
33}

Pro tip: The url_launcher Custom Action will NOT run in FlutterFlow's web Run Mode preview. To test it, use FlutterFlow's Test Mode with a real iOS or macOS device, or build and install an APK/IPA. If the canLaunchUrl check returns false, OmniFocus is not installed on the device.

Expected result: Tapping the Submit button on an iPhone with OmniFocus installed opens OmniFocus and shows the pre-filled task in the Quick Entry or Inbox. On Android, the button routes through the Cloud Function Mail Drop path instead.

Common use cases

Personal GTD dashboard readable from FlutterFlow

A FlutterFlow app that displays your OmniFocus tasks synced via an iOS Shortcut that runs on a schedule and pushes task data to Firestore. The app shows a ListView of open tasks grouped by project, with due dates and completion status — useful when you want a mobile overview without opening OmniFocus itself.

FlutterFlow Prompt

Build a FlutterFlow screen that reads OmniFocus tasks from a Firestore collection and displays them grouped by project, with each task showing its title, due date, and a checkbox to mark complete.

Copy this prompt to try it in FlutterFlow

Quick-capture button that creates tasks in OmniFocus

A FlutterFlow app with a floating action button that opens a text field for a task title and description, then taps a Submit button that launches the OmniFocus URL scheme with the pre-filled task data on the user's iPhone or Mac. Tasks appear instantly in OmniFocus Inbox without leaving the FlutterFlow app.

FlutterFlow Prompt

Add a quick-capture screen to a FlutterFlow app with a task title field and notes field, and a Submit button that opens the OmniFocus add-task URL scheme with those values pre-filled.

Copy this prompt to try it in FlutterFlow

Cross-platform team task submission via Mail Drop

A FlutterFlow form available on Android and web where team members enter a task title and project tag, and the app calls a Cloud Function that emails the task to the team lead's OmniFocus Mail Drop address. The task appears in the OmniFocus Inbox within minutes — no Apple device required for the submitter.

FlutterFlow Prompt

Create a FlutterFlow form where any team member (including Android users) can submit a task that gets forwarded to an OmniFocus Mail Drop inbox via a Firebase Cloud Function email relay.

Copy this prompt to try it in FlutterFlow

Troubleshooting

The Custom Action does not launch OmniFocus — canLaunchUrl returns false

Cause: OmniFocus is not installed on the device, or the iOS app's Info.plist does not declare the omnifocus URL scheme as a queried URL scheme (required since iOS 9 for canLaunchUrl to work).

Solution: In FlutterFlow, go to Settings and Integrations → App Details → iOS → Additional Plist Keys. Add a LSApplicationQueriesSchemes array entry with the value omnifocus. Then rebuild and reinstall the app on a device that has OmniFocus installed. Without this Plist entry, canLaunchUrl always returns false on iOS 9+.

typescript
1// Info.plist entry needed for canLaunchUrl on iOS 9+
2// Add via FlutterFlow: Settings -> iOS -> Additional Plist Keys
3// Key: LSApplicationQueriesSchemes
4// Type: Array
5// Value item: omnifocus

The Cloud Function webhook returns 401 Unauthorized when the Shortcut runs

Cause: The X-Webhook-Secret header in the Shortcut does not match the secret stored in Firebase Functions config, or the header is missing entirely.

Solution: In the Shortcuts app, open the Get Contents of URL action. Verify there is a header row with key X-Webhook-Secret and the value set to the exact secret string you configured with firebase functions:config:set omnifocus.secret=YOUR_SECRET. Secrets are case-sensitive and must match exactly — no extra spaces or line breaks.

Custom Action compiles but crashes with MissingPluginException at runtime

Cause: The url_launcher plugin dependency was not correctly saved in the FlutterFlow Custom Code Dependencies field, or the app has not been rebuilt after the dependency was added.

Solution: In FlutterFlow, go to Custom Code, click on your LaunchOmniFocusTask action, and verify the url_launcher package appears in the Dependencies section with a valid pub.dev version number. Click Save, then force a full rebuild of the project (try switching platforms back and forth in the app preview to trigger a dependency refresh). If testing in the web preview — stop, as Custom Actions only run on native device builds.

Firestore task list in FlutterFlow is stale and does not update when OmniFocus data changes

Cause: The iOS Shortcut automation trigger is not running on schedule, or the Shortcut failed silently due to a network timeout when the Cloud Function URL was unreachable.

Solution: On iOS, open Shortcuts and check the automation for Export OmniFocus Tasks — verify the trigger is enabled and the run history shows recent successful runs. If it shows failures, open the Shortcut manually to see the error. Common causes: the device was offline when the trigger fired, or the Cloud Function URL changed after a Firebase redeployment. Also add a pull-to-refresh on the FlutterFlow page so users can manually trigger a Firestore refresh while the Shortcut sync catches up.

Best practices

  • Set honest expectations in the app: show a 'Last synced' timestamp on the task list so users know the data is not live — it is as fresh as the last Shortcut run.
  • Secure the Cloud Function webhook with a secret header (X-Webhook-Secret). Without it, anyone who discovers the function URL can inject arbitrary tasks into your Firestore and FlutterFlow app.
  • Platform-guard all OmniFocus URL scheme calls in the Custom Action using Platform.isIOS or Platform.isMacOS. Never call the URL scheme on Android or web — it will silently fail or crash, and you need the Mail Drop path for those platforms.
  • Use batch.set with merge: true in the Cloud Function so repeat Shortcut runs upsert documents rather than failing on duplicates or creating ghost tasks.
  • Keep the OmniFocus Mail Drop email address exclusively in Firebase Functions config — never expose it in the FlutterFlow app or Dart code, as it is a publicly deliverable email that could be spammed if leaked.
  • Test the Custom Action on a real iOS or macOS device build, not the FlutterFlow web preview. The url_launcher package and omnifocus:// URL scheme do not work in the browser-based Run Mode.
  • Add the LSApplicationQueriesSchemes entry for omnifocus in FlutterFlow's iOS Plist settings before building, or canLaunchUrl will always return false on iOS 9+ regardless of whether OmniFocus is installed.

Alternatives

Frequently asked questions

Does OmniFocus have a REST API that FlutterFlow can call directly?

No. OmniFocus has no public REST API. It is a native Apple application with a local database, not a cloud service. The Omni Group has published a URL scheme and an Omni Automation JavaScript scripting layer, but neither provides an HTTP endpoint that FlutterFlow or any external service can call over the internet. This guide's bridge pattern — using iOS Shortcuts or Omni Automation to export tasks to a Firebase Cloud Function — is the practical workaround.

Will this integration work for Android users?

Partially. The FlutterFlow task reading experience (Firestore-backed task list) works on all platforms including Android, because Firestore is cross-platform. However, the OmniFocus URL scheme for creating tasks only fires on iOS and macOS where OmniFocus is installed. Android users can create tasks via the Mail Drop email path described in Step 6, which routes through a Cloud Function and does not require OmniFocus on the sender's device.

How often does the Firestore task data refresh?

As frequently as the iOS Shortcut or Omni Automation script runs. The FlutterFlow app reads from Firestore in real time (Firestore listeners push updates instantly), but Firestore only has new data when the Shortcut sync has run. You can configure the Shortcut to run automatically via iOS Shortcuts Automation on a schedule (e.g., every morning) or triggered by opening the OmniFocus app. The task list will show a 'last synced' timestamp from the syncedAt field so users know how fresh the data is.

Can I mark a task complete from FlutterFlow and have it complete in OmniFocus too?

Not via a direct API call — there is no OmniFocus API to complete a task remotely. You can update the completionStatus field in Firestore from FlutterFlow (which immediately changes the task's display in the app), but the actual OmniFocus task on the Apple device will not be marked complete until the next Shortcut sync overwrites it, or a user manually completes it in OmniFocus. For a two-way sync, you would need a second Shortcut that reads 'completed in Firestore' tasks and applies completions in OmniFocus — an advanced pattern beyond this guide's scope.

What if I want to avoid writing a Cloud Function? Is there a simpler path?

The simplest path for inbound task creation is the OmniFocus URL scheme from a Custom Action — that requires no Cloud Function, just the url_launcher Dart package. For outbound (reading OmniFocus tasks in FlutterFlow), there is no simpler path than the Firestore bridge; there is no alternative API. If you want to skip the Cloud Function and Custom Action development entirely, RapidDev's team builds FlutterFlow integrations and bridge architectures like this every week — free scoping call at rapidevelopers.com/contact.

Why does the url_launcher Custom Action not work in the FlutterFlow preview?

FlutterFlow's web-based Run Mode preview renders your app as a web page in the browser. The url_launcher package works by invoking native platform capabilities (iOS deep links, macOS URL handlers), which are not available in a browser sandbox. Custom Actions that rely on url_launcher, platform channels, or native SDKs must be tested on a real iOS or macOS device build via FlutterFlow Test Mode or a downloaded build.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.