Jetpack Compose — Bounce Click Animation

Why would you need a bouncing click effect?
A bouncing click effect is a way to add visual feedback to your UI when a user clicks on a button or other interactive element. The bouncing effect can help to make the click feel more satisfying and engaging, and it can also help to draw attention to the element.
There are a few reasons why you might want to use a bouncing click effect in your app:
- To improve the user experience: A bouncing click effect can make your app feel more responsive and engaging. It can also help to make sure that users are aware that they have successfully clicked on an element.
- To draw attention to important elements: A bouncing click effect can help to draw attention to important elements in your UI, such as buttons or call-to-action buttons. This can help to improve the usability of your app and make it easier for users to find what they are looking for.
- To add a sense of fun and personality: A bouncing click effect can add a sense of fun and personality to your app. This can be especially helpful if your app is targeting a younger audience.
How can you actually make a bouncing click effect?
fun Modifier.bounceClickable(
minScale: Float = 0.5f,
onAnimationFinished: (() -> Unit)? = null,
onClick: (() -> Unit)? = null,
) = composed {
var isPressed by remember { mutableStateOf(false) }
val scale by animateFloatAsState(
targetValue = if (isPressed) minScale else 1f,
label = ""
) {
if(isPressed) {
isPressed = false
onAnimationFinished?.invoke()
}
}
this
.graphicsLayer {
scaleX = scale
scaleY = scale
}
.clickable {
isPressed = true
onClick?.invoke()
}
}The bounceClickable modifier is a reusable modifier that adds a bouncing click effect to a composable. It works by animating the scale of the composable when it is pressed. The scale starts at 1f and then decreases to the minScale value when the composable is pressed. When the composable is released, the scale bounces back to 1f.
The modifier takes three parameters:
minScale: The minimum scale of the composable when it is pressed.onAnimationFinished: A callback function that is invoked when the animation finishes.onClick: A callback function that is invoked when the composable is clicked.
The modifier works by first creating a var variable called isPressed. This variable is used to track whether the composable is currently pressed. The modifier then uses the remember function to create a mutableStateOf object that stores the value of the isPressed variable. This means that the state of the isPressed variable will be retained across recompositions.
The next part of the modifier creates a val variable called scale. This variable is used to store the current scale of the composable. The modifier uses the animateFloatAsState function to animate the value of the scale variable. The animateFloatAsState function takes two parameters:
targetValue: The target value of the animation.label: A label for the animation.
In this case, the targetValue parameter is set to the minScale value when the composable is pressed and 1f when the composable is not pressed.
The animateFloatAsState function also takes a lambda expression as its last parameter. This lambda expression is called when the animation finishes. In this case, the lambda expression calls the isPressed variable to set it to false and the onAnimationFinished callback function, if it is not null.
Finally, the modifier uses the graphicsLayer modifier to set the scale of the composable to the value of the scale variable. The modifier also uses the clickable modifier to listen for clicks on the composable. When the composable is clicked, the isPressed variable is set to true and the onClick callback function, if it is not null, is called.
Cool right? But how can we actually use this? Well it is actually quite easy. It can be used instead of the standard clickable modifier.
var isFavorite by remember {
mutableStateOf(false)
}
Icon(
imageVector = if(isFavorite) {
Icons.Filled.Favorite
} else {
Icons.Filled.FavoriteBorder
},
contentDescription = null,
modifier = Modifier
.size(100.dp)
.clip(CircleShape)
.bounceClickable(
minScale = 0.5f,
onAnimationFinished = {
// Used to change the icon
isFavorite = !isFavorite
}
) {
// Perform action e.g.: API call
}
)Here we simply create an Icon on which we apply the bounceClickable modifier and every time we click it, when the press animation finished we change the icon to display to the end user that in this case the “favorite” was successful.
A little side note to leave you with is that this animation won’t work on composables that already have an onClick just like clickable won’t work on those. Examples of these composables are things like Buttons or Cards. That being said, using the bounceClickable you should be able to create the same effect for these kind of composables by simply relying on the onClick of that composable instead of the clickable modifier to apply the bounce effect.
I hope this explanation is helpful. Please let me know if you have any other questions.




