Jetpack Compose ᴺᴱᵂ

The spotim-compose module provides two Composables — PreConversation and Conversation — that wrap the SDK's Fragment-based API for Jetpack Compose apps. Both composables handle the Fragment lifecycle internally so you do not need to interact with FragmentManager directly.


ComposableDescription
PreConversationPre-conversation teaser showing comment count and top comments. Opens the full conversation on tap.
ConversationFull conversation experience with threaded comments and comment input.

⚠️

Prerequisites

Assign your spotId before using any composables. Your spotId is provided by your OpenWeb PSM.

OpenWeb.manager.spotId = "YOUR_SPOT_ID"

Add the Dependency

Add the compose module to your app's build.gradle. Use the same version as the core SDK.

implementation 'io.github.spotim:spotim-compose:3.0.0'

Required Activity Setup

The Conversation composable requires specific window configuration to keep the comment input visible above the soft keyboard. Apply all three steps to the activity that hosts the composable.

Note: PreConversation does not host a text input, so this setup is only required when using Conversation.

  1. Set windowSoftInputMode in your AndroidManifest.xml:
<activity
    android:name=".YourActivity"
    android:windowSoftInputMode="adjustResize" />

Note: adjustResize is required on API 29 and below, where WindowInsets.ime is only reported when this flag is set. On API 30 and above, enableEdgeToEdge() handles keyboard insets automatically, but having adjustResize set is harmless.

  1. Call enableEdgeToEdge() in your activity's onCreate:
class YourActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            YourTheme {
                // your composable tree
            }
        }
    }
}
  1. Apply window insets padding on the root Compose container:
Box(
    modifier = Modifier
        .padding(
            WindowInsets.ime
                .union(WindowInsets.systemBars)
                .asPaddingValues()
        )
        .consumeWindowInsets(WindowInsets.systemBars)
) {
    // your content
}

Important: Do NOT add Modifier.imePadding() anywhere in the Compose tree when using the Conversation composable. Adding it causes double-padding and breaks keyboard handling.

PreConversation

Displays the pre-conversation teaser — comment count, top comments, and a prompt to open the full conversation. Embed it in article feeds or detail screens.

import spotIm.compose.PreConversation

PreConversation(
    postId = "POST_ID",
    onError = { exception ->
        // fires if the host is not a FragmentActivity, or if the fragment fails to load
    }
)

Parameters

ParameterPurpose
postIdIdentifies the content item, matching the post ID used in your CMS. (String, required)
modifierApplied to the root container of the composable. (Modifier, default Modifier)
articleSettingsArticle metadata source. Defaults to fetching from the OpenWeb backend. (OWArticleSettings, default OWArticleSettings())
additionalSettingsBehavioural overrides for the pre-conversation. (OWAdditionalSettings, default OWAdditionalSettings())
conversationNavigationOptionsControls how the full conversation opens when the user taps. (OWConversationNavigationOptions, default ConversationFullScreen())
onErrorCalled on the main thread if the host is not a FragmentActivity or the fragment fails to load. ((SpotException) -> Unit?, default null)

Navigation modes

OptionBehaviour
OWConversationNavigationOptions.ConversationFullScreen()SDK opens the full conversation full-screen (default). Pass an optional OWFlowActionsCallbacks to receive action events.
OWConversationNavigationOptions.ConversationCustom(preConversationActionsCallbacks)You handle the transition. Implement OWPreConversationActionsCallbacks.openConversationFlow(route) to push your own screen.
// Default: SDK manages navigation
PreConversation(postId = "POST_ID")

// Custom: you handle the transition
PreConversation(
    postId = "POST_ID",
    conversationNavigationOptions = OWConversationNavigationOptions.ConversationCustom(
        preConversationActionsCallbacks = object : OWPreConversationActionsCallbacks {
            override fun openConversationFlow(route: OWConversationRoute) {
                // navigate to your own conversation screen, passing route
            }
        }
    )
)

Conversation

Embeds the full conversation experience inline in your Compose layout. Apply the required activity setup above before using this composable.

import spotIm.compose.Conversation

Conversation(
    postId = "POST_ID",
    onError = { exception ->
        // fires if the host is not a FragmentActivity, or if the fragment fails to load
    }
)

Parameters

ParameterPurpose
postIdIdentifies the content item, matching the post ID used in your CMS. (String, required)
modifierApplied to the root container of the composable. (Modifier, default Modifier)
articleSettingsArticle metadata source. Defaults to fetching from the OpenWeb backend. (OWArticleSettings, default OWArticleSettings())
routeDeep-links to a specific entry point within the conversation. (OWConversationRoute?, default null)
additionalSettingsBehavioural overrides for the conversation. (OWAdditionalSettings, default OWAdditionalSettings())
onActionCalled on the main thread for SDK-level action events. ((OWFlowActionCallbackType, OWFlowSourceType, OWPostId) -> Unit?, default null)
onErrorCalled on the main thread if the host is not a FragmentActivity or the fragment fails to load. ((SpotException) -> Unit?, default null)

Route options

Pass a route to deep-link the conversation to a specific entry point on load.

RoutePurpose
nullOpens the conversation at the top (default).
OWConversationRoute.OWCommentThreadRoute(commentId)Scrolls to a specific comment thread.
OWConversationRoute.OWCommentCreationRoute(type)Opens the comment editor. Pass OWCommentCreationType.Comment for a new top-level comment.
// Open a specific comment thread
Conversation(
    postId = "POST_ID",
    route = OWConversationRoute.OWCommentThreadRoute(commentId = "COMMENT_ID")
)

// Open the comment creation screen
Conversation(
    postId = "POST_ID",
    route = OWConversationRoute.OWCommentCreationRoute(type = OWCommentCreationType.Comment)
)

Action callbacks

Use onAction to respond to SDK-level events such as conversation dismissal or publisher profile taps.

OWFlowActionCallbackTypeWhen it fires
ConversationDismissedThe conversation was dismissed by the user.
OpenPublisherProfile(context, ssoPublisherId, type)The user tapped a publisher profile. Use ssoPublisherId to navigate to your own profile screen.
Conversation(
    postId = "POST_ID",
    onAction = { actionType, source, postId ->
        when (actionType) {
            is OWFlowActionCallbackType.OpenPublisherProfile -> {
                // navigate to your profile screen using actionType.ssoPublisherId
            }
            OWFlowActionCallbackType.ConversationDismissed -> {
                // conversation was dismissed
            }
        }
    }
)

Fragment Lifecycle

Both composables host a Fragment internally. The fragment is preserved across configuration changes (rotation, font size changes) and reattached on recomposition — you do not need to save or restore any state. Changing postId or route across recompositions triggers a fresh fragment load.