dynalinks 2.0.0 copy "dynalinks: ^2.0.0" to clipboard
dynalinks: ^2.0.0 copied to clipboard

Dynalinks SDK for Flutter - Deferred deep linking and attribution for iOS and Android apps.

Changelog #

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

2.0.0 - 2026-08-01 #

Breaking #

  • Raised the minimum supported version to Flutter 3.44 / Dart 3.12. This is required for both Swift Package Manager support and the UIScene life cycle APIs.
  • A link that launched the app is now delivered only through getInitialLink(), never through the onDeepLinkReceived stream. Previously, if a stream listener happened to be attached before the link resolved, the launch link arrived there instead. If your app only listens to the stream, it will stop seeing cold-start links - you must also call getInitialLink() on startup. Links that arrive while the app is running are unchanged and still come through the stream.

Added #

  • Swift Package Manager support: the iOS plugin now ships a Package.swift alongside its podspec, so it builds under either dependency manager. Swift Package Manager is the default from Flutter 3.44 onwards, and the CocoaPods registry becomes read-only on 2 December 2026.
  • UIScene life cycle support: the plugin now implements FlutterSceneLifeCycleDelegate and registers as a scene delegate, handling scene(_:willConnectTo:options:) (cold start) and scene(_:continue:) (warm continuation).
  • The privacy manifest is now bundled with the plugin. It was previously present in the repository but never shipped, because the podspec's resource_bundles entry was commented out.

Fixed #

  • Cold-start links are no longer dropped, on either platform. When a link launched the app from a terminated state, the platform delivered it before Dart had run and called configure, so the plugin resolved it against an unconfigured SDK, the call failed with notConfigured, and the link was discarded. The link is now held until the SDK is ready and resolved immediately afterwards. getInitialLink() waits for that resolution instead of racing it and reporting "no link" while the request is still in flight. On iOS this affected both life cycles; on Android the launching intent is read when the plugin attaches to the activity, which is likewise before configure.
  • getInitialLink() no longer swallows the link when called from initState. On Android it marked the initial link as consumed before checking whether one had arrived, so a call that beat the in-flight resolution returned null and permanently discarded the link. It is now marked consumed only when a result is actually handed to Dart.
  • A link arriving while no listener is attached is no longer lost. It was held for getInitialLink(), which reports "no link" once the launch link has been taken - so a link tapped while a listener was briefly detached, during a route change say, was unreachable on both channels. Such links are now held for the next listener and delivered when one attaches.
  • A slow first-launch link is no longer stranded. The wait that stops getInitialLink() hanging used to give up sooner than the request beneath it could finish, and the link then landed in a slot the app had already been told was empty. The wait now outlasts the request, and a launch link that still arrives after it goes to the stream rather than nowhere.
  • A failed configure() no longer throws away the cold-start link. The buffered link was the only copy, so an app that caught the error and configured again - after fetching its API key, say - had already lost it. Waiting calls are still answered immediately; if an app has already been told there is no initial link, the result of the retry is delivered on the stream instead.
  • Legacy iOS app-delegate cold starts still reach getInitialLink(). Launch options now distinguish the launch continuation from a link that arrives while the app is running, so each reaches the correct channel.
  • The no-listener stream backlog is bounded. It retains the 32 most recent links and discards the oldest result when an app leaves the stream unsubscribed for an extended period.
  • Android no longer drops a launch link when the engine outlives the activity. The launch intent was read once per plugin instance, so with a cached engine, add-to-app or a FlutterFragment, a tap that started a fresh activity never had its intent read. The guard is now keyed on the link itself.
  • Android no longer resolves intents that are not App Links. Any intent data was resolved, so a custom-scheme or content URI opened elsewhere in the app cost a network round trip and put a non-match on the stream. Only http and https are handled now, matching iOS.
  • Android no longer stops working when its plugin instance is reattached to a new engine. The coroutine scope was cancelled on detach and never rebuilt, so every later call silently did nothing and left its Dart future unanswered.
  • The same link arriving twice in quick succession is now delivered once, on both platforms.
  • A second getInitialLink() call no longer strands the first. On iOS, when a call arrived while an earlier one was still waiting for a link to resolve, it replaced the held reply and the first Future never completed. All waiting calls are now answered.
  • getInitialLink() no longer hangs when the link never arrives. A call made while a link was still buffered or resolving now waits for it on both platforms, and fails open with null rather than waiting forever if the resolution never lands. A configure() that fails answers waiting calls straight away instead of leaving them on that timeout.
  • reset() no longer leaves a link resolution in flight. A resolution started before reset() could land afterwards and repopulate the initial link, so a stale link surfaced after the SDK had been torn down.
  • Android no longer replays the launch link when the activity is recreated. The launching intent was re-read on every attach, so an activity recreation outside a configuration change delivered the cold-start link a second time.
  • checkForDeferredDeepLink() now reports isDeferred as true on Android, matching iOS. The result previously carried whatever the underlying SDK set.
  • reset() now reports failures as a DynalinksException like every other method, instead of letting a raw platform error escape.
  • Universal Links no longer stop working on apps that have adopted the UIScene life cycle. Incoming links were handled only via application(_:continue:restorationHandler:), which iOS does not call once an app declares a scene manifest. Such apps received no deep links at all, with no error. Apps still on the app delegate life cycle are unaffected and keep working.

