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)
}
}