A solution you could look at is passing callbacks into your functions where you are able to reassign the variables. (since by remember
constructors are just that).
Let's say you have a function doStuffWithStringState()
, then you could just do something like this:
fun doStuff(onComplete: () -> Unit) {
// do stuff here
onComplete()
}
and when calling the function in the composable:
@Composable
fun MyComponent() {
var stringState by remember { mutableStateOf("foo") }
var booleanState by remember { mutableStateOf(false) }
var integerState by remember { mutableStateOf(0) }
LaunchedEffect(Unit){
doStuff { stringState = "new state" }
}
}
}
If you have a more complex scenario, you could pass the current state as a parameter into the higher order function and then implement a switch in the callback:
enum class CustomState {State1, State2}
fun doStuff(onComplete: (CustomState) -> Unit) {
// do stuff here
val resultingState = determineState()
onComplete(resultingState)
}
doStuff {
when (it) {
State1 -> { stringState = "state1" }
}
}
But i would not recommend this approach, it looks a bit "unclean" in the compose philosophy. Your scenario very much sounds like it might need a refactor into a flow or livedata which is collected into a StateFlow
inside the viewmodel and collected as state in the composable, just as you discussed in the other answer. State is mostly meant to be changed either from a data source (i.e. observing a data source) or from user input, i.e. a composable.
Which by the way, composables which modify state are intended to be used exactly this way. For example a TextField
has the function parameter onValueChange: (String) -> Unit
where you could set the variable from remember
just like I described above.