Reputation: 4961
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 }
)
Upvotes: 156
Views: 87865
Reputation: 178
Now it is much easier: just add enabled parameter
Row(
modifier = Modifier
.clickable(enabled = state.isClickable) { onClick.invoke() }
Upvotes: -1
Reputation: 299
When using combinedClickable
, it works like this...
Row(
modifier = modifier
.combinedClickable(
onClick = {},
interactionSource = remember { MutableInteractionSource() },
indication = null,
),
)
Upvotes: 1
Reputation: 1627
best solution is: use noRippleClickable
modifier.noRippleClickable {
}
Upvotes: -3
Reputation: 606
As part of the new Ripple API provided by Google in its alpha versions to date (April 2024):
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0-alpha05
androidx.wear.compose:compose-material:1.4.0+
You just need to follow this amazing documentation that says:
CompositionLocalProvider(
LocalRippleConfiguration provides RippleConfiguration(isEnabled = false)
) {
Button {
// ...
}
}
That Button
or any other composable inside the block CompositionLocalProvider
won't have ripple.
Important Update May 2024
Google has updated Material3 library to 1.3.0-alpha06
and disabling the ripple effect has changed to this:
CompositionLocalProvider(
LocalRippleConfiguration provides null
) {
Button {
// ...
}
}
Google's repository with this change
Upvotes: 8
Reputation: 19190
Use this Modifier extension:
fun Modifier.noRippleClickable(onClick: () -> Unit): Modifier = composed {
this.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }) {
onClick()
}
}
then simply replace Modifier.clickable {}
with Modifier.noRippleClickable {}
Row(modifier = Modifier.noRippleClickable {
// action
})
Upvotes: 143
Reputation: 1679
Creates a custom Modifier that makes a Composable clickable without displaying a ripple effect when clicked.
fun Modifier.noRippleClickable(
onClick: () -> Unit
): Modifier = composed {
clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }) {
onClick()
}
}
After that, you can use it like below:
modifier = Modifier.noRippleClickable(
onClick = {
}),
Upvotes: 4
Reputation: 674
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 */ }
Upvotes: 27
Reputation: 1180
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
Upvotes: -2
Reputation: 363379
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
.
Upvotes: 196
Reputation: 832
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, )
Upvotes: 0
Reputation: 553
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()
}
}
Upvotes: 0
Reputation: 4961
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
}
)
Upvotes: 119