journiq_flutter_sdk 0.6.3 copy "journiq_flutter_sdk: ^0.6.3" to clipboard
journiq_flutter_sdk: ^0.6.3 copied to clipboard

Journiq deep linking, attribution, and analytics SDK for Flutter. Wraps native Android and iOS SDKs via platform channels.

[Journiq]

Journiq Flutter SDK

Official Flutter plugin for Journiq — deep linking, deferred deep links, event tracking, attribution analytics, in-app notifications, and push.

Wraps the native Android SDK and iOS SDK via platform channels.

Features #

  • Deferred Deep Links — Attribute installs to the link that drove them, even across the app store
  • Link Management — Create, list, and retrieve short links programmatically
  • Event Tracking — Track conversions and custom events with offline queue and automatic batching
  • Analytics — Retrieve link click stats and app configuration
  • In-App Notifications — Receive and display real-time in-app notifications with custom UI
  • Push Registration — Register FCM device tokens for push notifications
  • Cross-platform — Single Dart API backed by native SDKs on both Android and iOS

Requirements #

Platform Minimum Version
Android API 24 (Android 7.0)
iOS 13.0
Flutter 3.3.0+
Dart 3.11.0+

Installation #

Add to your pubspec.yaml:

dependencies:
  journiq_flutter_sdk: ^0.3.0

Then run:

flutter pub get

Quick Start #

1. Initialize the SDK #

import 'package:journiq_flutter_sdk/journiq_flutter_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Journiq.configure(apiKey: 'jq_pub_your_key_here');
  runApp(MyApp());
}
final match = await Journiq.deepLinks.checkDeferredDeepLink();
if (match.matched) {
  // Attribution is stored automatically — events will be linked to this deep link
  Navigator.pushNamed(context, match.deepLinkPath ?? '/');
}
import 'package:journiq_flutter_sdk/journiq_flutter_sdk.dart';

// When the app opens via a Universal Link or Android App Link
void handleUniversalLink(Uri uri) async {
  try {
    final resolved = await Journiq.deepLinks.resolveUniversalLink(uri);
    // resolved.deepLinkPath — e.g. "product/123"
    // resolved.parameters   — custom key-value pairs
    // resolved.utmSource, .utmMedium, .utmCampaign...
    navigateTo(resolved.deepLinkPath, params: resolved.parameters);
  } catch (e) {
    // Fallback: parse locally (e.g. URL scheme links)
    final cleanUri = Journiq.deepLinks.handleIncomingLink(uri);
    navigateTo(cleanUri.path);
  }
}

Note: resolveUniversalLink calls the server to resolve the short URL, tracks the open, and stores attribution automatically. Use handleIncomingLink as a local-only fallback for URL scheme links.

4. Track Events #

// deepLinkId is automatically attached from the last attribution source
await Journiq.events.track(
  'PURCHASE',
  metadata: {'amount': '29.99', 'currency': 'USD'},
);

// Or pass explicitly if needed
await Journiq.events.track(
  'PURCHASE',
  deepLinkId: 'specific_link_id',
  metadata: {'amount': '29.99', 'currency': 'USD'},
);
final link = await Journiq.links.create(LinkCreateRequest(
  webUrl: 'https://example.com/product/123',
  title: 'Cool Product',
  deepLinkPath: '/product/123',
  utmSource: 'app',
  utmMedium: 'share',
));
// Share link.shortUrl

5. User Identity #

// After login
await Journiq.setIdentity('user_12345');

// On logout
await Journiq.logout();

6. Push Notifications #

Register the device's FCM token so the backend can send push and in-app notifications:

import 'package:firebase_messaging/firebase_messaging.dart';

final token = await FirebaseMessaging.instance.getToken();
if (token != null) {
  await Journiq.push.registerToken(token);
}

// Re-register on token refresh
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
  Journiq.push.registerToken(newToken);
});

Handle incoming FCM data messages:

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  // Let the SDK handle Journiq messages (returns true if handled)
  final handled = Journiq.push.handleDataMessage(message.data);
  if (!handled) {
    // Handle your own messages
  }
});

7. In-App Notifications #

Listen for new notifications (stream-based)

Journiq.notifications.onNewNotification.listen((notification) {
  print('New: ${notification.title}');
});

Register a handler to control exactly how notifications appear in your app:

Journiq.notifications.setDisplayHandler(context, (ctx, notification) {
  // Return a widget to display, or null to suppress
  return MaterialBanner(
    content: Text(notification.title),
    actions: [
      TextButton(
        onPressed: () {
          Journiq.notifications.markAsRead(notification.id);
          JourniqNotifications.reportAction(notification, NotificationAction.tapped);
        },
        child: Text('VIEW'),
      ),
      TextButton(
        onPressed: () {
          JourniqNotifications.reportAction(notification, NotificationAction.dismissed);
        },
        child: Text('DISMISS'),
      ),
    ],
  );
});

Fetch notifications

final result = await Journiq.notifications.getAll(page: 1, limit: 20);
for (final n in result.notifications) {
  print('${n.title} - read: ${n.read}');
}

Mark as read & unread count

await Journiq.notifications.markAsRead(notificationId);
final count = await Journiq.notifications.getUnreadCount();

// Or listen to count changes
Journiq.notifications.onUnreadCountChanged.listen((count) {
  updateBadge(count);
});

API Reference #

Journiq #

