Skip to main content

In-app messaging

OpenCDP fetches eligible in-app messages from the Data Gateway using the current screen (Include / Exclude page rules). You can either:

  1. Recommended — auto-present modal / banner via the SDK entry point, or
  2. Custom UI — listen to the message stream and render yourself (inline / inbox_card always need a host layout slot).

Supported on Flutter, Android, iOS, and React Native (via native modules).

PlatformAuto-present entryScreen sourceCustom escape hatch
FlutterWrap app with OpenCDPInAppHostautoTrackScreens + navigatorObservers / named routesmessageStream listener
AndroidenableInAppAutoPresent: true at initActivity title / trackScreenViewaddInAppListener
iOSenableInAppAutoPresent: true at initVC title / trackScreenViewaddInAppListener

Prerequisites​

  • CDP API key and gateway URL
  • enableInAppMessages: true
  • User identified via identify() before sync
  • Campaign Include screens match the exact strings your SDK sends on /sync?screen=

Flutter​

await OpenCDPSDK.initialize(
config: OpenCDPConfig(
cdpApiKey: 'YOUR_CDP_API_KEY',
enableInAppMessages: true,
enableInAppAutoPresent: true,
autoTrackScreens: true,
),
);

MaterialApp.router(
navigatorObservers: [
if (OpenCDPSDK.instance.screenTracker != null)
OpenCDPSDK.instance.screenTracker!,
],
builder: (context, child) => OpenCDPInAppHost(
child: child!,
// Optional: keep auto-present, use your own modal/banner design
modalBuilder: (context, message) => MyBrandedModal(message: message),
bannerBuilder: (context, message, {required onPrimaryCta, required onClose}) =>
MyBrandedBanner(message: message, onCta: onPrimaryCta, onClose: onClose),
),
);

// Place where inline should appear in a screen:
OpenCDPInAppInlineSlot(slotId: 'home_above_balance');

Your modalBuilder widget should Navigator.pop(context, ctaIdOrNull) so the host can track click/dismiss. Omit the builders to use the SDK defaults. Use OpenCDPInAppInlineSlot / OpenCDPInAppInboxSlot for layout-bound render types.

Android​

val config = OpenCDPConfig(
cdpApiKey = "YOUR_CDP_API_KEY",
enableInAppMessages = true,
enableInAppAutoPresent = true,
autoTrackScreens = true,
)
OpenCDP.initialize(this, config)

No Activity wrap required — the SDK presents AlertDialog / banner overlay on the resumed Activity.

iOS​

let config = OpenCDPConfig(
cdpApiKey: "YOUR_CDP_API_KEY",
enableInAppMessages: true,
enableInAppAutoPresent: true,
autoTrackScreens: true
)
OpenCDP.shared.initialize(config: config)

No view wrap required — the SDK presents from the key window / top view controller.

Then identify the user:

await OpenCDPSDK.instance.identify(
identifier: 'user_123',
properties: {'email': '[email protected]'},
);

Navigate to a screen whose name is in the campaign Include list — the modal/banner should appear there automatically. Exclude always wins.


Advanced — custom UI via listener​

Register a listener and branch on render_type. Disable enableInAppAutoPresent if you fully own modal/banner UI (avoid double presentation).

Flutter:

OpenCDPSDK.instance.inApp?.messageStream.listen((message) async {
switch (message.renderType) {
case InAppRenderType.modal:
// Full-screen dialog
break;
case InAppRenderType.banner:
// Top overlay strip
break;
case InAppRenderType.inline:
case InAppRenderType.inboxCard:
// Embed in page or inbox list
break;
case InAppRenderType.unknown:
break;
}
await OpenCDPSDK.instance.inApp?.trackImpression(message);
});

Android (Kotlin):

OpenCDP.addInAppListener { message ->
when (message.renderType) {
InAppRenderType.MODAL -> { /* AlertDialog */ }
InAppRenderType.BANNER -> { /* top overlay */ }
InAppRenderType.INLINE, InAppRenderType.INBOX_CARD -> { /* host slot */ }
else -> {}
}
}

iOS (Swift):

OpenCDP.shared.addInAppListener { message in
switch message.renderType {
case .modal: break
case .banner: break
case .inline, .inboxCard: break
default: break
}
}

Screen targeting (Include / Exclude)​

  1. SDK keeps a current screen (setCurrentScreen / auto-track).
  2. Sync sends ?screen=….
  3. Backend filters deliveries with page_rules (empty Include = all screens; Exclude wins).
  4. Only eligible messages are emitted to the stream / auto-presenter.

Use stable path-like names (/profile/personal-details) that match the dashboard Include picker exactly (case-sensitive).