LinkId Mobile Marketing Platform for Flutter

Getting started

Run this command:

$ flutter pub add flutter_linkid_mmp

This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):

dependencies:
    flutter_linkid_mmp: ^1.6.1

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Usage

Retail Media

1. Widget AdRetailMediaWidget

  • A built-in widget that can get ad from id and type, display ad to user and handle tracking ad click, tracking ad impression.
  • Parameters:
Parameters Description
adId Advertisement's id, get from Ad Inventory on Airflex portal
adType Type of ad. Currently there are 2 types: BANNER (only image) and PRODUCT (information about product including image, name, description and price).
placeholder Placeholder when getting ad
padding Padding around ad, default is EdgeInsets.all(16)
borderRadius Border radius around ad, default is BorderRadius.zero
adCarouselConfig Configuration for ad carousel
timeoutGetAd Timeout when getting ad, default is Duration(seconds: 30)
keepAlive Whether to keep this widget alive in lazy list, default is true
adListenerCallback Listener callbacks for ad widget such as onAdError, onAdClick, onAdImpression,...
AdRetailMediaWidget(
    adId: 'ad inventory id',
    adType: AdType.banner,
    placeholder: Center(child: CircularProgressIndicator()),
    padding: EdgeInsets.all(16),
    borderRadius: BorderRadius.all(Radius.circular(8)),
    adCarouselConfig: AdCarouselConfig(
        autoPlay: true,
        enableInfiniteScroll: true,
        autoPlayInterval:const Duration(seconds: 5),
        showIndicator: true,
        indicatorHeight: 20,
        indicatorBuilder: (isSelected) {
            // Your custom indicator builder
        }
    ),
    keepAlive: true,
    timeoutGetAd: Duration(seconds: 30),
    adListenerCallback: AdListenerCallback(
        onAdError: (error, adId, adType) {
            debugPrint('onAdError: $error, $adId, $adType');
        },
        onAdClick: (adId, adType, productId, actionType, actionData) {
            debugPrint('onAdClick: $adId, $adType, $productId, $actionType, $actionData');
            // Should check actionType is inapp or outapp to handle actionData:
            // If actionType = inapp then should use product id or actionData to navigate to a specific screen in your app
            // If actionType = outapp then should open actionData as an url outside your app
        },
        onAdImpression: (adId, adType, productId) {
            debugPrint('onAdImpression: $adId, $adType, $productId');
        },
    ),
)

2. Customize your ad UI:

  • Use Airflex.shared.getAd() to get all information about your ad and the product it represent.
  • Then you can write your own widget to display your ad and handle action onAdClick, onAdImpression from scratch.
  • Remember when handling action onAdClick and onAdImpression, you must call Airflex.shared.trackAdClick() and Airflex.shared.trackAdImpression().

3. Ad functions:

Functions Description
Airflex.shared.getAd() Get ad by id (get from Ad Inventory on Airflex Portal) and type (banner, product,...). The response is in raw format (Map<String, dynamic>), you can use the pre-defined AdMediaData class to parse the response to a model or write your own model class.
Airflex.shared.trackAdClick() Tracking event on user click an ad.
Airflex.shared.trackAdImpression() Tracking event on user see your ad.
  • Product id can be retrieved from Airflex.shared.getAd() in 1 of 2 ways: (1) Get from attributes data-ad-element-id in html from field adData, (2) Get from field id in adDataJson.

Example

Additional information

Email: leonacky@gmail.com

Setup Notification

1. Requirements

Your application must be pre-configured with Firebase Cloud Messaging (FCM) before using the notification features of this SDK.

2. Android Setup

The SDK uses flutter_local_notifications for notification features, therefore you must enable Core library desugaring inside your Android gradle configuration (usually located at android/app/build.gradle or android/app/build.gradle.kts).

Configuration Example (with build.gradle.kts):

android {
    ...
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
        ...
    }
}

dependencies {
    // Add the desugar dependency required by flutter_local_notifications
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
}

Note on Icon (Android): When configuring a static icon for notifications, the image file (e.g., ic_launcher.png) must be copied and placed in the actual project's android/app/src/main/res/drawable/ directory (instead of just leaving it in the default mipmap directory). Do not pass the @mipmap/ prefix.

3. Usage

  • Register for Notifications: Use the registerAirflexNotification function to register. This function will acquire the FCM token from the device and send it to the server, while also tracking notification click events.
  • Unregister from Notifications: Use the unregisterAirflexNotification function to remove the user's token from the server.

Example:

// 1. Configure the display icon for notifications (optional)
Airflex.notifications.setForegroundNotification(
    androidSetting: const AndroidNotificationForegroundSetting(
        icon: "ic_launcher", // Icon name retrieved from the drawable directory
    ),
);

