0

Depending on the status of PagingData<ShoppingListItem>, I'm updating the loading status in the ViewModel with a sealed class. As I don't know how to check if PagingData<ShoppingListItem> is emtpy in the ViewModel, I'm just collecting the PagingData and calling the .map function on it to add it to a mutableList. I then update the loading state as Emtpy if the list is empty and display an empty message in the UI. The problem is all the code below the collect() method call does not execute, and the circular progress bar does not go away. Even a Log statement below it does not execute. How can I make this work?

Sealed Class

sealed class ListItemsState {
    object Loading : ListItemsState()
    object Empty : ListItemsState()
    object Error : ListItemsState()
    data class Success(val allItems: Flow<PagingData<ShoppingListItem>>?) : ListItemsState()
}

ViewModel

@HiltViewModel
class ShoppingListScreenViewModel @Inject constructor(
    private val getAllShoppingListItemsUseCase: GetAllShoppingListItemsUseCase
) {
   private val _shoppingListItemsState = mutableStateOf<Flow<PagingData<ShoppingListItem>>?>(null)

   private val _listItemsLoadingState = MutableStateFlow<ListItemsState>(ListItemsState.Loading)
   val listItemsLoadingState = _listItemsLoadingState.asStateFlow()

   private val tempList = mutableListOf<ShoppingListItem>()

   init {
        viewModelScope.launch {
            getAllShoppingListItemsFromDb()
        }
    }

   private suspend fun getAllShoppingListItemsFromDb() {
        _shoppingListItemsState.value = getAllShoppingListItemsUseCase().distinctUntilChanged()

        _shoppingListItemsState.value?.collect {
            it.map { shoppingListItem ->
                tempList.add(shoppingListItem)
            }
        } //everything below this line does not execute

        Log.i("@@sealed", tempList.isEmpty().toString()) 

        when {
            tempList.isEmpty() -> _listItemsLoadingState.value = ListItemsState.Empty
            else -> _listItemsLoadingState.value =
                ListItemsState.Success(_shoppingListItemsState.value)
        }
    }
}

ShoppingListScreen Composable

@Composable
fun ShoppingListScreen(
    navController: NavHostController,
    shoppingListScreenViewModel: ShoppingListScreenViewModel,
    sharedViewModel: SharedViewModel
) {
    val screenHeight = LocalConfiguration.current.screenHeightDp.dp
    val allItemsState = shoppingListScreenViewModel.listItemsLoadingState.collectAsState().value

    Scaffold(
        topBar = {
            CustomAppBar(
                title = "Shopping List",
                titleFontSize = 20.sp,
                appBarElevation = 4.dp,
                navController = navController
            )
        },
        floatingActionButton = {
            FloatingActionButton(
                onClick = {
                    shoppingListScreenViewModel.setStateValue(SHOW_ADD_ITEM_DIALOG_STR, true)
                },
                backgroundColor = Color.Blue,
                contentColor = Color.White
            ) {
                Icon(Icons.Filled.Add, "")
            }
        },
        backgroundColor = Color.White,
        // Defaults to false
        isFloatingActionButtonDocked = false,
        bottomBar = { BottomNavigationBar(navController = navController) }
    ) {
        Box {
            when (allItemsState) {
                is ListItemsState.Loading -> ConditionalCircularProgressBar(isDisplayed = true)
                is ListItemState.Empty -> Text("Empty!")
                is ListItemsState.Error -> Text("Error!")
                is ListItemsState.Success -> {
                    ConditionalCircularProgressBar(isDisplayed = false)

                    val successItems = allItemsState.allItems?.collectAsLazyPagingItems()

                    LazyColumn(
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(screenHeight)
                    ) {
                        items(
                            items = successItems!!,
                            key = { item ->
                                item.id
                            }
                        ) { item ->
                            ShoppingListScreenItem(
                                navController = navController,
                                item = item,
                                sharedViewModel = sharedViewModel
                            ) { isChecked ->
                                scope.launch {
                                    shoppingListScreenViewModel.changeItemChecked(item!!, isChecked)
                                }
                            }
                        }

                        item { Spacer(modifier = Modifier.padding(screenHeight - (screenHeight - 70.dp))) }
                    }
                }
                else -> {}
            }
        }
    }
}
Raj Narayanan
  • 2,443
  • 4
  • 24
  • 43
  • `collect()` is a `suspending` function. – Mark Nov 18 '22 at 15:24
  • 2
    You need to `launch()` that in a child coroutine. Within a given coroutine, `collect()` is a blocking call and will only return if the `Flow` you are observing is closed. IMHO and FWIW, having a viewmodel expose a `suspend fun` is a code smell. – CommonsWare Nov 18 '22 at 15:24
  • @CommonsWare agree with what you are saying, but if launched in a child coroutine the code following collect is likely to fail as it is predicated on an expected result of the collect lambda, all code should probably be in the collect lambda .. – Mark Nov 18 '22 at 15:28
  • I placed all the code that's below the `collect()` method call into the `collect()` lambda itself, and also inside a child `viewModelScope.launch()` coroutine. But, Logcat is reporting that `tempList` is empty even though there are items in the database. How can I fix that? – Raj Narayanan Nov 18 '22 at 18:36
  • collect will never return if the Flow never terminates which is probably what's happening here. – Ajmal Kunnummal Dec 03 '22 at 13:56
  • @AjmalKunnummal how do you make it terminate when the data has been retrieved from the database? – Raj Narayanan Dec 03 '22 at 17:10

0 Answers0