In an app I'm working on, I'm having trouble passing a MutableState<Boolean>
from a parent composable to a child composable. The reason I want to use MutableState
is that this variable will be changed in the child composable.
Here’s a simplified example of what I'm trying to accomplish:
@OptIn(UnstableApi::class)
@Composable
fun ParentComposable(
screenHeight: Dp,
barHeight: Dp,
) {
var scrollEnabled by remember { mutableStateOf(true) }
Column(
modifier = Modifier
.padding(top = barHeight)
.height(screenHeight - barHeight)
.fillMaxSize()
) {
UserButtons(
scrollEnabled = scrollEnabled // the error occurs here
)
}
}
@Composable
fun UserButtons(
scrollEnabled: MutableState<Boolean>
) {
IconButton(
onClick = {
scrollEnabled.value = false
println("Scroll has been deactivated")
}) {}
}
The error message I get is:
Type mismatch: inferred type is Boolean but MutableState was expected
I'm guessing that the issue is when passing scrollEnabled
(which is the MutableState<Boolean>
) to the child composable UserButtons
; for some reason it's detected as a normal Boolean
even if it's declared as a MutableState<Boolean>
.
How can I make sure that the parameter is correctly detected as an MutableState
?
The reason I want to use
MutableState
is that this variable will be changed in the child composable.
You should not do that.
Jetpack Compose uses the unidirectional data flow pattern. That means that
That means that you should not pass a MutableState<T>
to a child Composable, because when the child Composable modifies data from the parent Composable, the data flows upwards. Instead, use a callback function to notify the parent Composable to update the value of the scrollEnabled
variable:
@Composable
fun UserButtons(
scrollEnabled: Boolean,
updateScrollEnabled: (Boolean) -> Unit
) {
IconButton(
onClick = {
updateScrollEnabled(false)
println("Scroll has been deactivated")
}
) {
//...
}
}
In your ParentComposable
, call the UserButtons
Composable like this:
UserButtons(
scrollEnabled = scrollEnabled, // data flowing downwards
updateScrollEnabled = { updatedValue ->
scrollEnabled = updatedValue // event flowing upwards
}
)
Side Note
The reason you were getting the type mismatch is because you were using the by
keyword, which transforms a MutableState<T>
into T
by calling the getValue
function of MutableState
.
So, doing
val scrollEnabled = remember { mutableStateOf(false) }
Text(text = "scrollEnabled: ${scrollEnabled.value})
is the same as
var scrollEnabled by remember { mutableStateOf(false) }
Text(text = "scrollEnabled: ${scrollEnabled})
only that it is shorter and more convenient to use.