infly 0.4.0
infly: ^0.4.0 copied to clipboard
Short links, App Links / Universal Links and deferred deep links for Flutter, backed by infl.ly. Router-agnostic, no device fingerprinting.
infly #
Flutter SDK for infl.ly: short links, App Links / Universal Links and deferred deep links (the link survives an install).
- Router-agnostic. The SDK resolves a link to a destination string such as
/board/1234. Your app decides how to navigate. - Deferred deep links on both platforms. Android uses the Play Install Referrer. iOS uses an opt-in pasteboard claim, with a ready-made code dialog as the fallback.
- No fingerprinting. Nothing device-identifying is collected.
- Plays well with other plugins. URL callbacks are never claimed, so sign-in SDKs and home widgets keep receiving theirs. UIScene apps are supported.
Install #
flutter pub add infly
1. Register the app #
In the infl.ly dashboard, register the app and issue a public key (pk_live_…).
| Setting | Example |
|---|---|
| App ID | goodjob |
| Path prefix | gj → links look like https://infl.ly/gj/Ab3xK91 |
| Custom scheme / host | myapp / link → fallback myapp://link/board/1234 |
| Android | package name, SHA-256 fingerprints (upload and Play app signing key), Play Store URL |
| iOS | bundle ID, Team ID, App Store URL |
2. Configure the platforms #
Android #
Add to the main activity in android/app/src/main/AndroidManifest.xml
(android:launchMode="singleTask" is recommended):
<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="infl.ly" android:pathPrefix="/gj/" />
</intent-filter>
<!-- Fallback for in-app browsers that block App Links -->
<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="myapp" android:host="link" />
</intent-filter>
The Play Install Referrer needs no setup.
iOS #
Enable Associated Domains and add applinks:infl.ly. Register the custom scheme
in Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array><string>myapp</string></array>
</dict>
</array>
The plugin ships a privacy manifest (PrivacyInfo.xcprivacy) and supports both
CocoaPods and Swift Package Manager.
3. Initialize and listen #
import 'package:infly/infly.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Infly.onLink.listen((InflyLink link) {
router.push(link.destination); // e.g. /board/1234
});
await Infly.initialize(
appId: 'goodjob',
publicKey: 'pk_live_xxx',
scheme: 'myapp', // must match the dashboard
useIosPasteboard: true, // iOS deferred deep links, see below
);
runApp(const MyApp());
}
onLink replays the most recent link to each new subscriber, so a listener attached
after the first frame still receives the link that launched the app. Links that
arrive while the app is running go through the same stream.
Only https://infl.ly/… and myapp://link/… URLs are treated as links. Anything else
the app receives (OAuth callbacks, myapp://calendar/1 from a home widget) is ignored.
Create a link #
final CreatedLink link = await Infly.createLink(
destination: '/board/1234',
metadata: const LinkMetadata(
title: 'Join my board',
description: 'Put a sticker on it with me!',
image: 'https://cdn.example.com/board.png', // https only
),
);
await SharePlus.instance.share(ShareParams(text: link.url));
The metadata becomes the preview in messengers and social apps (Open Graph and Twitter cards).
Deferred deep links #
The first launch after an install checks once for the link the user tapped before
installing, and delivers it through onLink with link.isDeferred == true.
| Platform | How | App code |
|---|---|---|
| Android | Play Install Referrer carries an opaque, single-use token | none |
| iOS | The landing page's App Store button copies https://infl.ly/gj/<id>?c=<code>; the SDK reads it on first launch |
useIosPasteboard: true |
| iOS fallback | The user types the code shown on the landing page | Infly.showClaimCodeDialog(context) |
On iOS the pasteboard is read only when all of these hold:
- the app opted in with
useIosPasteboard: true, - this is the first check for this install and the install is under 24 hours old (updated apps are never asked),
- the pasteboard holds a URL (checked without reading, so no prompt otherwise).
iOS 16+ shows its "Allow Paste" prompt once in that case. When the link matched, the SDK clears the pasteboard.
If nothing matched, offer the code dialog, for example from onboarding or a "Have an invite code?" menu:
final InflyLink? link = await Infly.showClaimCodeDialog(
context,
labels: const InflyClaimCodeLabels.korean(), // or your own texts
);
A matched code is also delivered through onLink.
Keys #
Ship only the public key (pk_) in the app. initialize throws if it receives a
secret key (sk_). Secret keys are for servers.
Using another link SDK at the same time #
If another SDK (for example AppsFlyer) or your own code also receives the URL that
opened the app, make it ignore infl.ly URLs. Otherwise it may navigate to the raw
path (/gj/Ab3xK91) and override the destination infly resolved.
API #
| Member | Purpose |
|---|---|
Infly.initialize(...) |
Configure the SDK, handle the launch link, run the deferred check |
Infly.onLink |
Stream of resolved links (InflyLink) |
Infly.createLink(...) |
Create a short link for sharing |
Infly.checkDeferredLink() |
Run the deferred check manually (force: true to repeat) |
Infly.showClaimCodeDialog(context) |
Ready-made code dialog |
Infly.redeemClaimCode(code) |
Redeem a code with your own UI |
Infly.handleUrl(url) |
Resolve a URL received through your own channel |