InteractionSource

Known direct subclasses
MutableInteractionSource

MutableInteractionSource represents a stream of Interactions corresponding to events emitted by a component.


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 representing whether the component is in this state or not.

import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.rememberDraggableState import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.Interaction import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsDraggedAsState import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.LocalTextStyle import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp // 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(),  )  } }

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:

import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.rememberDraggableState import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.DragInteraction import androidx.compose.foundation.interaction.Interaction import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.LocalTextStyle import androidx.compose.material.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp // 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(),  ) }

To emit Interactions so that consumers can react to them, see MutableInteractionSource.

Summary

Public properties

Flow<Interaction>

Flow representing the stream of all Interactions emitted through this InteractionSource.

Cmn

Extension functions

State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is dragged or not.

Cmn
State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is focused or not.

Cmn
State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is hovered or not.

Cmn
State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is pressed or not.

Cmn

Public properties

interactions

val interactionsFlow<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.

import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.rememberDraggableState import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.DragInteraction import androidx.compose.foundation.interaction.Interaction import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.LocalTextStyle import androidx.compose.material.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp // 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(),  ) }

Extension functions

collectIsDraggedAsState

@Composable
fun InteractionSource.collectIsDraggedAsState(): State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is dragged or not.

DragInteraction is typically set by interactions such as androidx.compose.foundation.gestures.draggable and androidx.compose.foundation.gestures.scrollable, and higher level components such as androidx.compose.foundation.lazy.LazyRow, available through androidx.compose.foundation.lazy.LazyListState.interactionSource.

Returns
State<Boolean>

State representing whether this component is being dragged or not

collectIsFocusedAsState

@Composable
fun InteractionSource.collectIsFocusedAsState(): State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is focused or not.

FocusInteraction is typically set by androidx.compose.foundation.focusable and focusable components, such as androidx.compose.foundation.text.BasicTextField.

Returns
State<Boolean>

State representing whether this component is being focused or not

collectIsHoveredAsState

@Composable
fun InteractionSource.collectIsHoveredAsState(): State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is hovered or not.

HoverInteraction is typically set by androidx.compose.foundation.hoverable and hoverable components.

Returns
State<Boolean>

State representing whether this component is being hovered or not

collectIsPressedAsState

@Composable
fun InteractionSource.collectIsPressedAsState(): State<Boolean>

Subscribes to this MutableInteractionSource and returns a State representing whether this component is pressed or not.

PressInteraction is typically set by androidx.compose.foundation.clickable and clickable higher level components, such as buttons.

Returns
State<Boolean>

State representing whether this component is being pressed or not