Configure app cards

Prev Next

The App Cards module provides a complete card-based messaging solution for the Insider Flutter Plugin. It enables developers to retrieve card campaigns, manage read/unread/delete status, handle deeplink actions, and log analytics events (clicks and views).

This documentation covers the App Cards API (FlutterInsiderAppCards), the current architecture. The legacy v1 API (getMessageCenterDataWithIdentifiers) is a separate, older interface for retrieving push notification payloads as a notification center. If you are building a new integration, use the App Cards API.

Prerequisites

  • Insider Flutter Plugin flutter_insider: 5.0.0 or later

  • SDK must be initialized via FlutterInsider.Instance.init(...) before accessing App Cards.

Access

The FlutterInsiderAppCards client is obtained through the main SDK singleton.

final appCards = FlutterInsider.Instance.appCards;

The appCards getter is lazily initialized and always returns a valid instance. If the SDK is not initialized, operations will report an InsiderAppCardsException with code sdkNotInitialized, and void methods (view, click, clickButton) will silently forward the failure to the native SDK.

Async Model

Flutter App Cards APIs use Future-based async methods instead of callback interfaces. Methods that can fail throw InsiderAppCardsException; void methods (view, click, clickButton) are fire-and-forget and never throw.

// Async method — await or use .then(...)
final response = await FlutterInsider.Instance.appCards.getCampaigns();

// Void method — fire-and-forget
FlutterInsider.Instance.appCards.view(card);

Error Handling

InsiderAppCardsException

All App Cards operations report errors via InsiderAppCardsException, which implements Exception and exposes a typed error code via error.code.

Code

Description

sdkNotInitialized

SDK is not initialized, frozen, or not GDPR-compliant.

invalidParameter

Invalid parameter provided (e.g., null or empty card IDs).

networkError

A network error occurred during the request.

serverError

Server returned an error response (HTTP 4xx/5xx).

parseError

Failed to parse the server response.

unknown

An unexpected error occurred.

Example

try {
  final response = await FlutterInsider.Instance.appCards.getCampaigns();
  // ...
} on InsiderAppCardsException catch (e) {
  switch (e.error.code) {
    case InsiderAppCardsErrorCode.sdkNotInitialized:
      // SDK not initialized
      break;
    case InsiderAppCardsErrorCode.networkError:
      // Retry later
      break;
    case InsiderAppCardsErrorCode.serverError:
      // Server issue
      break;
    default:
      // Handle other errors
      break;
  }
}

Core Types

InsiderAppCardCampaignsResponse

Model class representing an app cards campaign response. Contains a list of card items.

Name

Type

Nullable

Description

appCards

List<InsiderAppCard>

No

The list of cards in the campaign response

InsiderAppCard

Model class representing a single card in the campaign response.

Name

Type

Nullable

Description

id

String

No

The card identifier.

type

String

No

The card type (e.g.,"image","message").

isRead

bool

No

Whether the card has been read.

images

List<InsiderAppCardImage>?

Yes

List of images, or null.

content

InsiderAppCardContent?

Yes

Content (title/description), or null.

buttons

List<InsiderAppCardButton>?

Yes

List of buttons, or null.

action

InsiderAppCardAction?

Yes

Card-level action, or null.

Convenience Methods

Method

Description

markAsRead()

Marks this single card as read. Returns Future<void>.

markAsUnread()

Marks this single card as unread. Returns Future<void>.

delete()

Deletes this card permanently. Returns Future<void>.

click()

Handles a click event (logs click conversion).

view()

Handles a view event (logs view conversion).

InsiderAppCardContent

Model class containing the title and description of a card.

Name

Type

Nullable

Description

title

String

No

The content title

description

String

No

The content description

InsiderAppCardImage

Model class representing an image within a card.

Name

Type

Nullable

Description

url

String

No

The image URL

InsiderAppCardButton

Model class representing a button in a card.

Name

Type

Nullable

Description

