Deferred deep links in FlutterFlow
Send people to the right page of your FlutterFlow app from any link, including after they install it from the App Store or Google Play. You add one custom action, paste one file, and set one On Page Load action.
A DeepLinkNow link's path is a FlutterFlow route. https://yourapp.deeplinknow.com/productPage?productId=42 opens the page whose route is /productPage, with its productId parameter set to 42. There's nothing to create in a dashboard first: every path on your link domain works.
Before you start
- A DeepLinkNow account (free up to 25,000 monthly active users) and an app created in the dashboard.
- Your iOS Team ID and Bundle ID, and your Android package name and Play signing SHA-256 certificate. The dashboard's setup page says where to find each one, and fills most of them in from your App Store or Play link.
- A build on a real phone to test. Deep links don't run in FlutterFlow's Run or Test mode, because the plugin has no web version.
1. Set up your app in DeepLinkNow
- In the dashboard, open your app's setup page (or Settings › iOS and Settings › Android) and fill in the Team ID, Bundle ID and App Store ID, and the package name and SHA-256 certificate.
- Open Settings › API keys and copy the public key (dln_pub_…). It's safe to put in your app.
Your link domain is <alias>.deeplinknow.com, shown on the app's Overview.
2. Add the custom action
- In FlutterFlow, open Custom Code and add an Action named dlnOpenDeferredLink.
- In its settings:
- turn on Include BuildContext;
- add one argument, publicKey, of type String;
- set the return value to String, nullable;
- under Pubspec Dependencies, add deeplinknow: ^1.3.0.
- Keep the automatic imports FlutterFlow puts at the top of the file, and replace everything below // DO NOT REMOVE OR MODIFY THE CODE ABOVE! with:
import 'package:deeplinknow/deeplinknow.dart';
import 'package:go_router/go_router.dart';
/// Opens the page for the link the user tapped before installing, and
/// returns the location it opened (or null). Add it as the first On Page
/// Load action of your entry page. The link's path is a FlutterFlow route:
/// /productPage?productId=42 opens the page whose route is /productPage.
Future<String?> dlnOpenDeferredLink(
BuildContext context,
String publicKey,
) async {
if (!DeepLinkNow.isInitialized) {
final info = await DeepLinkNow.initialize(publicKey);
if (info == null) return null;
}
final link = await DeepLinkNow.checkDeferredDeepLink();
if (link == null || !context.mounted) return null;
var value = link.fullPath.trim();
if (value.isEmpty) return null;
if (!value.startsWith('/')) value = '/$value';
final queryAt = value.indexOf('?');
var path = queryAt < 0 ? value : value.substring(0, queryAt);
var query = queryAt < 0 ? '' : value.substring(queryAt);
if (query == '?') query = '';
while (path.length > 1 && path.endsWith('/')) {
path = path.substring(0, path.length - 1);
}
if (path == '/' && query.isEmpty) return null;
final location = '$path$query';
context.push(location);
return location;
}
- Save and compile. go_router is already part of every FlutterFlow project.
3. Run it on the entry page
- Open your app's entry page (the first page people see).
- In Actions › On Page Load, add Custom Action › dlnOpenDeferredLink as the first action, and set publicKey to your public key.
The first time the app opens after an install, the action asks DeepLinkNow which link the person tapped and opens that page. A link to /, or no link, leaves them on the entry page. Every later launch skips the check without a network call.
A page's route is under the page's settings. Page parameters come from the link's query string, so a page with a productId parameter reads it from ?productId=42.
4. Open the installed app from links
For a tap to open the app when it's already installed, iOS and Android need to know your app owns the link domain. In each snippet, replace yourapp.deeplinknow.com with your link domain.
- Settings › App Details › Deep Linking: turn deep linking on.
- Custom Code › Configuration Files › Runner.entitlements: add
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:yourapp.deeplinknow.com</string>
</array>
- Custom Code › Configuration Files › AndroidManifest.xml, in Activity Tags: add
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="yourapp.deeplinknow.com" />
</intent-filter>
- In the Apple Developer portal, open your App ID under Certificates, Identifiers & Profiles and turn on Associated Domains.
If you add a custom domain in DeepLinkNow later, add it to both snippets and release an update.
5. Test it
- Delete the app from your phone.
- On the phone, tap a link such as https://yourapp.deeplinknow.com/productPage?productId=42. The link page sends you to the store.
- Install your build (TestFlight, an internal test track, or a local run) and open it. It lands on the product page.
To test again without reinstalling, add a second custom action, dlnResetDeferredLink (no arguments, no return value, the same dependency), call it from a debug-only button, then restart the app:
import 'package:deeplinknow/deeplinknow.dart';
/// For testing only: the next dlnOpenDeferredLink asks again, as after a
/// fresh install.
Future dlnResetDeferredLink() async {
try {
await DeepLinkNow.resetDeferredDeepLink();
} catch (_) {
// Already logged by the SDK.
}
}
Troubleshooting
- Nothing opens. Check the device logs for [DeepLinkNow] (iOS) or DLN (Android). An invalid key, or an app with no App Store ID or package name, shows up there.
- It opens the wrong page, or an error page. The link's path has to match a page's route exactly, including case.
- Taps open the browser instead of the installed app. Check your domain with the AASA validator and the assetlinks validator, and that Associated Domains is on for the App ID.
- Coming from Branch? The switch check shows how your existing links move.
The plugin underneath is deeplinknow on pub.dev; the Flutter SDK docs cover the rest of its API.