BaxLinks/Flutter SDK

Flutter SDK

Create and manage deep links in your Flutter iOS & Android apps

Installation

Add to your pubspec.yaml:

dependencies:
  baxcloud_links_sdk: ^1.0.0+1

Then run:

flutter pub get

Device info (device model, OS version) is collected automatically using device_info_plus. No extra setup needed.

Quick Start

1. Initialize the SDK

1import 'package:baxcloud_links_sdk/baxcloud_links_sdk.dart';
2
3final links = BaxCloudLinksClient(
4  config: BaxCloudLinksConfig(
5    projectId: 'your-project-id',
6    apiKey: 'your-api-key',
7    debug: true, // Enable SDK debug logs (disable in production)
8  ),
9);

2. Create a Deep Link

iOS/Android store URLs are configured once in the Dashboard → BaxLinks → Setup. You only pass link-specific data:

1final link = await links.createLink(
2  CreateLinkOptions(
3    fallbackUrl: 'https://yourapp.com/invite?code=ABC123',
4    deepLinkPath: '/invite/ABC123',
5    deepLinkParams: {'inviteCode': 'ABC123', 'referrer': 'john'},
6    title: 'Join us on MyApp!',
7  ),
8);
9
10// Short URL from project subdomain (e.g. https://xxxxx.baxcloud.link/abc123)
11print(link.shortUrl);

3. Track Install (device info auto-collected)

1// Device ID, model, OS, OS version collected automatically
2await links.trackInstall(appVersion: '1.0.0');

4. Get Deferred Deep Link

1// On first launch — device fingerprint matched automatically
2final result = await links.getDeferredLink();
3
4if (result.found && result.deepLinkPath != null) {
5  Navigator.of(context).pushNamed(
6    result.deepLinkPath!,
7    arguments: result.deepLinkParams,
8  );
9}

Debug Logging

Trace SDK activity during development

Set debug: true in the config to see detailed SDK activity in the console. All messages are prefixed with [BaxCloudLinks]. Disable in production.

[BaxCloudLinks] BaxCloudLinks SDK initialized (project: abc123)
[BaxCloudLinks] handleDeepLink: https://xxx.baxcloud.link/mTX4Jw (scheme=https)
[BaxCloudLinks] resolveLink: mTX4Jw
[BaxCloudLinks] GET .../v1/sdk/links/resolve/mTX4Jw → 200
[BaxCloudLinks] handleDeepLink: resolved params={type: post, objectId: abc}

Handling Incoming Deep Links

Resolve short links and URI scheme links automatically

The SDK provides handleDeepLink(Uri) which automatically detects HTTPS short links (App Links / Universal Links) and resolves them via the API, or extracts query parameters directly from URI scheme links. Returns a Map<String, dynamic>? or null.

Recommended Setup

1late BaxCloudLinksClient _linksClient;
2StreamSubscription? _linkSubscription;
3
4void initLinks() {
5  _linksClient = BaxCloudLinksClient(
6    config: BaxCloudLinksConfig(
7      projectId: 'your-project-id',
8      apiKey: 'your-api-key',
9      debug: true,
10    ),
11  );
12
13  _handleInitialLink();
14
15  _linkSubscription = _linksClient.listenForLinks(
16    (uri) => _handleDeepLink(uri),
17  );
18}
19
20Future<void> _handleInitialLink() async {
21  final uri = await _linksClient.getInitialLink();
22  if (uri == null) return;
23
24  // IMPORTANT: On cold start the Navigator may not be mounted yet.
25  await Future.delayed(const Duration(milliseconds: 500));
26
27  await _handleDeepLink(uri);
28}
29
30Future<void> _handleDeepLink(Uri uri) async {
31  final params = await _linksClient.handleDeepLink(uri);
32  if (params == null) return;
33
34  final type = params['type']?.toString();
35  final objectId = params['objectId']?.toString();
36
37  if (type == 'post' && objectId != null) {
38    // Navigate to post screen
39  } else if (type == 'profile' && objectId != null) {
40    // Navigate to profile screen
41  }
42}

Cold-Start Deep Links

When the app is opened from a fully closed state via a link, getInitialLink() returns the URI before the widget tree (Navigator, Overlay) is mounted. Navigating immediately causes: '_elements.contains(element)': is not true. Always add a short delay or use WidgetsBinding.instance.addPostFrameCallback before navigating.

Recommended: Initialize the deep link listener after runApp() — e.g. inside your root widget's initState() — rather than in main().

Resolve a Short Link Manually

1final result = await links.resolveLink('mTX4Jw');
2// { found: true, deepLinkPath: '/post/abc',
3//   deepLinkParams: { type: 'post', objectId: 'abc' } }

Extract Slug from URI

1final slug = links.extractSlugFromUri(
2  Uri.parse('https://xxx.baxcloud.link/mTX4Jw'),
3);
4// 'mTX4Jw'

Event Tracking

Device info (ID, model, OS, version) is collected automatically — no manual deviceId needed

Convenience Methods

1// First launch — attributes install to a link click
2await links.trackInstall(appVersion: '1.0.0');
3
4// Every app open
5await links.trackOpen(appVersion: '1.0.0');
6
7// After signup
8await links.trackSignup();
9
10// After a purchase
11await links.trackPurchase(29.99, eventData: {'planId': 'pro'});
12
13// Custom event
14await links.trackCustomEvent('level_complete', eventValue: 5);

