
One of the things I always like about Material UI design is its animations, they are simple, not distracting, and not outdated, given that I sometimes try to integrate more animations into my apps or websites to make them alive.
So, for today’s situation, I had a very specific use case:
When something needs to be changed in the Settings Screen. First, navigate to the Settings Page. Second, Scroll the page until the setting visible. Lastly, Make the setting noticeable to the user (highlight, Ripple Effect, etc — Developer choice) so he knows this setting needs to be changed.
Samsung Settings app is a real world example (when you search something):
My initial implementation on my app (just the ripple effect):
A recursive triggering compared to a thumb press example
As you can see in both the targeted setting tile gets that ripple effect, basically simulating a thumb press (without any actual interaction from user).
For simplification I will exclude Navigation and Scrolling implementation.
@Composable
fun MyScreen(modifier: Modifier = Modifier) {
// ...
// ... other variables
// ...
val configuration = LocalConfiguration.current
val scope = rememberCoroutineScope()
val interactionSource = remember { MutableInteractionSource() }
val indication = rememberRipple(
// if set false, the ripple effect will not be cropped to the button.
bounded = true,
// if you want to spread to stop right at the edges of your targeted composable,
// take the longest side divided by two, in this case the row width is the screen width.
radius = configuration.screenWidthDp.dp / 2,
)
// used later to change Dp unit to Pixels using .toPx()
val density = LocalDensity.current
LaunchedEffect(Unit) {
with(density) {
scope.launch {
// wait a little bit for user to focus on the screen
delay(800)
val centerX = configuration.screenWidthDp.dp / 2
val centerY = 64.dp / 2
// simulate a press for the targeted setting tile
val press = PressInteraction.Press(Offset(centerX.toPx(), centerY.toPx()))
interactionSource.emit(press)
// wait a little bit for the effect to animate
delay(400)
// release the effect
val release = PressInteraction.Release(press)
interactionSource.emit(release)
}
}
}
// ...
// ... The rest of the code in segment 2
// ...
Starting with piece of code, we initialize some variables, notably interactionSource and indication, these two are important later. Next we see is a LaunchedEffect(Unit), We are going highlight the composable we are targeting right after MyScreen finishes rendering, highly recommended to be run inside a coroutine, as you can see it’s pretty much straight-forward, emit a press, wait for it to animate, then release it. You can play more with the durations in the delay(xxx) to find what best suits your use case.
// ...
// ... The previous segment
// ...
Column(modifier = modifier.systemBarsPadding()) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.padding(horizontal = 32.dp, vertical = 8.dp)
) {
Text(text = "Select directory")
OutlinedButton(
contentPadding = PaddingValues(vertical = 15.dp, horizontal = 28.dp),
onClick = { /* Something to do.. */ }
) {
Text(text = "Select")
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.indication(interactionSource, indication) // <--- This line is soooo important :)
.padding(horizontal = 32.dp, vertical = 8.dp),
) {
Text(text = "Clear Cache")
OutlinedButton(
contentPadding = PaddingValues(vertical = 15.dp, horizontal = 28.dp),
onClick = { /* Something to do.. */ }
) {
Text(text = "Clear")
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.padding(horizontal = 32.dp, vertical = 8.dp)
) {
Text(text = "Another Example")
Text(text = "Do nothing")
}
}
}
The most important part in segment 2 is the line 28, that way when a press is emitted into the interactionSource, it goes into your targeted composable.
Here is a showcase this code app
Full Code of this showcase app can be found on Github here:
https://github.com/hahouari/triggered_ripple_effect
Happy coding day :) Oh! and if you have any questions go ahead and make a comment, I will be happy to help. Cheers!
Originally published on Medium.