traput_dynamic_linking 0.0.1
traput_dynamic_linking: ^0.0.1 copied to clipboard
Traput Dynamic Linking SDK for Flutter. Handles Universal Links (iOS), App Links (Android), and deferred deep links via a single API (Traput.onLink()). Logic-only, no UI.
traput_dynamic_linking #
Official Flutter SDK for Traput (Traput - ترابط) — dynamic links for mobile apps.
Links #
- Traput — Product, documentation, pricing (Firebase Dynamic Links replacement).
- Dashboard — Create projects, get API key, manage links.
Get your API key and subdomain from the Dashboard.
What it does #
- Initializes the SDK with an API key, base URL, and subdomain (one Traput project per app).
- Handles Universal Links (iOS) and App Links (Android) - opens app directly without redirects
- Captures deferred deep links via
traput_tokenquery parameter - Provides unified API:
Traput.onLink()- handles all link types (Universal, App, deferred) - Captures links from:
- Android: App Links (intent data), deferred tokens (intent extras/query params)
- iOS: Universal Links (
NSUserActivity), deferred tokens (launch URL/query params)
- Resolves deferred tokens via backend endpoint:
POST {baseUrl}/deferred/resolve
Primary API: Traput.onLink() - recommended for all new projects
Legacy API: resolveDeferredLink() - deprecated, use onLink() instead
This package contains no UI.
Install #
Add to your app (from pub.dev):
dependencies:
traput_dynamic_linking: ^0.0.1
``
Quick start (Dart) #
Recommended: Using Traput.onLink() #
Initialize and register link handler:
import 'package:traput_dynamic_linking/traput_dynamic_linking.dart';
void main() {
Traput.initialize(
apiKey: '<YOUR_API_KEY>',
baseUrl: 'https://api.traput.com',
subdomain: 'your-project', // From Dashboard; must match your Traput project
);
// Register callback to handle all link types (Universal Links, App Links, deferred)
Traput.onLink((link) {
// Handle Universal Link, App Link, or deferred link
print('Received link: ${link.uri}');
if (link.uri.pathSegments.contains('product')) {
// Navigate to product page
}
// For deferred links, access resolved data
if (link.isDeferred) {
print('Deferred token: ${link.deferredToken}');
print('Resolved data: ${link.data}');
}
});
runApp(MyApp());
}
The callback is automatically invoked:
- Cold start: When app is launched from a Universal/App Link
- Warm start: When app is already running and a link is opened
- Deferred: When a deferred token is resolved
Legacy: Using resolveDeferredLink() (Deprecated) #
// Initialize once on app startup
Traput.initialize(
apiKey: '<YOUR_API_KEY>',
baseUrl: 'https://api.traput.com',
subdomain: 'your-project',
);
// Then resolve (usually on first screen / first app session)
final resolved = await Traput.instance.resolveDeferredLink();
if (resolved != null) {
// Navigate using resolved.deepLink
// Optional metadata from backend: resolved.data
}
Note: resolveDeferredLink() only handles deferred links. Use Traput.onLink() for Universal Links, App Links, and deferred links.
API reference #
Traput.initialize({ apiKey, baseUrl, subdomain, httpClient, timeout, enableDebugLogging }) #
- apiKey (required): The raw Traput API key to send in the
X-API-Keyheader. - baseUrl (required): Traput backend API root, including scheme + host (and optional port), e.g.:
https://api.traput.comhttp://localhost:3000
- subdomain (required): Your Traput project's subdomain (must match the project that owns the API key). The SDK sends this on every API request so the backend can enforce one project per app.
- httpClient (optional): Custom HTTP client for testing or custom network configuration.
- timeout (optional): Timeout for network requests. Defaults to 10 seconds.
- enableDebugLogging (optional): Whether to enable debug logging. Defaults to
false. When enabled, logs are emitted (but never secrets like API keys or tokens).
The SDK calls POST {baseUrl}/deferred/resolve and GET {baseUrl}/links/data with the subdomain on every request.
Throws:
TraputInvalidApiKeyExceptionif API key is emptyTraputInvalidBaseUrlExceptionif base URL is invalidTraputMissingConfigurationExceptionif subdomain is emptyTraputInitializationExceptionif timeout is invalid
Traput.onLink(void Function(TraputLink) callback) #
Primary API - Handles Universal Links, App Links, and deferred deep links.
Registers a callback that is invoked when the app is opened via a link.
Parameters:
callback: Function that receives aTraputLinkobject
When callback is invoked:
- Cold start: App is launched from a Universal/App Link
- Warm start: App is already running and a link is opened
- Deferred: A deferred token is resolved (if present in link)
TraputLink properties:
uri:Uri- The URI that opened the app (Universal Link or App Link URL)deferredToken:String?- Optional deferred token if this link was resolved from a deferred sessiondata:Map<String, Object?>- Additional data extracted from the link (for deferred links, contains resolved data)isDeferred:bool- Whether this link contains a deferred token
Example:
Traput.onLink((link) {
// Extract path segments
final segments = link.uri.pathSegments;
if (segments.length >= 2 && segments[0] == 'r') {
final slug = segments[1]; // e.g., 'abc' from /r/abc
// Navigate based on slug
}
// Extract query parameters
final productId = link.uri.queryParameters['productId'];
// Handle deferred links
if (link.isDeferred) {
final resolvedData = link.data;
// Use resolved data for navigation
}
});
Note: Only one callback can be registered at a time. Registering a new callback replaces the previous one.
Future<ResolvedDeferredLink?> resolveDeferredLink() (Deprecated) #
Deprecated: Use Traput.onLink() instead for handling all link types.
- Returns
nullwhen there is no captured token. - Uses consume-once semantics: the captured token is cleared on the native side when consumed.
- Returns
ResolvedDeferredLinkon success:deepLink:Uridata:Map<String, Object?>
- Throws
TraputApiExceptionon failures, including:- non-200 responses from the backend
- network/client failures (e.g. timeouts, connectivity)
- invalid/unsupported response bodies (missing/invalid
deepLink)
Migration: See Universal Links Documentation for migration guide.
Debug logging
When enableDebugLogging: true is set in Traput.initialize(), the SDK emits debug logs (prefixed with [Traput]) for:
- Link processing
- Deferred link resolution
- Network requests (sanitized)
- Errors (sanitized, no secrets)
Important: Logs never include secrets (API keys, tokens, or other sensitive data).
Breaking change: required subdomain #
As of this version, subdomain is required in Traput.initialize(). One Flutter app corresponds to one Traput project, identified by its subdomain.
- If you are upgrading: Add
subdomain: 'your-project'to everyTraput.initialize()call. The value must match your Traput project’s subdomain (the one that owns your API key). The backend rejects requests when the API key’s project does not match the subdomain (403 Forbidden). - If you are new: Always pass
subdomainwhen initializing; see Quick start above.
Backend contract (what the SDK sends) #
Endpoint: POST /deferred/resolve
- Headers:
X-API-Key: <apiKey>Content-Type: application/json
- Body:
deferredSessionId: the capturedtraput_tokenvalueplatform:"ios"or"android"bundleId: your app bundle id/package name
Platform integration notes #
Android #
The Android plugin captures links via getInitialLink() method:
- App Links: Captures full URL from
intent.data(whenandroid:autoVerify="true"is set) - Deferred tokens: Captures
traput_tokenfrom:intent.extras["traput_token"]- fallback:
intent.data?.getQueryParameter("traput_token")
It also listens to onNewIntent for warm start scenarios (consume-once semantics).
App Links Setup:
- Add intent filter in
AndroidManifest.xmlwithandroid:autoVerify="true" - Configure
dataelements with schemehttps, host<subdomain>.traput.link, pathPrefix/r - See Universal Links Documentation for complete setup
Manual test (App Link):
adb shell am start -a android.intent.action.VIEW -d "https://myapp.traput.link/r/test"
Manual test (deferred link with query param):
adb shell am start -a android.intent.action.VIEW -d "myapp://open?traput_token=abc123"
Important:
- For App Links, ensure
assetlinks.jsonis accessible and verified - Play Store install itself does not automatically deliver arbitrary query params to your app
- To use deferred linking, your app must receive/produce the token on first launch and pass it via the launch
Intentextras (e.g. via your attribution flow)
iOS #
The iOS plugin captures links via getInitialLink() method:
- Universal Links: Captures full URL from
NSUserActivity(including cold start) - Deferred tokens: Captures
traput_tokenfrom:- launch URL (
didFinishLaunchingWithOptions) - open URL (
application(_:open:options:)) - universal links query parameters
- launch URL (
Universal Links Setup:
- Enable Associated Domains capability in Xcode
- Add domain:
applinks:<subdomain>.traput.link - See Universal Links Documentation for complete setup
Important:
- For Universal Links, your app must be configured with Associated Domains and receive the URL
- Ensure AASA file is accessible at
https://<subdomain>.traput.link/.well-known/apple-app-site-association - All links must use subdomain format:
https://<subdomain>.traput.link/r/*
Development #
Run SDK unit tests:
cd sdk/flutter
flutter test
Run analyzer:
cd sdk/flutter
flutter analyze
How to test against the local backend #
If you run the backend at http://localhost:3000, initialize with:
baseUrl: "http://localhost:3000"
Then use Traput.onLink() to handle links, or ensure your app receives a traput_token on first launch and call resolveDeferredLink() (deprecated).
Troubleshooting #
Common Issues #
Links not opening the app:
- iOS: Verify Associated Domains are configured in Xcode
- Android: Verify
android:autoVerify="true"is set in AndroidManifest.xml - Check that AASA/assetlinks.json files are accessible
Deferred links not resolving:
- Check API key is correct
- Verify backend is accessible
- Enable debug logging to see what's happening
Initialization errors:
- Ensure API key is not empty
- Ensure base URL includes scheme and host
- Ensure subdomain is not empty and matches your Traput project’s subdomain
- Check error messages for specific issues
Error Handling #
The SDK throws typed exceptions that you can catch:
try {
Traput.initialize(
apiKey: 'your-api-key',
baseUrl: 'https://api.traput.com',
subdomain: 'your-project', // From Dashboard
);
} on TraputInvalidApiKeyException {
// Handle invalid API key
} on TraputInvalidBaseUrlException {
// Handle invalid base URL
} on TraputMissingConfigurationException {
// Handle missing/empty subdomain
} on TraputException catch (e) {
// Handle other Traput errors
print('Error: ${e.message}');
}
Common Mistakes #
-
Forgetting to call
initialize(): Always callTraput.initialize()before using the SDK. -
Omitting
subdomain:subdomainis required and must match your Traput project’s subdomain; the backend returns 403 if the API key’s project does not match. -
Registering callback too late: Register
Traput.onLink()as early as possible, ideally right after initialization. -
Not handling errors: Wrap SDK calls in try-catch to handle errors gracefully.
-
Incorrect AndroidManifest.xml: Ensure
android:autoVerify="true"is set for App Links. -
Missing Associated Domains: iOS requires Associated Domains capability to be enabled.
-
Testing in simulator: Universal Links don't work in iOS Simulator - test on real devices.
For more detailed troubleshooting, see the Troubleshooting Guide.
Additional Documentation #
For setup when using the published package from pub.dev, see Traput documentation.
- Universal Links & App Links Setup Guide - Complete setup instructions for iOS and Android
- Troubleshooting Guide - Common issues and solutions
- Integration Checklist - Step-by-step integration guide
- Migration Guide - Migrating from
resolveDeferredLink()toTraput.onLink()