id

String

No

The button identifier.

text

String

No

The button display text.

appCardId

String?

Yes

The parent card ID (auto-populated when parsed from InsiderAppCard.fromMap).

action

InsiderAppCardAction?

Yes

The button action, or null.

Convenience Methods

Method

Description

click()

Handles a click event (logs button click event).

InsiderAppCardAction (Abstract)

Abstract base class for all app card action models.

Name

Type

Nullable

Description

actionType

String

No

Returns the action type

Action Types

Class

Type Value

Description

InsiderAppCardDeeplinkAction

"deep_link"

Deeplink navigation (URL scheme, internal/external browser).

InsiderAppCardOpenSettingsAction

"open_settings"

Opens the app's system settings.

InsiderAppCardFeedbackAction

"feedback"

Triggers the in-app feedback flow.

InsiderAppCardDeeplinkAction

Deep link action with support for URL schemes, internal browser, and external browser URLs.

Name

Type

Nullable

Description

urlScheme

String?

Yes

Custom URL scheme (e.g.myapp://home).

internalBrowserUrl

String?

Yes

URL opened in the in-app browser.

externalBrowserUrl

String?

Yes

URL opened in the system browser.

json

String?

Yes

Raw JSON payload delivered alongside the deep link.

keysAndValues

List<Map<String, String>>?

Yes

List of key-value pairs, or null.

Getters

Getter

Return Type

Description

url

String

The resolved URL (urlScheme > internalBrowserUrl > externalBrowserUrl).

jsonMap

Map<String, dynamic>

Parsed JSON as a map; empty on parse failure.

keyValueMap

Map<String, dynamic>

Flattened key/value pairs as a single map.

InsiderAppCardOpenSettingsAction

Opens the OS-level settings screen for the application. No additional fields.

InsiderAppCardFeedbackAction

Triggers the Insider in-app feedback flow. No additional fields.

InsiderAppCardType

Constants class for card type identifiers.

Constant

Value

Description

MESSAGE

"message"

Text-only card

IMAGE

"image"

Image-forward card

InsiderAppCardActionType

Constants class for action type identifiers.

Constant

Value

Description

DEEP_LINK

"deep_link"

Deeplink navigation

OPEN_SETTINGS

"open_settings"

Opens system settings

FEEDBACK

"feedback"

Opens feedback flow

InsiderAppCardCategory

Represents a single category assigned to an app card.

Name

Type

Required

Description

id

int

Yes

Unique identifier of the category

name

string

Yes

Category name as entered in the panel, e.g. "promotion"

Match on name for app logic — it is the human-readable value configured in the panel. id is stable and useful as a key.

InsiderAppCard.categories

InsiderAppCard gains one new property.

Name

Type

Required

Description

categories

List<InsiderAppCardCategory>?

No

Categories attached to the card. May be null or empty when the card has none

Unlike the React Native and Cordova plugins, where categories is always an array, on Flutter, a card with no categories arrives as null on Android and as an empty list on iOS. Always normalize before iterating.

for (final category in card.categories ?? []) {
  print('${category.id} ${category.name}');
}

card.categories! throws at runtime on the first uncategorized card. Because iOS never produces null, an iOS-only test pass will not catch it; the crash appears on Android.

Access categories

Categories appear in the getCampaigns() response for every card. No extra call is needed.

final appCards = FlutterInsider.Instance.appCards;


try {
  final response = await appCards.getCampaigns();


  for (final card in response?.appCards ?? []) {
    final names = (card.categories ?? []).map((category) => category.name);
    print('App Card: ${card.id} Categories: ${names.join(', ')}');
  }
} on InsiderAppCardsException catch (e) {
  print('Failed to fetch campaigns: ${e.error.code}');
}

Common use cases

Filter cards by category

Show only the cards belonging to a given category, e.g., a "Promotions" tab in an inbox screen.

List<InsiderAppCard> cardsInCategory(
  List<InsiderAppCard> cards,
  String categoryName,
) {
  return cards
      .where((card) => (card.categories ?? [])
          .any((category) => category.name == categoryName))
      .toList();
}


final response = await FlutterInsider.Instance.appCards.getCampaigns();
final promotions = cardsInCategory(response?.appCards ?? [], 'promotion');

A card may belong to more than one category, so it can legitimately match multiple filters.

Group cards by category

Build a sectioned list. A card with multiple categories appears in each of its groups; a card with none is collected under a default bucket.

Map<String, List<InsiderAppCard>> groupByCategory(List<InsiderAppCard> cards) {
  final groups = <String, List<InsiderAppCard>>{};


  for (final card in cards) {
    final categories = card.categories ?? [];


    if (categories.isEmpty) {
      groups.putIfAbsent('other', () => []).add(card);
      continue;
    }


    for (final category in categories) {
      groups.putIfAbsent(category.name, () => []).add(card);
    }
  }


  return groups;
}

Style a card by category

const categoryColors = <String, Color>{
  'promotion': Colors.orange,
  'onboarding': Colors.blue,
};


Color? colorForCard(InsiderAppCard card) {
  for (final category in card.categories ?? []) {
    final color = categoryColors[category.name];
    if (color != null) {
      return color;
    }
  }
  return null;
}


Widget buildCard(InsiderAppCard card) {
  return GestureDetector(
    onTap: card.click,
    child: Container(
      color: colorForCard(card) ?? Colors.white,
      child: Text(card.content?.title ?? ''),
    ),
  );
}

Always keep a default: a category created on the panel after your app shipped will not have an entry in your local map.

Dart types

InsiderAppCard and InsiderAppCardCategory are exported from the plugin's single entry point; no separate model import is needed.

import 'package:flutter_insider/flutter_insider.dart';


bool hasCategory(InsiderAppCard card, String name) {
  return (card.categories ?? []).any((category) => category.name == name);
}

categories is declared as List<InsiderAppCardCategory>?, so the analyzer flags an unhandled null at compile time rather than letting it surface at runtime.

Behavior notes

Case

Behavior

Card has no categories.

categories is null on Android, an empty list on iOS

Categories are missing from the response.

categories is null.

A single category entry is invalid.

That entry is ignored; the remaining categories are kept, and the card is still returned

An invalid category entry never fails the whole getCampaigns() call. The entry is dropped and the card is still delivered.

  • Treat null and an empty list identically. Both mean "this card has no categories". card.categories ?? [] covers each case in one expression.

  • Categories are delivered only in the getCampaigns() response. markAsRead, markAsUnread, and delete operate on card identifiers and are unaffected.

  • The order is the order configured on the panel; the SDK does not sort or deduplicate.

  • Treat an unrecognized category name as a default-styled card: categories are created on the panel independently of your app releases.

  • Categories do not affect any other App Cards behavior. read/unread state, delete, view(), click(), and button clicks are unchanged.

  • Aside from the null versus empty-list difference noted above, category support behaves identically on iOS and Android.

Available Methods

getCampaigns

This method retrieves the app card campaigns for the current user.

Method signature

Future<InsiderAppCardCampaignsResponse?> getCampaigns()

Method example

final appCards = FlutterInsider.Instance.appCards;

try {
  final response = await appCards.getCampaigns();
  final cards = response?.appCards ?? [];

  for (final card in cards) {
    print('Card ID: ${card.id}');
    print('Is Read: ${card.isRead}');

    final content = card.content;
    if (content != null) {
      print('Title: ${content.title}');
    }
  }
} on InsiderAppCardsException catch (e) {
  print('Failed to get campaigns: ${e.error.code}');
}

markAsRead

This method marks one or more cards as read.

Name

Type

Required

Description

appCardIds

List<String>

Yes

A list of card IDs to mark as read

Method signature

Future<void> markAsRead(List<String> appCardIds)

Method example

final appCards = FlutterInsider.Instance.appCards;
final ids = ['card-001', 'card-002'];

try {
  await appCards.markAsRead(ids);
  print('Cards marked as read successfully');
} on InsiderAppCardsException catch (e) {
  print('Failed to mark as read: ${e.error.code}');
}

markAsUnread

This method marks one or more cards as unread.

Name

Type

Required

Description

appCardIds

List<String>

Yes

A list of card IDs to mark as unread

Method signature

Future<void> markAsUnread(List<String> appCardIds)

Method example

final appCards = FlutterInsider.Instance.appCards;
final ids = ['card-001'];

try {
  await appCards.markAsUnread(ids);
  print('Cards marked as unread successfully');
} on InsiderAppCardsException catch (e) {
  print('Failed to mark as unread: ${e.error.code}');
}

You can also use the convenience methods card.markAsRead() and card.markAsUnread() directly on an InsiderAppCard instance to mark a single card.

delete

This method deletes one or more cards from the current user's campaigns. Deleted cards are permanently removed.

Name

Type

Required

Description

appCardIds

List<String>

Yes

A list of card IDs to delete

Method signature

Future<void> delete(List<String> appCardIds)

Method example

final appCards = FlutterInsider.Instance.appCards;
final ids = ['card-001'];

try {
  await appCards.delete(ids);
  print('Cards deleted successfully');
} on InsiderAppCardsException catch (e) {
  print('Failed to delete cards: ${e.error.code}');
}

You can also use the convenience method card.delete() directly on an InsiderAppCard instance to delete a single card.

click

This method logs a click conversion event for a card.

Name

Type

Required

Description

appCard

InsiderAppCard

Yes

The card that was clicked

Method signature

void click(InsiderAppCard appCard)

Method example

// Using the FlutterInsiderAppCards client directly
FlutterInsider.Instance.appCards.click(card);

// Or using the convenience method on the card
card.click();

view

This method logs a view conversion event for a card.

Name

Type

Required

Description

appCard

InsiderAppCard

Yes

The card that was viewed

Method signature

void view(InsiderAppCard appCard)

Method example

// Using the FlutterInsiderAppCards client directly
FlutterInsider.Instance.appCards.view(card);

// Or using the convenience method on the card
card.view();

clickButton

This method logs a button click action event.

Name

Type

Required

Description

button

InsiderAppCardButton

Yes

The button that was clicked

Method signature

void clickButton(InsiderAppCardButton button)

Method example

// Using the FlutterInsiderAppCards client directly
FlutterInsider.Instance.appCards.clickButton(button);

// Or using the convenience method on the button
button.click();

Full Integration Example

Below is a complete example showing how to retrieve, display, interact with, and manage cards.

final appCards = FlutterInsider.Instance.appCards;

try {
  final response = await appCards.getCampaigns();
  final cards = response?.appCards ?? [];

  for (final card in cards) {
    // Log view event
    card.view();

    // Display card content
    final content = card.content;
    if (content != null) {
      displayCard(content.title, content.description);
    }

    // Display images
    final images = card.images;
    if (images != null) {
      for (final image in images) {
        loadImage(image.url);
      }
    }

    // Handle buttons
    final buttons = card.buttons;
    if (buttons != null) {
      for (final button in buttons) {
        addButton(button.text, () => button.click());
      }
    }

    // Handle card-level action
    final action = card.action;
    if (action is InsiderAppCardDeeplinkAction) {
      final url = action.url;
      print('Action URL: $url Type: ${action.actionType}');
    }

    // Mark as read when user views
    if (!card.isRead) {
      try {
        await card.markAsRead();
        print('Marked as read: ${card.id}');
      } on InsiderAppCardsException catch (e) {
        print('Failed to mark as read: ${e.error.code}');
      }
    }
  }
} on InsiderAppCardsException catch (e) {
  print('Failed to get campaigns: ${e.error.code}');
}