Low-level trackEvent

1// Use trackEvent() for full control
2await links.trackEvent(
3  TrackEventOptions(
4    eventType: BaxLinkEventType.custom,
5    eventName: 'level_complete',
6    eventValue: 5,
7    linkSlug: 'invite-john', // attribute to a specific link
8    platform: 'flutter',
9  ),
10);

Event Types: installopenreinstallsignuppurchasecustom

Deferred Deep Linking

Navigate users to the right screen after install

Call getDeferredLink() on first launch. BaxCloud fingerprints the device IP and matches it to a recent link click (48-hour window).

Full Implementation

1import 'package:shared_preferences/shared_preferences.dart';
2
3Future<void> checkDeferredLink(BuildContext context) async {
4  final prefs = await SharedPreferences.getInstance();
5  if (prefs.getBool('bax_deferred_checked') ?? false) return;
6  await prefs.setBool('bax_deferred_checked', true);
7
8  // Track install (device info auto-collected)
9  await links.trackInstall(appVersion: '1.0.0');
10
11  // Check for deferred deep link
12  final result = await links.getDeferredLink();
13  if (result.found && result.deepLinkPath != null) {
14    Navigator.of(context).pushNamed(
15      result.deepLinkPath!,
16      arguments: result.deepLinkParams,
17    );
18  }
19}

How it works: Device IP is matched to a recent click within 48 hours. Returns deepLinkPath + deepLinkParams.

Attribution & Install Tracking

Query attributed installs for referral rewards

When an install event is reported, BaxCloud matches it to a link click via IP fingerprinting (7-day window).

Query Attributed Installs

1final result = await links.getAttributedInstalls();
2print('Total: ${result.total}');
3
4for (final install in result.data) {
5  print('${install.linkSlug} · ${install.os} · ${install.country}');
6}
7
8// Filter by link
9final linkInstalls = await links.getAttributedInstalls(
10  linkId: 'your-link-id',
11  page: 1,
12  limit: 20,
13);

Attribution Webhook

A link.install.attributed webhook fires on your server when attribution succeeds:

1{
2  "event": "link.install.attributed",
3  "linkSlug": "invite-john",
4  "deepLinkPath": "/invite/REF456",
5  "deepLinkParams": { "referralCode": "REF456" },
6  "attributedAt": "2025-01-15T10:30:00.000Z",
7  "install": { "os": "iOS", "appVersion": "1.0.0", "country": "US" }
8}

API Reference

Link Management

createLink(options)Create a new deep link
getLink(linkId)Get a link by ID
listLinks(params?)List project links
updateLink(id, options)Update a link
deleteLink(id)Delete a link

Event Tracking

trackInstall({appVersion?})Track first install — auto device info
trackOpen({appVersion?})Track app open — auto device info
trackSignup({eventData?})Track signup — auto device info
trackPurchase(value, {eventData?})Track purchase — auto device info
trackCustomEvent(name, {value?, eventData?})Track custom event
trackEvent(TrackEventOptions)Low-level event tracking

Deep Linking & Attribution

handleDeepLink(uri)Resolve any incoming URI — HTTPS short links via API, URI scheme directly
resolveLink(slug)Resolve a short link slug to deep link data via the API
extractSlugFromUri(uri)Extract the slug from a short link URL
getInitialLink()Get the initial deep link URI that opened the app (cold start)
listenForLinks(onLink)Listen for incoming deep links while app is running
getDeferredLink()Retrieve deferred deep link (auto device ID)
getAttributedInstalls(opts?)Query attributed installs
getAppConfig()Get project config + shortLinkBase
getShortUrl(link)Returns link.shortUrl (e.g. https://xxxxx.baxcloud.link/slug)

Platform Setup

Required native configuration for deep link handling. Get your subdomain from Dashboard → BaxLinks → Setup.

Android — Disable Flutter deep linking

In AndroidManifest.xml inside your <activity>:

<meta-data
    android:name="flutter_deeplinking_enabled"
    android:value="false" />

<!-- URI Scheme (e.g. myapp://invite/ABC123) -->
<intent-filter>
    <data android:scheme="@string/baxcloud_links_scheme" android:host="*" />
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
</intent-filter>

<!-- App Links (HTTPS) -->
<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="@string/baxcloud_links_default_url" />
</intent-filter>

Android — res/values/strings.xml

<resources>
    <string name="app_name">YourApp</string>
    <!-- Your URI scheme, e.g. "myapp" -->
    <string name="baxcloud_links_scheme">myapp</string>
    <!-- Replace xxxxx with YOUR project subdomain from Dashboard → BaxLinks → Setup -->
    <string name="baxcloud_links_default_url">xxxxx.baxcloud.link</string>
</resources>

iOS — Runner.entitlements

<key>com.apple.developer.associated-domains</key>
<array>
    <!-- Wildcard covers all xxxxx.baxcloud.link subdomains (iOS 14+) -->
    <string>applinks:*.baxcloud.link</string>
</array>

iOS — URI Scheme in Info.plist

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>