Interface

InteractionSource

InteractionSource represents a stream of [Interaction]s corresponding to events emitted by a component.

InteractionSourceFlowSample

@Composable
fun InteractionSourceFlowSample() {
    // Hoist the MutableInteractionSource that we will provide to interactions
    val interactionSource = remember { MutableInteractionSource() }
    // Provide the MutableInteractionSource instances to the interactions we want to observe state
    // changes for
    val draggable =
        Modifier.draggable(
            interactionSource = interactionSource,
            orientation = Orientation.Horizontal,
            state = rememberDraggableState { /* update some business state here */ },
        )
    val clickable =
        Modifier.clickable(
            interactionSource = interactionSource,
            // This component is a compound component where part of it is clickable and part of it
            // is
            // draggable. As a result we want to show indication for the _whole_ component, and not
            // just for clickable area. We set `null` indication here and provide an explicit
            // Modifier.indication instance later that will draw indication for the whole component.
            indication = null,
        ) { /* update some business state here */
        }
    // SnapshotStateList we will use to track incoming Interactions in the order they are emitted
    val interactions = remember { mutableStateListOf<Interaction>() }
    // Collect Interactions - if they are new, add them to `interactions`. If they represent stop /
    // cancel events for existing Interactions, remove them from `interactions` so it will only
    // contain currently active `interactions`.
    LaunchedEffect(interactionSource) {
        interactionSource.interactions.collect { interaction ->
            when (interaction) {
                is PressInteraction.Press -> interactions.add(interaction)
                is PressInteraction.Release -> interactions.remove(interaction.press)
                is PressInteraction.Cancel -> interactions.remove(interaction.press)
                is DragInteraction.Start -> interactions.add(interaction)
                is DragInteraction.Stop -> interactions.remove(interaction.start)
                is DragInteraction.Cancel -> interactions.remove(interaction.start)
            }
        }
    }
    // Display some text based on the most recent Interaction stored in `interactions`
    val text =
        when (interactions.lastOrNull()) {
            is DragInteraction.Start -> "Dragged"
            is PressInteraction.Press -> "Pressed"
            else -> "No state"
        }
    Column(Modifier.fillMaxSize().wrapContentSize()) {
        Row(
            // Draw indication for the whole component, based on the Interactions dispatched by
            // our hoisted MutableInteractionSource
            Modifier.indication(
                interactionSource = interactionSource,
                indication = LocalIndication.current,
            )
        ) {
            Box(
                Modifier.size(width = 240.dp, height = 80.dp)
                    .then(clickable)
                    .border(BorderStroke(3.dp, Color.Blue))
                    .padding(3.dp)
            ) {
                val pressed = interactions.any { it is PressInteraction.Press }
                Text(
                    text = if (pressed) "Pressed" else "Not pressed",
                    style = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
                    modifier = Modifier.fillMaxSize().wrapContentSize(),
                )
            }
            Box(
                Modifier.size(width = 240.dp, height = 80.dp)
                    .then(draggable)
                    .border(BorderStroke(3.dp, Color.Red))
                    .padding(3.dp)
            ) {
                val dragged = interactions.any { it is DragInteraction.Start }
                Text(
                    text = if (dragged) "Dragged" else "Not dragged",
                    style = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
                    modifier = Modifier.fillMaxSize().wrapContentSize(),
                )
            }
        }
        Text(
            text = text,
            style = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
            modifier = Modifier.fillMaxSize().wrapContentSize(),
        )
    }
}

SimpleInteractionSourceSample

@Composable
fun SimpleInteractionSourceSample() {
    // Hoist the MutableInteractionSource that we will provide to interactions
    val interactionSource = remember { MutableInteractionSource() }
    // Provide the MutableInteractionSource instances to the interactions we want to observe state
    // changes for
    val draggable =
        Modifier.draggable(
            interactionSource = interactionSource,
            orientation = Orientation.Horizontal,
            state = rememberDraggableState { /* update some business state here */ },
        )
    val clickable =
        Modifier.clickable(
            interactionSource = interactionSource,
            indication = LocalIndication.current,
        ) { /* update some business state here */
        }
    // Observe changes to the binary state for these interactions
    val isDragged by interactionSource.collectIsDraggedAsState()
    val isPressed by interactionSource.collectIsPressedAsState()
    // Use the state to change our UI
    val (text, color) =
        when {
            isDragged && isPressed -> "Dragged and pressed" to Color.Red
            isDragged -> "Dragged" to Color.Green
            isPressed -> "Pressed" to Color.Blue
            // Default / baseline state
            else -> "Drag me horizontally, or press me!" to Color.Black
        }
    Box(Modifier.fillMaxSize().wrapContentSize().size(width = 240.dp, height = 80.dp)) {
        Box(
            Modifier.fillMaxSize()
                .then(clickable)
                .then(draggable)
                .border(BorderStroke(3.dp, color))
                .padding(3.dp)
        ) {
            Text(
                text,
                style = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
                modifier = Modifier.fillMaxSize().wrapContentSize(),
            )
        }
    }
}