Changed #

  • Migrated to Flutter's built-in Kotlin support: the Android build no longer applies the Kotlin Gradle Plugin itself and uses kotlin { compilerOptions { } } in place of kotlinOptions { }. Applying KGP is deprecated in Flutter 3.44 and fails to build under AGP 9.
  • Updated the iOS SDK dependency to ~> 1.0.4.
  • The Android setup guide no longer tells you to set launchMode. It previously prescribed singleTask, which deep linking does not require - Flutter already generates singleTop, and that is sufficient. The setup step is now just the intent filter. Whatever launch mode your app uses today keeps working.

1.0.4 - 2026-02-06 #

Changed #

  • Updated Android SDK dependency to 1.0.3
    • Adds support for deferred deep linking with full URL preservation in referrer parameter
    • Improves unnamed link handling with query parameter preservation

1.0.3 - 2026-01-21 #

Fixed #

  • CRITICAL: Updated iOS SDK dependency to require minimum version 1.0.3 (~> 1.0.3)
    • Fixes build error: "Value of type 'DeepLinkResult.LinkData' has no member 'referrer'"
    • Previous ~> 1.0 allowed iOS SDK 1.0.2 which lacks attribution fields
    • Users should run cd ios && pod update DynalinksSDK after updating

1.0.2 - 2026-01-21 #

Added #

  • Added attribution tracking fields to LinkData model:
    • referrer - Referrer tracking parameter (e.g., "utm_source=facebook&utm_campaign=summer")
    • providerToken - Apple Search Ads attribution token (pt parameter)
    • campaignToken - Campaign identifier for attribution (ct parameter)
  • Updated README with attribution tracking examples
  • iOS plugin bridge now passes attribution fields from native SDK 1.0.3
  • Android plugin bridge now passes attribution fields from native SDK 1.0.1

Changed #

  • Updated Android SDK dependency to 1.0.1 (adds iosDeferredDeepLinkingEnabled field)
  • iOS SDK dependency remains at ~> 1.0 (automatically resolves to 1.0.3)

1.0.1 - 2026-01-21 #

Fixed #

  • Fixed TypeError when parsing nested maps from Android platform channels
  • Android platform now properly handles Map<Object?, Object?> to Map<String, dynamic> conversion for link data
  • Resolves: type '_Map<Object?, Object?>' is not a subtype of type 'Map<String, dynamic>' in type cast error

1.0.0 - 2025-01-06 #

Added #

  • Initial release of the Dynalinks Flutter SDK
  • Deferred Deep Linking: Check for deep links that were clicked before app installation
  • Universal Links (iOS): Automatic handling of incoming Universal Links
  • App Links (Android): Automatic handling of incoming App Links
  • Manual Link Resolution: Resolve any Dynalinks URL programmatically

Features #

  • Dynalinks.configure() - Initialize the SDK with your API key
  • Dynalinks.checkForDeferredDeepLink() - Check for deferred deep links on first launch
  • Dynalinks.handleDeepLink(Uri) - Manually resolve a deep link URI
  • Dynalinks.getInitialLink() - Get the link that launched the app (cold start)
  • Dynalinks.onDeepLinkReceived - Stream of incoming links while app is running
  • Dynalinks.reset() - Reset SDK state (for testing)

Models #

  • DeepLinkResult - Result of deep link resolution with match confidence
  • LinkData - Complete link data including deep link value, URLs, and social metadata
  • Confidence - Match confidence level (high, medium, low)
  • DynalinksLogLevel - SDK logging verbosity levels

Exceptions #

  • NotConfiguredException - SDK not configured before use
  • InvalidApiKeyException - Invalid API key provided
  • SimulatorException - Running on iOS Simulator or Android Emulator
  • NetworkException - Network request failed
  • ServerException - Server returned an error
  • NoMatchException - No matching link found
  • Platform-specific exceptions for Android Install Referrer

Platform Support #

  • iOS 16.0+
  • Android API 21+ (Android 5.0 Lollipop)
  • Flutter 3.3.0+
1
likes
160
points
360
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Dynalinks SDK for Flutter - Deferred deep linking and attribution for iOS and Android apps.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on dynalinks

Packages that implement dynalinks