3 Ways to Disable the Ripple Effect in Jetpack Compose

jetpack compose disable ripple

In this article, we’ll learn how to disable or remove the ripple effect in Jetpack Compose.

Prerequisites:

We can remove the ripple in 3 ways:

  1. Using the Mutable Interaction Source
  2. Create a Custom MutableInteractionSource Class
  3. Create a Custom No Ripple Theme

Let’s see how to implement them in the Android Studio.

First, create an empty Compose project and open MainActivity.kt. Create a composable called MyUI() and call it from the onCreate() method. We will write our code in it.

MainActivity for Material 3 Jetpack Compose:

// we need the following packages
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material.ripple.RippleAlpha
import androidx.compose.material.ripple.RippleTheme
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.example.yourprojectname.ui.theme.YourProjectNameTheme
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {

}

MainActivity for the Material 2 version:

// we need the following packages
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource  
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material.ripple.RippleAlpha
import androidx.compose.material.ripple.RippleTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme(darkTheme = false) {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {
    
}

Note: I am using the Material 3 APIs. If you are working with Material 2, you may see a slightly different appearance, but the code will still work as expected.

1. Using the Mutable Interaction Source:

You can use this method if you set the click listener using the clickable modifier. For example, look at the Box below:

@Composable
fun MyUI() {
    val contextForToast = LocalContext.current.applicationContext

    Box(
        modifier = Modifier
            .size(width = 100.dp, height = 48.dp)  
            .background(color = Color.Yellow)
            .clickable {
                Toast
                    .makeText(contextForToast, "Click", Toast.LENGTH_SHORT)
                    .show()
            },
        contentAlignment = Alignment.Center
    ) {
        Text(text = "Click Me")
    }
}

Output:

Box click with ripple effect

Let’s disable the ripple. First, open the Modifier.clickable() signature. It has two overloads. Open the one with the interactionSource parameter.

fun Modifier.clickable(
    interactionSource: MutableInteractionSource,  
    indication: Indication?,
    enabled: Boolean = true,
    onClickLabel: String? = null,
    role: Role? = null,
    onClick: () -> Unit
)

We can disable the ripple using the interactionSource and indication parameters.

The MutableInteractionSource represents the stream of interactions corresponding to the events emitted by the component.

The Indication represents the visual effects that occur when certain interactions happen. For example, showing the ripple effect when a component is pressed. Set its value to null to remove the ripple.

@Composable
fun MyUI() {
    val interactionSource = remember { MutableInteractionSource() } // or use val interactionSource = MutableInteractionSource()

    val contextForToast = LocalContext.current.applicationContext

    Box(
        modifier = Modifier
            .size(width = 100.dp, height = 48.dp)
            .background(color = Color.Yellow)
            .clickable(
                interactionSource = interactionSource,
                indication = null
            ) {
                Toast
                    .makeText(contextForToast, "Click", Toast.LENGTH_SHORT)
                    .show()
            },
        contentAlignment = Alignment.Center
    ) {
        Text(text = "Click Me")
    }
}

Output:

Box click without ripple effect

2. Create a Custom MutableInteractionSource Class:

This method is helpful when the composable you are working with accepts MutableInteractionSource as the parameter. For example, Switch().

@Composable
fun MyUI() {
    var checked by remember {  
        mutableStateOf(false)
    }

    Switch(
        checked = checked,
        onCheckedChange = {
            checked = !checked
        }
    )
}

Output:

switch with ripple

To disable the ripple, create a custom MutableInteractionSource Class.

class NoRippleInteractionSource : MutableInteractionSource {

    override val interactions: Flow<Interaction> = emptyFlow()  

    override suspend fun emit(interaction: Interaction) {}
    
    override fun tryEmit(interaction: Interaction) = true
}

Create an object of this class and pass it to the Switch.

@Composable
fun MyUI() {
    var checked by remember {
        mutableStateOf(false)
    }

    Switch(
        checked = checked,
        onCheckedChange = {
            checked = !checked
        },
        interactionSource = NoRippleInteractionSource()
    )
}

class NoRippleInteractionSource : MutableInteractionSource {

    override val interactions: Flow<Interaction> = emptyFlow()  

    override suspend fun emit(interaction: Interaction) {}
    
    override fun tryEmit(interaction: Interaction) = true
}

Output:

switch without ripple

Related: 3 Awesome Custom Switch Buttons Made with Jetpack Compose

3. Create a Custom No Ripple Theme:

You can use this method when you want to disable the ripple on multiple composables.

Create a custom theme and remove the ripple color and alpha.

private object NoRippleTheme : RippleTheme {
    @Composable
    override fun defaultColor() = Color.Unspecified

    @Composable
    override fun rippleAlpha(): RippleAlpha = RippleAlpha(0.0f,0.0f,0.0f,0.0f)  
}

Apply it to the composables.

@Composable
fun MyUI() {
    val contextForToast = LocalContext.current.applicationContext

    CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {  
        // add your composables here
        Box(
            modifier = Modifier
                .size(width = 100.dp, height = 48.dp)
                .background(color = Color.Yellow)
                .clickable {
                Toast
                    .makeText(contextForToast, "Click", Toast.LENGTH_SHORT)
                    .show()
                },
            contentAlignment = Alignment.Center
        ) {
            Text(text = "Click Me")
        }
    }
}

private object NoRippleTheme : RippleTheme {
    @Composable
    override fun defaultColor() = Color.Unspecified

    @Composable
    override fun rippleAlpha(): RippleAlpha = RippleAlpha(0.0f,0.0f,0.0f,0.0f)  
}

Output:

Box click without ripple effect

How to Disable Ripple Effect on Jetpack Compose Button?

We can disable the ripple effect on Button() in 2 ways.

1. Using Mutable Interaction Source Parameter:

The Button() composable takes MutableInteractionSource as a parameter.

@Composable
public fun Button(
    onClick: () -> Unit,
    modifier: Modifier,
    enabled: Boolean,
    shape: Shape,
    colors: ButtonColors,
    elevation: ButtonElevation?,
    border: BorderStroke?,
    contentPadding: PaddingValues,
    interactionSource: MutableInteractionSource, // interaction source parameter 
    content: @Composable() (RowScope.() -> Unit)
): Unit

So, we can use the second method to remove the ripple.

@Composable
fun MyUI() {
    val contextForToast = LocalContext.current.applicationContext

    Button(
        onClick = {
                Toast
                    .makeText(contextForToast, "Click", Toast.LENGTH_SHORT)
                    .show()
        },
        interactionSource = NoRippleInteractionSource()
    ) {
        Text(text = "Click")
    }
}

class NoRippleInteractionSource : MutableInteractionSource {

    override val interactions: Flow<Interaction> = emptyFlow()

    override suspend fun emit(interaction: Interaction) {}

    override fun tryEmit(interaction: Interaction) = true
}

Output:

Button disable ripple

2. Create a Button Using the Box:

We can also create a button using the Box and apply the first method.

@Composable
fun MyUI() {
    val interactionSource = remember { MutableInteractionSource() } // or use val interactionSource = MutableInteractionSource()

    val contextForToast = LocalContext.current.applicationContext

    Box(
        modifier = Modifier
            .size(width = 100.dp, height = 48.dp)
            .background(
                color = Color.Magenta,
                shape = RoundedCornerShape(percent = 16)
            )
            .clickable(
                interactionSource = interactionSource,
                indication = null
            ) {
                Toast
                    .makeText(contextForToast, "Click", Toast.LENGTH_SHORT)
                    .show()
            },
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "Click Me",
            color = Color.White
        )
    }
}

Output:

custom button with no ripple

Related: 5 Awesome Custom Buttons Designs Using Jetpack Compose

These are the different ways to disable the ripple in Jetpack Compose. I hope you have learned something new. If you have any doubts, comment below.

Related Articles:

4 thoughts on “3 Ways to Disable the Ripple Effect in Jetpack Compose”

Leave a Comment