InteractionSource
interface InteractionSource
InteractionSource represents a stream of Interactions corresponding to events emitted by a
component. These Interactions can be used to change how components appear in different states,
such as when a component is pressed or dragged.
A common use case is androidx.compose.foundation.indication, where
androidx.compose.foundation.Indication implementations can subscribe to an InteractionSource
to draw indication for different Interactions, such as a ripple effect for
PressInteraction.Press and a state overlay for DragInteraction.Start.
For simple cases where you are interested in the binary state of an Interaction, such as
whether a component is pressed or not, you can use InteractionSource.collectIsPressedAsState
and other extension functions that subscribe and return a Boolean State representing whether
the component is in this state or not.
For more complex cases, such as when building an androidx.compose.foundation.Indication, the
order of the events can change how a component / indication should be drawn. For example, if a
component is being dragged and then becomes focused, the most recent Interaction is
FocusInteraction.Focus, so the component should appear in a focused state to signal this event
to the user.
InteractionSource exposes interactions to support these use cases - a Flow representing the
stream of all emitted Interactions. This also provides more information, such as the press
position of PressInteraction.Press, so you can show an effect at the specific point the
component was pressed, and whether the press was PressInteraction.Release or
PressInteraction.Cancel, for cases when a component should behave differently if the press was
released normally or interrupted by another gesture.
You can collect from interactions as you would with any other Flow:
To emit Interactions so that consumers can react to them, see MutableInteractionSource.
Properties
val interactions: Flow<Interaction>
Flow representing the stream of all Interactions emitted through this
InteractionSource. This can be used to see Interactions emitted in order, and with
additional metadata, such as the press position for PressInteraction.Press.
Code Examples
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(),
)
}
}
}
