In Jetpack Compose, when you enable clickable {} on a modifier for a composable, by default it enables ripple effect for it. How to disable this behavior?
Example code
Row(modifier = Modifier
         .clickable { // action }
)
In Jetpack Compose, when you enable clickable {} on a modifier for a composable, by default it enables ripple effect for it. How to disable this behavior?
Example code
Row(modifier = Modifier
         .clickable { // action }
)
 
    
     
    
    Short answer:
to disable the ripple pass null in the indication parameter in the clickable modifier:
val interactionSource = remember { MutableInteractionSource() }
Column {
    Text(
        text = "Click me without any ripple!",
        modifier = Modifier
            .clickable(
                interactionSource = interactionSource,
                indication = null
            ) {
                /* doSomething() */
            }
    )
Why it doesn't work with some Composables as Buttons:
Note that in some Composables, like Button or IconButton, it doesn't work since the indication is defined internally by the component which uses indication = rememberRipple(). This creates and remembers a Ripple using values provided by RippleTheme.
In this cases you can't disable it but you can change the appearance of the ripple that is based on a RippleTheme. You can define a custom RippleTheme and apply it to your composable with the LocalRippleTheme.
Something like:
CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {
    Button(
        onClick = { /*...*/ },
    ) {
       //...
    }
}
with:
private object NoRippleTheme : RippleTheme {
    @Composable
    override fun defaultColor() = Color.Unspecified
    @Composable
    override fun rippleAlpha(): RippleAlpha = RippleAlpha(0.0f,0.0f,0.0f,0.0f)
}
Custom modifier
If you prefer you can build your custom Modifier with the same code above, you can use:
fun Modifier.clickableWithoutRipple(
    interactionSource: MutableInteractionSource,
    onClick: () -> Unit
) = composed(
    factory = {
        this.then(
            Modifier.clickable(
                interactionSource = interactionSource,
                indication = null,
                onClick = { onClick() }
            )
        )
    }
)
and then just apply it:
    Row(
        modifier = Modifier
            .clickableWithoutRipple(
                interactionSource = interactionSource,
                onClick = { doSomething() }
            )
    ){ 
      //Row content
    }
Long answer:
If you add the  clickable modifier to a composable to make it clickable within its bounds it will show an Indication as specified in indication parameter.
By default, indication from LocalIndication will be used.
If you are using a MaterialTheme in your hierarchy, a Ripple, defined by rememberRipple(), will be used as the default Indication inside components such as androidx.compose.foundation.clickable and androidx.compose.foundation.indication.
 
    
    Use this Modifier extension:
fun Modifier.noRippleClickable(onClick: () -> Unit): Modifier = composed {
    clickable(indication = null,
        interactionSource = remember { MutableInteractionSource() }) {
        onClick()
    }
}
then simply replace Modifier.clickable {} with Modifier.noRippleClickable {}
Row(modifier = Modifier.noRippleClickable { 
  // action 
})
 
    
    To disable the ripple effect, have to pass null to indication property of the modifier.
More about indication on Jetpack Compose documentation
Code
Row(
    modifier = Modifier
        .clickable(
            indication = null, 
            interactionSource = remember { MutableInteractionSource() } // This is mandatory
        ) { 
            // action
        }
)
You can handle it this way when working with Buttons.
Create a Ripple interactionSource class
class NoRippleInteractionSource : MutableInteractionSource {
override val interactions: Flow<Interaction> = emptyFlow()
override suspend fun emit(interaction: Interaction) {}
override fun tryEmit(interaction: Interaction) = true
}
In case of a button, you can handle it by passing the ripple interaction class as the interactionSource parameter i.e:
 Button(
    onClick = { /*...*/ },
    interactionSource = NoRippleInteractionSource()
) {
    //..
}
This solution works with all compossables that accept a mutableInteractionSource as a parameter for example Button(), TextButton(), Switch(), etc
When working the clickable modifier you can do it this way:
modifier = Modifier.clickable(
                    indication = null,
                    interactionSource = NoRippleInteractionSource()
                ) { /** todo  action here */ }
You can also go a further step to modify the above answer and create an extension function and re use for all your clickable functions i.e:
fun Modifier.noRippleClick(onClick:()->Unit):Modifier {
  return this.clickable(
    interactionSource = NoRippleInteractionSource(),
    indication = null
   ){ 
      onClick() 
   } 
 }
You can now use the extension modifier in place of the clickable modifier i.e:
modifier = modifier.noRippleClick { /** todo  action here */  }
 
    
    Modifier extension with other parameters :
inline fun Modifier.noRippleClickable(
        enabled: Boolean = true,
        onClickLabel: String? = null,
        role: Role? = null,
        crossinline onClick: ()->Unit
    ): Modifier = composed {
        clickable(
            enabled = enabled,
            indication = null,
            onClickLabel = onClickLabel,
            role = role,
            interactionSource = remember { MutableInteractionSource() }) {
            onClick()
        }
    }
 
    
    I used @Mahdi-Malv's answer and modify as below:
fun Modifier.noRippleClickable( interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, indication: Indication? = null, enabled: Boolean = true, onClickLabel: String? = null, role: Role? = null, onClick: () -> Unit, ) = clickable( interactionSource = interactionSource, indication = indication, enabled = enabled, onClickLabel = onClickLabel, role = role, onClick = onClick, )
 
    
    With androidx.compose.foundation there is a enabled attribute inside clickable extension. I think that it is easiest way. Link
fun Modifier.clickable(
    interactionSource: MutableInteractionSource,
    indication: Indication?,
    enabled: Boolean = true,
    onClickLabel: String? = null,
    role: Role? = null,
    onClick: () -> Unit
): Modifier
