WarpLink
SDKsFlutter

Flutter SDK

Integrate the WarpLink Flutter SDK for Universal Links on iOS, App Links on Android, deferred deep links, and install attribution.

Requirements: Flutter 3.38+, Dart 3.10+, iOS 15+ and/or Android API 26+. The plugin runs on iOS and Android only. Calling it on another platform throws an UnsupportedError.

Source: github.com/WarpLinkApp/warplink-flutter-sdk

Installation

This adds the package to your pubspec.yaml:

The plugin wraps the native WarpLink iOS and Android SDKs, so link handling, deduplication, and attribution behave exactly as they do in a native app. The iOS SDK is distributed through Swift Package Manager. If your Flutter version does not use Swift Package Manager by default, turn it on once:

There is no Podfile change and no pod to install.

Platform Setup

A Flutter app is one WarpLink app that covers both platforms. Register it once in the dashboard with iOS and Android selected, and do not create a second app for the other platform. Both platforms need the link domain configured. The plugin registers itself with Flutter's iOS and Android lifecycles, so you edit no AppDelegate, SceneDelegate, or MainActivity.

iOS

  1. Open ios/Runner.xcworkspace in Xcode, select the Runner target > Signing & Capabilities
  2. Add Associated Domains > applinks:yourapp.aplnk.to
  3. Enable Associated Domains in your App ID at developer.apple.com

Find your app's subdomain in the dashboard: open the app, then Settings. Or see Your app's own link address.

That is the whole iOS setup. The capability can also live in ios/Runner/Runner.entitlements.

Android

Add the intent filter to your main Activity in android/app/src/main/AndroidManifest.xml:

Then set the minimum SDK to 26 in android/app/build.gradle.kts. The Flutter default can be lower, and the manifest merge then fails because the native WarpLink SDK needs API 26:

Keep the android:launchMode your Flutter project already has. The default Flutter template uses singleTop, which routes a link tapped on a running app to the existing Activity. standard and singleInstance are not supported.

Serving links from a custom domain? Add it to the associated domains and the intent filter alongside yourapp.aplnk.to, then declare it in configure().

Every app has its own subdomain, yourapp.aplnk.to. Add that exact host, never a wildcard such as applinks:*.aplnk.to on iOS. If your app existed before subdomains, keep aplnk.to listed next to the new address: links you already have stay on aplnk.to and keep opening your app. See Your app's own link address.

Flutter has its own deep link handler, on by default. It forwards every incoming link to your router as a route, so https://yourapp.aplnk.to/abc123 becomes a lookup for /abc123 and can land on an unknown-route page. Switch it off and let onLink drive navigation.

In ios/Runner/Info.plist:

In android/app/src/main/AndroidManifest.xml, inside your main <activity>:

If your app must keep Flutter's own deep linking for other, non-WarpLink domains, WarpLink cannot share the same links safely. Keep the WarpLink domains out of Flutter's route table. See Routing with go_router.

Configure the SDK

Call WarpLink.configure() once from your root widget's initState. Pass an onLink callback and the SDK wires cold start, warm start, and the deferred deep link check into that single sink.

The credential below is an SDK key, created in the dashboard under Keys > SDK keys. An API key looks identical but cannot record installs: deep links still resolve, so the integration looks healthy while every attribution call is rejected. If your organization has several apps, create the key for this app: a key tied to one app is refused for any other app with KEY_APP_MISMATCH.

navigateTo waits for the navigator before it pushes a route, so a link that launches the app is never dropped. See Deep Links for navigatorKey and navigateTo.

onLink receives a WarpLinkEvent, which is either a LinkEvent or an ErrorEvent. A switch over the two is exhaustive.

configure() returns a Future<void> that completes when native configuration completes. A configuration failure is delivered to onLink as an ErrorEvent when you pass onLink, as in the example above. Only when you omit onLink does the returned future complete with a WarpLinkException, so await it in that case:

The SDK always recognizes aplnk.to. List your app's subdomain, yourapp.aplnk.to, and any custom domain you serve links from in linkDomains. configure() passes the list straight to the native iOS and Android SDKs, so a link on those hosts resolves on the very first launch, with no network round trip.

Without it, the native side only learns your domain after it has reached the server, and the decision to claim a URL is made synchronously before that answer arrives. That is why a fresh install can hand your own link back to your router.

The native declaration works too, and is read before any Dart runs: a WarpLinkDomains array in ios/Runner/Info.plist on iOS, an app.warplink.DOMAINS meta-data entry in AndroidManifest.xml on Android. See the iOS and Android guides.

Every form is optional and additive. Domains are normalized natively (trimmed, lowercased, a full URL reduced to its host), so paste whichever form you have.

Opt Out of Automatic Handling

Both pieces are on by default. Disable either to drive it yourself with the manual methods:

automaticDeepLinks only takes effect when you pass onLink.

Testing

iOS: Physical Device Required

  1. Build and run on a physical device with flutter run -d <device-id>
  2. Open your test link in Safari (e.g., https://yourapp.aplnk.to/abc123)
  3. The app should open and trigger the deep link callback
  4. Check the Xcode console for [WarpLink] log messages

Universal Links do not work on the iOS Simulator. Always test the iOS path on a physical device.

Android: Physical Device or Emulator

Android App Links work on both a physical device and the emulator, unlike iOS Universal Links.

Testing a deferred deep link, meaning a click that happens before the app is installed, needs a delete/uninstall-then-reinstall cycle so the SDK treats it as a fresh install. See Testing in the Deferred Deep Links guide.

Next Steps

On this page