There are times when Jetpack Compose does not allow necessary recomposition right away. In this case, let’s find out how to force immediate recomposition manually.
Contents
What I wanted was for when I press a button, another button would appear somewhere else. I thought the logic was correct, but the button didn’t appear. I turned the screen sideways to see if this would solve the problem. Then the screen was redrawn and the result I wanted finally appeared. In other words, the problem had occurred because the state value had been passed correctly but it had not been immediately recomposed.
Find a way to force recompositon manually.
I tried modifying my code here and there to somehow get it to be recomposed manually, but without success. I had no choice but to search. I tried several of the methods they suggested, but none of them suited my situation. Then I found the following suggestion.
If you ever need to force a recompose then create an empty LaunchedEffect with a MutableState value key and toggle the mutable state. https://stackoverflow.com/questions/66950978/how-to-force-jetpack-compose-to-recompose
At first I didn’t know what he meant and I didn’t understand his code. But when all other methods had failed, I felt like this was the only place I could turn to.
As I reflected on his words, I realized what he meant. I still didn’t fully understand his code, but then I thought I could do it my way.
Recompose immediately
Saving state value
I first followed his instructions and created a place to store the necessary state values:
data class MyUiState( .. .. val recomposeToggleState: MutableState<Boolean> = mutableStateOf(false), )
recomposeImmediately()
Then, I created a member function of ViewModel to toggle the state value:
class MyViewModel(...) : ViewModel() { var myUiState by mutableStateOf(MyUiState()) private set .. .. fun recomposeImmediately() { myUiState.recomposeToggleState.value = !myUiState.recomposeToggleState.value } }
LaunchedEffect()
I placed the LaunchedEffect() function, which will receive the state value, at an appropriate location in the composable function and passed the state value as an argument:
LaunchedEffect(myUiState.recomposeToggleState.value) {}
As you can see, this function only takes the state value and does nothing in particular. Its body is empty. Even within a composable function, there are places where you cannot insert this, and Android Studio will let you know. For example, it cannot be placed inside a LazyColumn block.
Now, just put the function to toggle the state value in the appropriate place. In my case, the onClick lambda of the Button() function is suitable.
Button( modifier = Modifier, onClick = { // put some code here viewModel.recomposeImmediately() }) { Text( text = "Recompose", fontSize = 16.sp ) }
Now, whenever I click the button, the state value changes automatically. I actually tried pressing the button. It worked well. It was immediately recomposed to my liking.
Appearance of the completed composable function
Putting the above two code blocks together, the composable function looks roughly like this:
@Composable fun MyExampleComposableFunction( viewModel: MyViewModel = viewModel(), .. ) { var myUiState = viewModel.myUiState .. Button( modifier = Modifier, onClick = { // put some code here viewModel.recomposeImmediately() }) { Text( text = "Recompose", fontSize = 16.sp ) } .. LaunchedEffect(myUiState.recomposeToggleState.value) {} .. }