> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appdna.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter Deep Links

> Handle deferred deep links for first-launch routing and attribution

<Info>
  **Supported on:** iOS SDK `1.0.61+` · Android SDK `1.0.33+` · Flutter SDK `1.0.3+`
</Info>

The deep links module handles deferred deep links -- routing users to the correct screen on first launch after installing from a link. This enables attribution tracking, referral programs, and contextual first-launch experiences.

## Deferred Deep Links

A deferred deep link works across the install boundary:

1. User clicks a link on the web (e.g., `https://yourapp.com/workout/123?ref=instagram`)
2. User is redirected to the App Store or Play Store and installs your app
3. On first launch, the SDK resolves the original link and returns the destination

```dart theme={null}
final deepLink = await AppDNA.checkDeferredDeepLink();
if (deepLink != null) {
  // deepLink.screen = "/workout/123"
  // deepLink.params = {"ref": "instagram"}
  navigate(deepLink.screen, deepLink.params);
}
```

<Info>
  `checkDeferredDeepLink` is a static method on `AppDNA`. It should be called once on first launch, typically after `configure()` completes. The SDK returns `null` if no deferred link is found or if the app was not installed from a link. On Android, resolution uses the Google Install Referrer API plus AppDNA's stored visitor context.
</Info>

## Deferred Deep Link Check

`checkDeferredDeepLink` is a top-level static method on `AppDNA`, not part of the deep links module:

```dart theme={null}
final deepLink = await AppDNA.checkDeferredDeepLink();
if (deepLink != null) {
  navigate(deepLink.screen, deepLink.params);
}
```

## Module Access

```dart theme={null}
final deepLinks = AppDNA.deepLinks;
```

### Module Methods

| Method        | Signature                                            | Description                                              |
| ------------- | ---------------------------------------------------- | -------------------------------------------------------- |
| `setDelegate` | `void setDelegate(AppDNADeepLinkDelegate? delegate)` | Set a delegate for deep link callbacks                   |
| `handleURL`   | `Future<void> handleURL(String url)`                 | Pass an incoming deep link URL to the SDK for processing |

## DeferredDeepLink

| Property    | Type                  | Description                                      |
| ----------- | --------------------- | ------------------------------------------------ |
| `screen`    | `String`              | Target screen path (e.g., `"/workout/123"`)      |
| `params`    | `Map<String, String>` | Additional parameters (e.g., referrer, campaign) |
| `visitorId` | `String`              | The visitor identifier from the originating link |

## AppDNADeepLinkDelegate

Register a delegate for deep link events:

```dart theme={null}
abstract class AppDNADeepLinkDelegate {
  /// Veto. Return false to suppress deep-link processing.
  bool shouldOpen(String url, Map<String, dynamic> params);

  void onDeepLinkReceived(String url, Map<String, dynamic> params);
}
```

`shouldOpen` is a veto called BEFORE the SDK processes a deep link. Return `false` to defer routing (for example until after login completes) and handle the URL yourself once the user is authenticated. Return `true` to let the SDK proceed with default handling.

### Example Implementation

```dart theme={null}
import 'package:appdna_sdk/appdna_sdk.dart';

class LinkHandler extends AppDNADeepLinkDelegate {
  @override
  bool shouldOpen(String url, Map<String, dynamic> params) {
    // Return false to defer routing (e.g., until login completes).
    return true;
  }

  @override
  void onDeepLinkReceived(String url, Map<String, dynamic> params) {
    // Route based on the incoming URL
    final uri = Uri.parse(url);
    route(uri.path, params);
  }

  void route(String screen, Map<String, dynamic> params) {
    if (screen.startsWith("/workout/")) {
      final id = screen.substring("/workout/".length);
      showWorkout(id);
    } else if (screen == "/referral") {
      showReferralWelcome(params["ref"] as String?);
    } else {
      showHome();
    }
  }

  void showWorkout(String id) { /* ... */ }
  void showReferralWelcome(String? referrer) { /* ... */ }
  void showHome() { /* ... */ }
}

AppDNA.deepLinks.setDelegate(LinkHandler());
```

## Platform Setup

Deep links require platform-specific configuration in your iOS and Android host projects.

### iOS — Universal Links

To support Universal Links on iOS, configure your app:

1. Enable **Associated Domains** in your target's **Signing & Capabilities**.
2. Add your domain: `applinks:yourapp.com`
3. Host an `apple-app-site-association` file on your domain.

In your iOS host (`AppDelegate.swift` or `SceneDelegate.swift`), forward incoming URLs to the SDK:

```swift theme={null}
// SceneDelegate
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let url = userActivity.webpageURL {
        AppDNA.deepLinks.handleURL(url)
    }
}
```

### Android — App Links

1. Add intent filters to your `AndroidManifest.xml`:

```xml theme={null}
<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="yourapp.com" />
</intent-filter>
```

2. Host a Digital Asset Links file at `https://yourapp.com/.well-known/assetlinks.json`.
3. In your `MainActivity.kt`, forward the incoming intent's data URL to the SDK:

```kotlin theme={null}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    intent?.dataString?.let { AppDNA.deepLinks.handleURL(it) }
}

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    intent.dataString?.let { AppDNA.deepLinks.handleURL(it) }
}
```

### Forwarding from Dart

If your app already parses deep links in Dart (e.g. via the `uni_links` package), forward them to the SDK so triggers, attribution, and the delegate fire consistently:

```dart theme={null}
await AppDNA.deepLinks.handleURL(incomingUri.toString());
```

## Auto-Tracked Events

| Event                         | Trigger                                          |
| ----------------------------- | ------------------------------------------------ |
| `deep_link_handled`           | A deep link URL is processed                     |
| `deferred_deep_link_resolved` | A deferred deep link is resolved on first launch |

## Full Example

```dart theme={null}
import 'package:appdna_sdk/appdna_sdk.dart';

class AppCoordinator extends AppDNADeepLinkDelegate {
  AppCoordinator() {
    AppDNA.deepLinks.setDelegate(this);
  }

  Future<void> handleFirstLaunch(String currentUserId) async {
    // Check for deferred deep link on first launch
    final deepLink = await AppDNA.checkDeferredDeepLink();

    if (deepLink != null) {
      // User installed from a link -- route to the target screen
      route(deepLink.screen, deepLink.params);

      // Track the visitor as a user trait
      await AppDNA.identify(currentUserId, traits: {
        "visitor_id": deepLink.visitorId,
      });
    } else {
      // Normal install -- show default onboarding
      showOnboarding();
    }
  }

  // AppDNADeepLinkDelegate

  @override
  bool shouldOpen(String url, Map<String, dynamic> params) => true;

  @override
  void onDeepLinkReceived(String url, Map<String, dynamic> params) {
    final uri = Uri.parse(url);
    route(uri.path, params);
  }

  void route(String screen, Map<String, dynamic> params) {
    // Route to the correct screen based on the path
  }

  void showOnboarding() { /* ... */ }
}
```

<Note>
  Deep link routes are configured in the Console under **Engagement > Deep Links**. The SDK resolves deferred links by checking the Google Install Referrer (Android) plus AppDNA's stored visitor context on first launch.
</Note>
