androidkotlinandroid-jetpack-composekotlin-stateflowcompose-recomposition

Android collectAsStateWithLifecycle() not collecting inside composable


I am using compose LazyColumn with viewModel updating the list items by having inside my viewModel:

    data class ContactsListUiState(
       val contacts: MutableList<Contact>
    )
    @HiltViewModel
    class ContactsViewModel @Inject constructor(savedStateHandle: SavedStateHandle) : ViewModel() {
    private val _contactsListUiState = MutableStateFlow(ContactsListUiState(mutableListOf()))
    val contactsListUiState: StateFlow<ContactsListUiState> = _contactsListUiState.asStateFlow()

    private fun updateContactsList(newContacts: MutableList<Contact>) {
        _contactsListUiState.update{ currentState ->
            currentState.copy(
                contacts = newContacts
            )
        }
    }

such that my updateContactsList() function updates the stateFlow and I am supposed to collect the contacts list with collectAsStateWithLifecycle() inside my composable function inside MainActivity

@OptIn(ExperimentalLifecycleComposeApi::class, ExperimentalFoundationApi::class)
    @Composable
    fun ContactsListScreen(
        navController: NavController,
        modifier: Modifier = Modifier
    ) {
        Log.d("ViewModel", "ContactsListScreen recomposed")
        val uiState by contactsViewModel.contactsListUiState.collectAsStateWithLifecycle()
        Box(modifier = modifier) {
            val listState = rememberLazyListState()
            val scope = rememberCoroutineScope()
            Column {
                TextField(
                    value = searchText,
                    singleLine = true,
                    onValueChange = {
                        searchText = it
                        Log.d("MainActivity", "calling filter with $it")
                        contactsViewModel.filterContactsByString(it)
                    },
                    leadingIcon = if (searchText.isNotBlank()) searchLeadingIcon else null,
                    trailingIcon = if (searchText.isBlank()) searchTrailingIcon else null,
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(Color.White)
                )
                LazyColumn(
                    Modifier.fillMaxWidth(),
                    state = listState,
                    contentPadding = PaddingValues(bottom = 80.dp)
                ) {                      
                        items(uiState.contacts) { contact ->
                            ContactListItem(
                                navController,
                                contact = contact,
                                modifier = Modifier.fillMaxWidth()
                            )
                            Divider(thickness = 0.5.dp, color = colorResource(id = R.color.blue))
                    }
                }
                 ...

where contactsListViewModel is declared on top of the activity which I inject using hilt :

@AndroidEntryPoint
class MainActivity : ComponentActivity(), LoaderManager.LoaderCallbacks<Cursor> {
    private val contactsViewModel: ContactsViewModel by viewModels()

For some reason uiState.contacts is empty inside the composable function, but it does contain items when I logged it inside the viewModel function and therefore my list stays empty..

Any suggestions on what could have gone wrong?

Thanks


Solution

  • Please use SnapshotStatelist/(mutableStateListOf()) instead of an ordinary list (mutableList).

    private val _contactsListUiState = MutableStateFlow(ContactsListUiState(mutableStateListOf()))
    

    Also please check this post this one and this one