Skip to Content
Flutter SDK

Flutter SDK

The Flutter Redirectly SDK is a pure Dart implementation that enables dynamic links and app install tracking in your Flutter applications. No native code required!

Installation

Add flutter_redirectly to your pubspec.yaml:

dependencies: flutter_redirectly: ^2.1.6

Then run:

flutter pub get

Quick Setup

1. Get Your API Key

Sign up at redirectly.app  to get your free API key and create your subdomain.

Android Configuration

Add the following intent filter to android/app/src/main/AndroidManifest.xml:

<activity android:name=".MainActivity" android:exported="true" ...> <!-- Existing intent filters --> <!-- Add Redirectly deep link 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="YOUR_SUBDOMAIN.redirectly.app" /> </intent-filter> </activity>

Replace YOUR_SUBDOMAIN with your actual subdomain (e.g., myapp.redirectly.app).

iOS Configuration

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>

Important iOS Notes:

  • If Runner.entitlements doesn’t exist, create it
  • Ensure your app is properly signed
  • Enable Associated Domains capability in your Apple Developer account
  • Add the domain to your App ID in Apple Developer Portal

3. Initialize the SDK

Initialize Redirectly in your app (typically in main.dart or your app’s initialization):

import 'package:flutter_redirectly/flutter_redirectly.dart'; final redirectly = FlutterRedirectly(); void main() async { WidgetsFlutterBinding.ensureInitialized(); await redirectly.initialize(RedirectlyConfig( apiKey: 'your-api-key-here', debug: true, // Set to false in production )); runApp(MyApp()); }

Listen for incoming links when your app is running:

// Listen for incoming links redirectly.onLinkClick.listen((linkClick) { print('Link clicked: ${linkClick.originalUrl}'); print('Slug: ${linkClick.slug}'); print('Username: ${linkClick.username}'); // Navigate to the target content if (linkClick.linkResolution != null) { final target = linkClick.linkResolution!.target; // Handle navigation based on target URL _navigateToTarget(target); } });

Handle links when app is launched from a link:

// Check for initial link on app launch final initialLink = await redirectly.getInitialLink(); if (initialLink != null) { // App was opened via a Redirectly link print('App opened from link: ${initialLink.slug}'); _handleLinkClick(initialLink); }

5. Track App Installs

The SDK automatically tracks app installs on first launch. Listen for install attribution:

// Listen for app install events redirectly.onAppInstalled.listen((installEvent) { if (installEvent.matched) { // User installed app after clicking a Redirectly link print('Install attributed to: ${installEvent.username}/${installEvent.slug}'); print('Original click time: ${installEvent.clickedAt}'); // Show personalized onboarding or deep link to specific content _handleAttributedInstall(installEvent); } else { // Organic install (no prior link click) print('Organic app install'); _handleOrganicInstall(); } });

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

Create permanent links:

// Create a permanent link final link = await redirectly.createLink( slug: 'my-link', target: 'https://example.com/page', ); print('Created link: ${link.url}'); print('Link ID: ${link.id}');

Create temporary links (expires after specified time):

// Create a temporary link (expires in 1 hour) final tempLink = await redirectly.createTempLink( target: 'https://example.com/page', ttlSeconds: 3600, // 1 hour ); print('Temp link: ${tempLink.url}'); print('Expires at: ${tempLink.expiresAt}');

Complete Example

Here’s a complete example showing the full attribution flow:

import 'package:flutter/material.dart'; import 'package:flutter_redirectly/flutter_redirectly.dart'; 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, )); // Handle ongoing link clicks redirectly.onLinkClick.listen(_handleLinkClick); // Handle app install attribution redirectly.onAppInstalled.listen(_handleAppInstall); // Handle initial link if app was opened via link final initialLink = await redirectly.getInitialLink(); if (initialLink != null) { _handleLinkClick(initialLink); } } void _handleLinkClick(RedirectlyLinkClick linkClick) { print('Link clicked: ${linkClick.slug}'); if (linkClick.linkResolution != null) { // Navigate based on link target _navigateToTarget(linkClick.linkResolution!.target); } } void _handleAppInstall(RedirectlyAppInstallResponse install) { if (install.matched) { // Show attributed install welcome _showAttributedWelcome(install); } else { // Show standard onboarding _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(); // Navigate to the content they were originally trying to reach if (install.linkResolution != null) { _navigateToTarget(install.linkResolution!.target); } }, child: Text('Get Started'), ), ], ), ); } void _navigateToTarget(String target) { // Handle navigation based on your app's routing print('Navigating to: $target'); } void _showStandardOnboarding() { // Show your standard onboarding flow } @override Widget build(BuildContext context) { return MaterialApp( title: 'My App', home: Scaffold( appBar: AppBar(title: Text('My App')), body: Center(child: Text('Welcome')), ), ); } }

Error Handling

Handle errors gracefully:

try { final link = await redirectly.createLink( slug: 'test', target: 'https://example.com', ); } on RedirectlyError catch (e) { print('Error: ${e.message}'); // Handle error (e.g., show user-friendly message) }

API Reference

Configuration

RedirectlyConfig({ required String apiKey, // Your API key String? baseUrl, // Optional: custom domain bool enableDebugLogging, // Optional: debug mode })

Models

RedirectlyLink({ String id, // Unique link ID String slug, // Link slug/identifier String target, // Target URL String url, // Full Redirectly URL int clickCount, // Number of clicks DateTime createdAt, // Creation timestamp Map<String, dynamic>? metadata, // Custom metadata })
RedirectlyTempLink({ String id, // Unique link ID String slug, // Auto-generated slug String target, // Target URL String url, // Full Redirectly URL int ttlSeconds, // Time to live in seconds DateTime expiresAt, // Expiration timestamp DateTime createdAt, // Creation timestamp })
RedirectlyLinkClick({ String originalUrl, // Original clicked URL String slug, // Link slug String username, // Link owner username DateTime receivedAt, // When click was received RedirectlyLinkResolution? linkResolution, // Link details RedirectlyError? error, // Error if any })

RedirectlyAppInstallResponse (Install Attribution)

RedirectlyAppInstallResponse({ bool matched, // Whether install was attributed String? username, // Attribution username (if matched) String? slug, // Attribution slug (if matched) DateTime? clickedAt, // Original click time (if matched) RedirectlyLinkResolution? linkResolution, // Original link details DateTime loggedAt, // When install was logged })

Streams

// Listen for link clicks (app running or launch) Stream<RedirectlyLinkClick> redirectly.onLinkClick // Listen for app install events (first launch only) Stream<RedirectlyAppInstallResponse> redirectly.onAppInstalled

Testing Install Attribution

During development, test install attribution by:

  1. Delete and reinstall your app to simulate first install
  2. Clear app data on Android or delete from device on iOS
  3. Click a Redirectly link → install app → open app
  4. The onAppInstalled event should fire with matched: true

The plugin automatically prevents duplicate install logging by creating a tracking file on first launch.

Best Practices

  1. Initialize Early - Initialize Redirectly as early as possible in your app lifecycle
  2. Handle Errors - Always wrap API calls in try-catch blocks
  3. Test Deep Links - Test both app-installed and app-not-installed scenarios
  4. Privacy - Inform users about link tracking in your privacy policy
  5. Debug Mode - Use debug: true during development, false in production

Support

Package Info

Last updated on