Compose collectAsState from a StateFlow not recomposing the screen

190 Views Asked by At

I have a StateFlow screen state in the ViewModel which I'm trying to propagate to a Composable screen through a Fragment. In some cases, the screen is not being recomposed even when the state changes. I know for sure that the value of the screen state is changing because I'm able to collect it in the fragment using viewModel.screenState.collectLatest.

ViewModel.kt

class MyViewModel: ViewModel() {
    private val someData1 = MutableStateFlow("initialData1")
    private val someData2 = MutableStateFlow("initialData2")
    val screenState: StateFlow<ScreenState> = combine(someData1, someData2) { data1, data2 ->
        // ScreenState is a data class
        ScreenState(data1, data2)
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = ScreenState("initialData1", "initialData2"),
    )
}

MyFragment.kt

class MyFragment: Fragment() {
    private val viewModel: MyViewModel by viewModels()
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View = ComposeView(inflater.context).apply {
        lifecycleScope.launch {
            viewModel.screenState.collectLatest {
                Log.d(TAG, "new state is always collected here successfully")
            }
        }
        setContent {
            val screenState by viewModel.screenState.collectAsState()
            // SOMETIMES NOT RECOMPOSING!!!
            MyScreen(
                screenState = screenState,
            )
        }
    }
}

MyScreen.kt

@Composable
fun MyScreen(
    screenState: ScreenState,
) {
    Log.d(TAG, "screen not recomposing on state change") // this line not called!
}

Replacing collectAsState with collectAsStateWithLifecycle didn't help.

Using androidx.compose:compose-bom:2023.06.01.

1

There are 1 best solutions below

1
Nirav Rangapariya On

Here, You are using fragment that will not recompose every time, for that you have to use @Composable (it will recompose every time it notices any changes in data)

I think use of fragments in compose is bad habit. You should use compose navigation graph with different composable screens.

you can refer this link for compose navigation https://medium.com/google-developer-experts/navigating-in-jetpack-compose-78c78d365c6a