Hello, I'm curios how to correctly use buttons so ...
# compose
r
Hello, I'm curios how to correctly use buttons so that they ripple animation plays out properly: Let's say I use a navigation icon to navigate to the profile screen of my application, once I tap on it the ripple effect starts and the navigation host changes the screen to the profile composable also the profile icon will now be replaced with the arrow back icon, I'm guessing because of re-composition the ripple animation never plays out and there is a very hard visual swap between icons and screens, this is also visible if you would have a lazy list with items and tapping on one item would remove it from the list, the tap ripple animation would never play out. My workaround was to just wrap all on click actions in an animateAfter() function that delays the actual on click by a few milliseconds to give the ripple animation time to play out. But it doesn't quite feel right doing this. My question is is there a correct way to do this? is this a bug? Should the on click action of the composable happen automatically at the end of the animation or somehow trigger across re-compositions? Thanks
n
best way would be to provide a sample code of the issue, with the minimum code to reproduce (for example without any nav part)
it looks like a use case for https://developer.android.com/jetpack/compose/side-effects#rememberupdatedstate but it's probably on the button part ? (and I think it might already be there for ripple)
r
Copy code
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeApplicationTheme {
                val scope = rememberCoroutineScope()
                var route by remember { mutableStateOf("home") }
                Scaffold(topBar = {
                    TopAppBar(title = { Text("Simple TopAppBar") },navigationIcon = {
                        if(route == "home") {
                            IconButton(onClick = {
                                route = "profile"
//                                scope.launch { afterAnimation { route = "profile" } }
                            }) {
                                Icon(Icons.Filled.Person, contentDescription = null)
                            }
                        } else {
                            IconButton(onClick = {
                                route = "home"
//                                scope.launch { afterAnimation { route = "home" } }
                            }) {
                                Icon(Icons.Filled.ArrowBack, contentDescription = null)
                            }
                        }
                    })
                }) {
                    Text("Content")
                }
            }
        }
    }
}

suspend fun afterAnimation(run: () -> Unit) {
    delay(150).also { run() }
}
a simple way to reproduce try running the code once with just route = "something" part and then swap with the commented scope.launch part in the first case the ripple wont play for the icon button and the re-composition makes for a hard visual transition while the second one looks better, it feels like a weird way to handle a click just to get the default animation to finish
another way to make it work would be to just change the content and behavior of the icon button this will trigger a re-compose inside the button only and the ripple would work
Copy code
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeApplicationTheme {
                var route by remember { mutableStateOf("home") }
                Scaffold(topBar = {
                    TopAppBar(title = { Text("Simple TopAppBar") },navigationIcon = {
                        IconButton(onClick = {
                            route = if(route == "home") {
                                "profile"
                            } else {
                                "home"
                            }
                        }) {
                            if(route == "home") {
                                Icon(Icons.Filled.Person, contentDescription = null)
                            } else {
                                Icon(Icons.Filled.ArrowBack, contentDescription = null)
                            }
                        }
                    })
                }) {
                    Text("Content")
                }
            }
        }
    }
}
but this feels clunky to use since intuitively you would have premade composable with buttons that you would swap out not a master button with different behavior (eg a home button a profile button and so on; not a home/profile/etc button)