Skip to main content

Events and typed payloads

Events

The KMP SDK exposes five focused listener interfaces. Register them globally or on an individual widget. A per-widget listener takes precedence over the corresponding global listener; unset categories fall back to the global listener.

val listeners = object : StorifyMeWidgetListener {
override fun onLoaded(widgetId: Long, experiences: List<StorifyMeExperience>) { }
override fun onFailed(error: StorifyMeError) { }
override fun onSizeChanged(widgetId: Long, height: Int) { }
override fun onScrolled(widgetId: Long, offsetX: Int) { }
}

StorifyMe.widgetListener = listeners
widget.setWidgetListener(listeners)

Focused callbacks

ListenerCallbacks
StorifyMeWidgetListeneronLoaded, onFailed, onSizeChanged, onScrolled
StorifyMeExperienceListeneronOpened, onFinished, onClosed, onShared, onNavigationRequested
StorifyMeAdListeneronAdDisplayed
StorifyMeInteractionListeneronAction, onEvent, onLinkTriggered
StorifyMeCommerceListeneronCartItemAdded, onCartItemRemoved, onCartUpdated, onCartCleared, onCheckout

All callbacks use the native v3 names. onOpened and onFinished receive a non-null StorifyMeExperience; onClosed may receive null.

Experience model

StorifyMeExperience is the common model for stories, shorts, snaps, and ads:

fun logExperience(experience: StorifyMeExperience) {
println("${experience.type} ${experience.id} ${experience.name}")
println("handle=${experience.handle} url=${experience.navigationUrl}")
println("duration=${experience.totalDuration} poster=${experience.thumbnailUrl}")
}

Use StorifyMeExperienceType.SHORTS for short-form content. The canonical properties are navigationUrl, totalDuration, and thumbnailUrl.

The experience listener can decide whether the native viewer should continue opening an experience:

val experienceListener = object : StorifyMeExperienceListener {
override fun onNavigationRequested(
experience: StorifyMeExperience,
completion: (StorifyMeExperienceNavigationDecision) -> Unit,
) {
val url = experience.navigationUrl
if (url != null && routeInApp(url)) {
openInApp(url)
completion(StorifyMeExperienceNavigationDecision.HANDLE_NAVIGATION)
} else {
completion(StorifyMeExperienceNavigationDecision.OPEN_EXPERIENCE)
}
}
}

Always call the completion exactly once. The default is OPEN_EXPERIENCE.

Links inside an experience use the interaction listener:

val interactionListener = object : StorifyMeInteractionListener {
override fun onLinkTriggered(
url: String,
completion: (StorifyMeLinkTriggerCompletion) -> Unit,
) {
completion(StorifyMeLinkTriggerCompletion.OPEN_LINK_BY_DEFAULT)
}
}

The alternative IGNORE_PRESENTING_LINK leaves link handling to the host app. Its completion must also be called exactly once.

Typed interactions and commerce

onAction receives StorifyMeInteractionAction, including type, id, value, button/quiz fields, href, eventData, and a typed StorifyMeInteractionContext. onEvent receives StorifyMeInteractionEvent with action, value, and context. Both models retain rawData: Map<String, Any?> for fields introduced by the dashboard in future versions.

Commerce callbacks expose StorifyMeCartItem for item additions/removals and StorifyMeCart for updated, cleared, and checkout state. Cart payloads retain their raw maps as well.

val commerceListener = object : StorifyMeCommerceListener {
override fun onCartUpdated(cart: StorifyMeCart) {
cart.items.forEach { item ->
println("${item.id}: ${item.quantity} × ${item.price} ${item.currency}")
}
}

override fun onCheckout(cart: StorifyMeCart) {
analytics.trackCheckout(cart.total, cart.currency)
}
}