How to apply ripple effect only to Buttons background, but not not content?

808 Views Asked by At

In XML the ripple effect is applied only to the background of a material button, whereas on Compose, it covers the entire button, also partially covering the text, when button is pressed.

I want to match ripple implementation as it is on XML, so that it does not apply a visual effect on the Buttons text, only background.

Any help is appreciated.

Below is a screenshot of a pressed Compose Button, when ripple (yellow) is covering the text.

enter image description here

Here is the XML version, where yellow ripple is applied only to the background of a Button.

enter image description here

4

There are 4 best solutions below

1
Eliza Camber On

Currently, this isn't possible by using the Button AFAIK. I would try to replace the button with a Box + a Text. Something like this:

@Composable
fun RippleButton() {
    Box(
        modifier = Modifier
            ...
            .clickable(
                interactionSource = MutableInteractionSource(),
                indication = rememberRipple(color = Color.DarkGray),
                onClick = { /* your code here */ }),
        contentAlignment = Alignment.Center
    ) {
        Text(...)
    }
}
0
Akexorcist On

I also encountered the same issue and found that I had to use this workaround instead, even though it looks bad because it renders the same text twice.

@Composable
fun RippleButton() {
    Box(contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                ...
                .clickable( /* ... */ ),
            contentAlignment = Alignment.Center
        ) {
            Text( /* Parameter is the same as the text below */ )
        }
        Text( /* Parameter is the same as the text above */ )
    }
}
0
galihif On

The concept is simple, you have to separate the clickable background and the content so the content is no longer a child of the background but a sibling in the same parent. You can use Box to put the content on the top of the clickable background. With this approach, the ripple wouldn't covered the content.

Before:

Box(){ // Your clickable goes here
    Text()
}

After:

Box(){ // a parent container, wrap content size
    Box(){} // Your clickable goes here
    Text()
}
0
Thracian On

You can use Button as workaround as well.

Box(
    contentAlignment = Alignment.Center
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            backgroundColor = Color.Yellow
        ),
        onClick = {}
    ) {
        Text("Button Text", color = Color.Transparent)
    }
    Text(
        "Button Text", color = contentColorFor(Color.Yellow)
    )
}