// 2. Register for notifications & track events (Note: must be called after the user has logged in)
await Airflex.notifications.registerAirflexNotification(userId: "YOUR_USER_ID");

// 3. Unregister from notifications (call when the user logs out)
await Airflex.notifications.unregisterAirflexNotification("YOUR_USER_ID");

// 4. Listen to notifications being opened
Airflex.notifications.addOpenMessageListener((data) {
    debugPrint("User tapped on notification: $data");
    // Handle navigation or logic based on the data payload
});

// 5. Listen to notifications received while the app is in the foreground
Airflex.notifications.onMessage().listen((data) {
    debugPrint("Received notification in foreground: $data");
});

Topic Management

Topics let users control which categories of notifications they want to receive. Topics are organized into groups, each represented by a GroupTopicData object:

Property Type Description
groupCode String? The unique code used to identify the group.
groupTitle String? The display title of the group.
groupDescription String? An optional description of the group.
topics List<TopicData> The topics belonging to this group.

Each topic is represented by a TopicData object:

Property Type Description
name String The display name of the topic.
code String The unique code used to identify the topic.
isSubscribed bool Whether the user is currently subscribed to this topic.
  • Get user topics: Use getUserTopics to fetch the topic groups available to a user along with the current subscription status of each topic. Returns List<GroupTopicData>?.
  • Toggle subscription: Use setTopicSubcribeStatus to subscribe or unsubscribe a user from a topic. Returns a bool indicating whether the update succeeded.

Example:

// 1. Fetch the topic groups for a user
final groups = await Airflex.notifications.getUserTopics("YOUR_USER_ID");
if (groups != null) {
    for (final group in groups) {
        debugPrint("Group: ${group.groupTitle ?? group.groupCode ?? '(no title)'}");
        for (final topic in group.topics) {
            debugPrint("  ${topic.name} (${topic.code}) - subscribed: ${topic.isSubscribed}");
        }
    }
}

// 2. Subscribe the user to a topic
final subscribed = await Airflex.notifications.setTopicSubcribeStatus(
    "YOUR_USER_ID",
    "TOPIC_CODE",
    true,
);

// 3. Unsubscribe the user from a topic
final unsubscribed = await Airflex.notifications.setTopicSubcribeStatus(
    "YOUR_USER_ID",
    "TOPIC_CODE",
    false,
);

if (subscribed) {
    debugPrint("Subscription updated successfully");
}

Notification Inbox

The inbox stores the notifications a user has received so you can show them in an in-app list. Each notification is represented by an InboxNotificationData object:

Property Type Description
id String The notification id, used to call markInboxNotificationsRead.
title String? The notification title.
body String? The notification body.
deeplink String? The deeplink to open when the user taps the notification.
topicCode String? The topic the notification belongs to.
isRead bool Whether the user has read the notification.
createdAt DateTime? The time the notification was created (UTC).

A page of notifications is represented by an InboxNotificationPageData object with items, nextCursor (pass it to the next call, null when there is no next page) and hasMore.

  • Get inbox notifications: Use getInboxNotifications to fetch one page of the user's inbox, newest first. limit is 1 to 50 (server default 20), topicCode filters by topic and unreadOnly returns only unread notifications. Returns InboxNotificationPageData? (null on error).
  • Get unread count: Use getInboxUnreadCount to get the badge count. The server caps it at 100, so show 99+ for large values. Returns int?.
  • Mark as read: Use markInboxNotificationsRead to mark 1 to 100 notifications as read. Ids already read or not owned by the user are ignored. Returns the number of notifications that changed to read, or null on error.
  • Mark all as read: Use markAllInboxNotificationsRead to mark all notifications, or only those of topicCode, as read. Returns a bool indicating whether the update succeeded.

Example:

import 'package:flutter_linkid_mmp/notifications/inbox_notification_data.dart';

// 1. Fetch the first page
final page = await Airflex.notifications.getInboxNotifications("YOUR_USER_ID", limit: 20);

// 2. Fetch the next page
if (page != null && page.hasMore) {
    final nextPage = await Airflex.notifications.getInboxNotifications(
        "YOUR_USER_ID",
        cursor: page.nextCursor,
    );
}

// 3. Get the unread count for the badge
final unread = await Airflex.notifications.getInboxUnreadCount("YOUR_USER_ID");

// 4. Mark notifications as read
await Airflex.notifications.markInboxNotificationsRead("YOUR_USER_ID", ["NOTIFICATION_ID"]);

// 5. Mark all notifications as read
await Airflex.notifications.markAllInboxNotificationsRead("YOUR_USER_ID");