ImMathan
ImMathan

Reputation: 4961

How to disable ripple effect when clicking in Jetpack Compose

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

Answers (12)

Svetlana Rozhkova
Svetlana Rozhkova

Reputation: 178

Now it is much easier: just add enabled parameter

        Row(
        modifier = Modifier
            .clickable(enabled = state.isClickable) { onClick.invoke() }

Upvotes: -1

Pezcraft
Pezcraft

Reputation: 299

When using combinedClickable, it works like this...

Row(
   modifier = modifier
       .combinedClickable(
           onClick = {},
           interactionSource = remember { MutableInteractionSource() },
           indication = null,
       ),
)

Upvotes: 1

Mehrdad
Mehrdad

Reputation: 1627

best solution is: use noRippleClickable

modifier.noRippleClickable {
            
        }

Upvotes: -3

AndroidZen
AndroidZen

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

Mahdi-Malv
Mahdi-Malv

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

WhoisAbel
WhoisAbel

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

Felix Kariuki
Felix Kariuki

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

Enes Zor
Enes Zor

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

Gabriele Mariotti
Gabriele Mariotti

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

Rucha Bhatt Joshi
Rucha Bhatt Joshi

Reputation: 832

I used @Mahdi-Malv's answer and modify as below:

  • remove inline and crossinline
  • modify according to my requirement
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

Mahdi Safarmohammadloo
Mahdi Safarmohammadloo

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

ImMathan
ImMathan

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

Related Questions