Flux Dynamic Link (Flutter)
Flutter plugin to:
- Receive Flux links (deep links + universal links) and emit the resolved target URL.
- Create shortened links via your Flux backend.
- Support deferred deep linking:
- Android: Play Install Referrer
- iOS: NativeLink (clipboard token) + fingerprint fallback
This package is designed to work with the Flux Dynamic Link backend (the Worker/API in this repo). You must provide:
publicKey: API key used asAuthorization: Bearer ...linkDomain: your link service domain, e.g.https://deeplink.fluxbuilder.comor a custom domain
Requirements
- Flutter
>= 3.3.0 - Dart
>= 3.10.0
1) Create your Flux project
Create your project in FluxBuilder (download: https://fluxbuilder.com/download).
Keep these values:
- Project Id
- Project Key (used as
publicKey) - Link domain (used as
linkDomain)
2) Install
Add to your app pubspec.yaml:
dependencies:
flux_dynamic_link: ^1.2.1
Then run:
flutter pub get
3) Platform setup
Android
Add deep link + app link intent-filters to your app AndroidManifest.xml on your main Activity.
Replace:
{projectId}with your Flux project iddeeplink.fluxbuilder.comwith your custom domain host if you use one
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="fluxlink-{projectId}"
android:host="deeplink.fluxbuilder.com" />
</intent-filter>
<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="deeplink.fluxbuilder.com" />
</intent-filter>
Also ensure your domain hosts a valid /.well-known/assetlinks.json for Android App Links verification.
iOS
- Add a custom URL scheme in
ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleURLSchemes</key>
<array>
<string>fluxlink-{projectId}</string>
</array>
</dict>
</array>
- Enable Universal Links (recommended):
- Open Xcode → Runner target → Signing & Capabilities → add Associated Domains
- Add:
applinks:deeplink.fluxbuilder.com(or your custom domain)
Your domain must host a valid Apple App Site Association file (AASA).
4) Initialize
Important: if you want deferred deep link callbacks, call FluxDynamicLink.setOnInstallReferrerDetected before initialize().
import 'package:flutter/widgets.dart';
import 'package:flux_dynamic_link/flux_dynamic_link.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
FluxDynamicLink.setOnInstallReferrerDetected((result) {
// Called once per install (guarded by local storage)
if (result.matched == true) {
// Handle deferred deep link here
// result.deepLink / result.webFallbackUrl / result.metadata
}
});
await FluxDynamicLink.initialize(
publicKey: '{publicKey}',
linkDomain: 'https://deeplink.fluxbuilder.com',
);
runApp(const MyApp());
}
5) Listen for incoming links
Use FluxDynamicLink.instance.dynamicLinkStream.
This stream emits the resolved target URL (usually actual_url from the link, otherwise a fallback resolved from the backend).
FluxDynamicLink.instance.dynamicLinkStream.listen((url) {
final uri = Uri.tryParse(url);
if (uri == null) return;
// Navigate based on uri.path / uri.queryParameters
});
6) Deferred deep linking
Android (Play Install Referrer)
On first app open after installation, the plugin reads Play Install Referrer and attempts to match a deferred deep link. You receive the result via setOnInstallReferrerDetected.
iOS — NativeLink (recommended)
On iOS, App Store installs do not preserve query params into the app. Flux supports a deterministic NativeLink-style flow:
- Your iOS landing page generates a single-use token server-side.
- On user gesture (CTA tap), the page copies
https://<domain>/_nl?t=TOKENinto clipboard and redirects to the App Store. - On first app open, the plugin:
- reads clipboard once (to reduce repeated paste permission prompts)
- obtains a stable
install_idfrom Keychain - calls
POST /api/nativelink/redeemto redeemTOKEN
- If NativeLink is missing/expired/overwritten/denied, the plugin falls back to fingerprint matching.
Backend requirement: expose POST /api/nativelink/redeem on your linkDomain.
7) Create shortened links
Use createShortenedLink to create a link, then getFullUrl to get the shareable URL.
final linkDetails = await FluxDynamicLink.instance.createShortenedLink(
CreateFluxDynamicLinkForm(
slug: 'product-123',
iosDeeplink: 'fluxstore://product/123',
androidDeeplink: 'fluxstore://product/123',
androidPackage: 'com.inspireui.fluxstore',
appstoreUrl: 'https://apps.apple.com/app/id1234567890',
playstoreUrl: 'https://play.google.com/store/apps/details?id=com.inspireui.fluxstore',
webFallbackUrl: 'https://fluxstore.app/product/123',
metadata: LinkMetadata(
title: 'FluxStore Product 123',
description: 'Check out this product',
image: 'https://fluxstore.app/images/product-123.jpg',
),
expiresAt: DateTime.now().add(const Duration(days: 30)),
),
);
final url = FluxDynamicLink.instance.getFullUrl(linkDetails);
// => {linkDomain}/l/{slug}
8) Custom domains (optional)
If you use a custom domain (e.g. https://links.customer.com), update all of these:
- App initialization:
linkDomain: 'https://links.customer.com' - Android: intent-filter host +
assetlinks.jsonmust be served by that domain - iOS: Associated Domains must include
applinks:links.customer.comand AASA must be served by that domain
Typical DNS is:
links.customer.com CNAME deeplink.fluxbuilder.com
Testing
- Android Install Referrer is only reliable when installed from Play Store (or internal testing tracks). A local
flutter runinstall usually will not provide real referrer data.
Troubleshooting
- Not receiving links:
- Ensure the clicked link host matches
linkDomain(ordeeplink.fluxbuilder.com). - Verify Android intent-filters +
assetlinks.json. - Verify iOS Associated Domains + AASA.
- Ensure the clicked link host matches
- iOS deferred not matched:
- NativeLink must be copied on a user gesture.
- Clipboard can be overwritten before first app open; plugin will then fallback to fingerprint.