// Define a Composable function
@Composable
fun MyComposableFunction() {
// Call other @Composable functions here
MyOtherComposableFunction()
}
// Another Composable function
@Composable
fun MyOtherComposableFunction() {
// Composable UI elements go here
Text("Hello, Jetpack Compose!")
}
In the above code, `MyComposableFunction` is a `@Composable` function and it calls `MyOtherComposableFunction`, which is also a `@Composable` function. This is the correct way to structure your code in Jetpack Compose.
Make sure that all your `@Composable` functions are called from within other `@Composable` functions.
I will come with a little explanation. Composable functions are run in parallel, which means that Compose takes care of an internal thread pool to perform fast and optimized composition.
However, onClick takes place on the UI thread, which is out of the composition context and is not controlled by the thread pool. That's the reason why you cannot call a composable function from onClick.
To perform such an operation, you indeed need to create a mutable state which will be controlled by the button.
var isTimerVisible by remember { mutableStateOf(false) } if (isTimerVisible) { TimerView() } OutlinedButton( onClick = { isTimerVisible = !isTimerVisible }, shape = CircleShape, border = BorderStroke(1.dp, Color(0xFFADFF2F)), colors = ButtonDefaults.buttonColors( backgroundColor = Color(0xFF1C536F), contentColor = Color.White ) ) { Text(text = "OK", fontSize = 20.sp) }
UPD: With the help from z.g.y, I understood that I was not quite correct about it.
@Compose annotation works similarly to the suspend keyword in Kotlin; it changes the function's type. Composable function type, just like suspend, is not compatible with regular function type.
The compiler treats that type of function differently; it inserts the $composer parameter to it and calls its start method with a generated integer key.
fun TimerView($composer: Composer) { $composer.start(123) }
This composer object is passed to a composable from a parent composable, but since onClick is not composable and happens outside of the composition context, there is no valid composer in it. So you cannot call a composable without the composer.
onClick is not marked @Composable, so you get this warning. You can only change the state with onClick.
For example, you can create a flag and display the UI depending on that flag:
var signUp by remember { mutableStateOf(false) } if (signUp) { SignUpNewUser(email, fullName, country, password) } else { Box(contentAlignment = Alignment.Center) { Button(onClick = { signUp = true }, modifier = Modifier .width(200.dp) .background(color = Color.DarkGray)) { Text(text = "Sign Up", style = TextStyle(color = Color.White, fontWeight = FontWeight.Bold)) } } }
Recently, I was trying to show a toast message when clicking on a toolbar action in my application, but I got this error. Then I solved it myself. If anyone facing the same can have a look at my solution:
@Composable
fun Toolbar() {
TopAppBar(title = { Text(text = "Jetpack Compose") }, navigationIcon = {
IconButton(onClick = {}) {
Icon(Icons.Filled.Menu)
}
}, actions = {
IconButton(onClick = {
showMessage(message = "test")
}) {
Icon(vectorResource(id = R.drawable.ic_baseline_save_24))
}
})
}
@Preview
@Composable
fun ToolbarPreview(){
Toolbar()
}
@Composable
fun showMessage(message:String){
Toast.makeText(ContextAmbient.current, message, Toast.LENGTH_SHORT).show()
}
Solution:
The onClick parameter doesn't accept a composable function. Remove the @Composable annotation in the showMessage. Use something like:
@Composable
fun Toolbar() {
val context = LocalContext.current
TopAppBar(title = {}, actions = {
IconButton(onClick = {
showMessage(context, message = "test")
}) {}
})
}
fun showMessage(context: Context, message:String){
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}