Method Description
configure({apiKey, baseUrl?}) Initialize the SDK. Call once at app startup.
isInitialized Whether the SDK has been configured.
setIdentity(userId) Set user identity for cross-device attribution.
logout() Remove user identity and clear attribution.
onAppForegrounded() Trigger event queue flush (call from lifecycle handler).
attributedDeepLinkId The currently stored deep link ID (read-only).
attributedClickId The currently stored click ID (read-only).
Method Description
checkDeferredDeepLink() Check for a deferred deep link match on first open. Auto-stores attribution. Returns MatchResult.
resolveUniversalLink(Uri, {source?}) Resolve a universal/app link URL via API (tracks open + stores attribution). Returns ResolvedLink.
handleIncomingLink(Uri) Extract attribution from an incoming URL scheme link locally (no network call). Returns clean Uri for routing.
Method Description
create(LinkCreateRequest) Create a new deep link. Returns DeepLink.
list({page, limit}) List deep links with pagination.
get(linkId) Get a single deep link by ID.

Journiq.events #

Method Description
track(eventName, {deepLinkId?, metadata?}) Track a conversion or custom event. Auto-attaches stored deepLinkId if not provided.
flush() Force flush queued events immediately.

Journiq.analytics #

Method Description
getLinkStats(linkId) Get click statistics for a link. Returns LinkStats.
getAppConfig() Get app configuration. Returns AppConfig.

Journiq.notifications #

Method Description
onNewNotification Stream of new notifications as they arrive.
onUnreadCountChanged Stream of unread count changes.
getAll({page, limit, unreadOnly}) Fetch paginated notifications. Returns NotificationsResult.
markAsRead(notificationId) Mark a notification as read.
getUnreadCount() Get current unread count.
setDisplayHandler(context, handler) Register a custom widget builder for incoming notifications.
setActionHandler(handler) Register a callback for notification tap/dismiss actions.
removeDisplayHandler() Remove the display handler.

Journiq.push #

Method Description
registerToken(fcmToken) Register the device FCM token with Journiq.
handleDataMessage(data) Process an FCM data message. Returns true if handled by Journiq.

Platform Setup #

1. Configure URL Schemes in Journiq Dashboard #

In the Journiq dashboard, navigate to Apps → your app → Edit and configure:

Field Example Description
iOS URL Scheme myapp://{{path}} Template used to open your iOS app. {{path}} is replaced with the deep link path at redirect time.
Android URL Scheme myapp://{{path}} Template used to construct the Android intent URL.
Bundle ID com.example.myapp Your iOS app bundle identifier
Package Name com.example.myapp Your Android application ID
App Store URL https://apps.apple.com/app/id123456 Fallback if app not installed (iOS)
Play Store URL https://play.google.com/store/apps/details?id=com.example.myapp Fallback if app not installed (Android)

Important: The {{path}} placeholder is required. When a user clicks a link with deepLinkPath: "product/123", the redirect service generates myapp://product/123.

2. iOS Setup #

Install native SDK

cd ios && pod install

Register your URL scheme

Add your custom scheme to ios/Runner/Info.plist:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
    <key>CFBundleURLName</key>
    <string>com.example.myapp</string>
  </dict>
</array>

Replace myapp with the scheme portion of your iOS URL Scheme (everything before ://).

3. Android Setup #

The SDK dependency is pulled from Maven Central automatically.

Register your URL scheme

Add an intent filter to your main activity in android/app/src/main/AndroidManifest.xml:

<activity
    android:name=".MainActivity"
    android:launchMode="singleTop"
    ...>

    <!-- Existing intent filters ... -->

    <!-- Journiq deep link URL scheme -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="myapp" />
    </intent-filter>
</activity>

Replace myapp with the scheme portion of your Android URL Scheme.

Use the app_links package to listen for incoming URLs, then parse them with the Journiq SDK:

import 'package:app_links/app_links.dart';
import 'package:journiq_flutter_sdk/journiq_flutter_sdk.dart';

class DeepLinkHandler {
  final _appLinks = AppLinks();

  void init() {
    // Handle link when app is already running
    _appLinks.uriLinkStream.listen((Uri uri) {
      _handleDeepLink(uri);
    });

    // Handle link that launched the app
    _appLinks.getInitialLink().then((uri) {
      if (uri != null) _handleDeepLink(uri);
    });
  }

  void _handleDeepLink(Uri uri) {
    final path = uri.path.replaceFirst('/', ''); // e.g. "product/123"
    final params = uri.queryParameters;           // e.g. {"ref": "campaign1"}

    // Navigate to the appropriate screen
    navigateTo(path, params);
  }
}

Alternatively, if you use go_router, deep links are handled automatically via its route definitions.

How It Works #

User clicks Journiq link (e.g. https://links.example.com/abc123)
    │
    ▼
Journiq redirect service detects platform
    │
    ├── iOS mobile ──► Redirects to myapp://product/123
    │                   (falls back to App Store if not installed)
    │
    ├── Android ────► Redirects via intent URL
    │                   intent:#Intent;scheme=myapp;package=com.example.myapp;
    │                   S.browser_fallback_url=<Play Store URL>;end
    │                   (falls back to Play Store if not installed)
    │
    └── Web/Desktop ► Redirects to webUrl

Every click goes through the redirect service, ensuring analytics are always recorded before the app opens.

License #

MIT

0
likes
150
points
550
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Journiq deep linking, attribution, and analytics SDK for Flutter. Wraps native Android and iOS SDKs via platform channels.

Homepage

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on journiq_flutter_sdk

Packages that implement journiq_flutter_sdk