Flutter SDK


Installation

Add the dependency to your pubspec.yaml:

dependencies:
  dynalink_flutter: ^0.0.13
flutter pub get

Platform Setup

Android

Add both intent filters to AndroidManifest.xml:

<!-- Custom scheme (fallback) -->
<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="dynalink-{projectId}" android:host="dynalink.app" />
</intent-filter>

<!-- App Links (verified) -->
<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="{projectPrefix}.dynalink.app" />
</intent-filter>

Add the Asset Links JSON in the Admin Panel under Project → Settings.

iOS

Add to ios/Runner/Info.plist:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>{bundleId}</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>dynalink-{projectId}</string>
    </array>
  </dict>
</array>
<key>NSUserActivityTypes</key>
<array>
  <string>NSUserActivityTypeBrowsingWeb</string>
</array>
<key>CFBundleAssociatedDomains</key>
<array>
  <string>applinks:{projectPrefix}.dynalink.app</string>
</array>
<key>FlutterDeepLinkingEnabled</key>
<false/>

Add the Apple App Site Association file in the Admin Panel under Project → Settings.

Initialize

Call initialize before runApp:

import 'package:dynalink_flutter/dynalink_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Dynalink.initialize(
    publicKey: 'YOUR_PROJECT_KEY',
    projectId: 'YOUR_PROJECT_ID',
  );

  runApp(const MyApp());
}

Handling Deep Link Events

Listen to dynamicLinkStream to receive every processed link:

Dynalink.instance.dynamicLinkStream.listen((DynalinkEvent event) {
  // Navigate to the resolved destination
  navigateTo(event.actualUrl);

  // Campaign / UTM context
  if (event.hasCampaign) {
    analytics.setCampaign(event.campaignId, event.utmCampaign);
  }

  // Forward click IDs to your ad-network measurement SDKs
  if (event.gclid  != null) googleAds.reportConversion(event.gclid!);
  if (event.fbclid != null) facebookCapi.reportInstall(event.fbclid!, event.fbclidCapturedAt);
  if (event.ttclid != null) tiktokEvents.reportInstall(event.ttclid!);
});

DynalinkEvent Fields

Field Type Description
actualUrl String Resolved destination URL
campaignId int? Campaign ID
campaignName String? Campaign display name
utmSource String? UTM source
utmMedium String? UTM medium
utmCampaign String? UTM campaign slug
utmTerm String? UTM term
utmContent String? UTM content
gclid String? Google Ads click ID
gbraid String? Google Ads enhanced click ID (iOS 14.5+)
fbclid String? Meta (Facebook/Instagram) click ID
fbclidCapturedAt DateTime? When fbclid was first captured in the browser
ttclid String? TikTok click ID
twclid String? X (Twitter) click ID
liFatId String? LinkedIn first-party ad tracking ID
attributedAt DateTime? When the backend confirmed the install match
hasCampaign bool Convenience getter — true when campaignId != null
hasClickIds bool Convenience getter — true when any click ID is non-null

Persisted Attribution Getters

Click IDs are written to SharedPreferences automatically. Use these getters if you need the values outside of the stream:

final gclid    = await Dynalink.instance.getLastGclid();
final gbraid   = await Dynalink.instance.getLastGbraid();
final fbclid   = await Dynalink.instance.getLastFbclid();
final ttclid   = await Dynalink.instance.getLastTtclid();
final twclid   = await Dynalink.instance.getLastTwclid();
final liFatId  = await Dynalink.instance.getLastLiFatId();
final campaignId = await Dynalink.instance.getLastCampaignId();

Attribution API

Query attribution data on demand:

// By device fingerprint
final result = await Dynalink.instance.getAttributionByFingerprint(fingerprint);

// By DynaLink short code
final result = await Dynalink.instance.getAttributionByCode('SUMR99');

if (result != null && result.matched) {
  print('gclid: ${result.gclid}');
  print('fbclid: ${result.fbclid}');
  print('attributed at: ${result.attributedAt}');
}

Creating Links

final url = await Dynalink.instance.createShortenedLink(
  CreateDynalinkForm(
    url: 'https://yourapp.io?screen=offer&id=99',
    isDeepLink: true,
    iosUrl: 'https://apps.apple.com/app/id123456789',
    androidUrl: 'https://play.google.com/store/apps/details?id=com.yourapp',
    fallbackUrl: 'https://yourapp.io',
    campaignId: 4,
  ),
);

Campaign Management

// List campaigns
final campaigns = await Dynalink.instance.getCampaigns(status: 'active');

// Get stats (last N days)
final stats = await Dynalink.instance.getCampaignStats(4, days: 7);
print('Total clicks: ${stats?.totalClicks}');
for (final day in stats?.clicksPerDay ?? []) {
  print('${day.date}: ${day.count}');
}

// Create
final campaign = await Dynalink.instance.createCampaign(
  CreateCampaignForm(name: 'Summer 2026', utmSource: 'google', utmMedium: 'cpc'),
);

// Update
await Dynalink.instance.updateCampaign(campaign.id, CreateCampaignForm(name: 'Summer 2026 — Revised'));

// Delete
await Dynalink.instance.deleteCampaign(campaign.id);

How Attribution Works

  1. Click — User clicks a DynaLink. The fingerprint page captures click IDs from the URL and stores them against the device fingerprint.
  2. Redirect — User is sent to the App Store or Play Store. On Android, the original DynaLink URL is embedded as the install referrer.
  3. Install & Open — User installs and opens the app. The SDK runs the attribution flow:
    • Android — reads install referrer URL → parses code → fetches click IDs from attribution API.
    • iOS (clipboard) — if the DynaLink URL is in the clipboard, processes it the same way.
    • iOS (fingerprint) — regenerates the device fingerprint → finds the pending link → marks it as confirmed → fetches click IDs from the attribution API.
  4. Event — A DynalinkEvent is emitted on dynamicLinkStream with the full payload. Values are also saved to SharedPreferences.