Snapshot do README original do repositório-fonte vinculado. Consulte o repositório para o conteúdo mais recente.
Flutter Redirectly
A Flutter package for creating and handling dynamic links using your own Redirectly backend.
Installation
dependencies:
flutter_redirectly: ^2.1.1
Quick Setup
1. Get Your API Key
Get your free API key from redirectly.app - sign up and create your subdomain to get started.
2. Configure Deep Links
Android - Add to android/app/src/main/AndroidManifest.xml:
<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="YOUR_SUBDOMAIN.redirectly.app" />
</intent-filter>
iOS - Add Associated Domains to ios/Runner/Runner.entitlements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:YOUR_SUBDOMAIN.redirectly.app</string>
</array>
</dict>
</plist>
If this file doesn't exist, create it. Also ensure your app is properly signed and the Associated Domains capability is enabled in your Apple Developer account.
3. Initialize
import 'package:flutter_redirectly/flutter_redirectly.dart';
final redirectly = FlutterRedirectly();
await redirectly.initialize(RedirectlyConfig(
apiKey: 'your-api-key-here',
debug: true,
));
4. Handle Links
redirectly.onLinkClick.listen((linkClick) {
print('Link: ${linkClick.originalUrl}');
print('Slug: ${linkClick.slug}');
});
final initialLink = await redirectly.getInitialLink();
if (initialLink != null) {
}
5. Track App Installs
The plugin automatically tracks app installs on first launch. Listen for install attribution events:
redirectly.onAppInstalled.listen((installEvent) {
if (installEvent.matched) {
print('Install attributed to: ${installEvent.username}/${installEvent.slug}');
print('Original click time: ${installEvent.clickedAt}');
_handleAttributedInstall(installEvent);
} else {
print('Organic app install');
_handleOrganicInstall();
}
});
void _handleAttributedInstall(RedirectlyAppInstallResponse install) {
if (install.linkResolution != null) {
final target = install.linkResolution!.target;
}
}
What gets tracked:
- First app launch only (subsequent launches are ignored)
- Device information (OS, version, timezone, language)
- App information (version, build number)
- Attribution matching with prior link clicks (if any)
Privacy-friendly:
- No personal information is collected
- Only standard app/device metadata
- Tracking file prevents duplicate logging
- All data stays within your Redirectly account
Deterministic Android attribution (Install Referrer)
Deferred deep link attribution used to match installs to prior link clicks
only with a server side fingerprint (IP address and IP derived geo data).
That match breaks when the device's IP changes between the link click and
the first app launch, for example when switching from WiFi to cellular.
Since 2.2.0 the SDK makes Android attribution deterministic automatically.
When Redirectly redirects a user to the Play Store, it embeds the click
identifier in the Google Play Install Referrer; on the first launch the SDK
reads it back (via the bundled
play_install_referrer
plugin) and the backend matches the exact click, regardless of IP changes.
No setup is required.
Notes:
- The referrer is read once, on the first launch install tracking call, and
only on Android. On iOS, when Google Play Services are unavailable, or
when the read fails or takes longer than 5 seconds, the SDK simply omits
the referrer and the backend falls back to fingerprint matching.
- Organic installs carry no Redirectly referrer and are reported as organic,
as before.
- If you need a custom referrer source, the optional
installReferrerProvider callback in RedirectlyConfig overrides the
built-in read:
await redirectly.initialize(RedirectlyConfig(
apiKey: 'your-api-key-here',
installReferrerProvider: () async => myCustomReferrerSource(),
));
6. Create Links
final link = await redirectly.createLink(
slug: 'my-link',
target: 'https://example.com',
);
print('Created: ${link.url}');
final tempLink = await redirectly.createTempLink(
target: 'https://example.com',
ttlSeconds: 3600,
);
print('Temp link: ${tempLink.url}');
Complete Attribution Flow
Here's how to implement a complete attribution flow:
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final redirectly = FlutterRedirectly();
@override
void initState() {
super.initState();
_initializeRedirectly();
}
Future<void> _initializeRedirectly() async {
await redirectly.initialize(RedirectlyConfig(
apiKey: 'your-api-key',
debug: true,
));
redirectly.onLinkClick.listen(_handleLinkClick);
redirectly.onAppInstalled.listen(_handleAppInstall);
final initialLink = await redirectly.getInitialLink();
if (initialLink != null) {
_handleLinkClick(initialLink);
}
}
void _handleLinkClick(RedirectlyLinkClick linkClick) {
print('Link clicked: ${linkClick.slug}');
if (linkClick.linkResolution != null) {
_navigateToTarget(linkClick.linkResolution!.target);
}
}
void _handleAppInstall(RedirectlyAppInstallResponse install) {
if (install.matched) {
_showAttributedWelcome(install);
} else {
_showStandardOnboarding();
}
}
void _showAttributedWelcome(RedirectlyAppInstallResponse install) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Welcome!'),
content: Text('Thanks for installing from our ${install.slug} link!'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
if (install.linkResolution != null) {
_navigateToTarget(install.linkResolution!.target);
}
},
child: Text('Get Started'),
),
],
),
);
}
void _navigateToTarget(String target) {
print('Navigating to: $target');
}
}
Error Handling
try {
final link = await redirectly.createLink(slug: 'test', target: 'https://example.com');
} on RedirectlyError catch (e) {
print('Error: ${e.message}');
}
API Reference
Configuration
RedirectlyConfig({
required String apiKey,
bool enableDebugLogging,
InstallReferrerProvider? installReferrerProvider,
})
Models
RedirectlyLink({
String id, slug, target, url,
int clickCount,
DateTime createdAt,
Map<String, dynamic>? metadata,
})
RedirectlyTempLink({
String id, slug, target, url,
int ttlSeconds,
DateTime expiresAt, createdAt,
})
RedirectlyLinkClick({
String originalUrl, slug, username,
DateTime receivedAt,
RedirectlyError? error,
})
RedirectlyAppInstallResponse({
bool matched,
String? username, slug,
DateTime? clickedAt,
RedirectlyLinkResolution? linkResolution,
DateTime loggedAt,
})
Streams
Stream<RedirectlyLinkClick> redirectly.onLinkClick
Stream<RedirectlyAppInstallResponse> redirectly.onAppInstalled
Testing Install Attribution
During development, you can test install attribution by:
- Delete and reinstall your app to simulate first install
- Clear app data on Android or delete from device on iOS
- Click a Redirectly link → install app → open app
- The
onAppInstalled event should fire with matched: true
The plugin automatically prevents duplicate install logging by creating a tracking file on first launch.
Example
See the example app for a complete implementation.
Support
License
MIT License - see LICENSE file.