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
| Listener | Callbacks |
|---|---|
StorifyMeWidgetListener | onLoaded, onFailed, onSizeChanged, onScrolled |
StorifyMeExperienceListener | onOpened, onFinished, onClosed, onShared, onNavigationRequested |
StorifyMeAdListener | onAdDisplayed |
StorifyMeInteractionListener | onAction, onEvent, onLinkTriggered |
StorifyMeCommerceListener | onCartItemAdded, 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.
Navigation interception
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)
}
}