0

I have a LazyColumn that displays a list of shopping list items retrieved from the database in the ViewModel. If the retrieved list of items is empty, the LazyColumn shows the following message: "You don't have any items in this shopping list." The problem is that this message displays briefly for 1 second before the items are displayed. To solve the problem I implemented a circular progress bar while the items are being retrieved, but it does not even appear, and the message is still displayed. How can I fix this?

ViewModel

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

   val shoppingListItemsState: State<Flow<PagingData<ShoppingListItem>>?> get() = _shoppingListItemsState

   val loading = mutableStateOf(false)

   init {
      loading.value = true
      getAllShoppingListItemsFromDb()
   }

   private fun getAllShoppingListItemsFromDb() {
        viewModelScope.launch {
            _shoppingListItemsState.value = getAllShoppingListItemsUseCase().distinctUntilChanged()
            loading.value = false
        }
    }
}

ShoppingListScreen Composable

fun ShoppingListScreen(
    navController: NavHostController,
    shoppingListScreenViewModel: ShoppingListScreenViewModel,
    sharedViewModel: SharedViewModel
) {
    val scope = rememberCoroutineScope()
    val focusManager = LocalFocusManager.current
    val screenHeight = LocalConfiguration.current.screenHeightDp.dp
    val allItems = shoppingListScreenViewModel.shoppingListItemsState.value?.collectAsLazyPagingItems()
    val showProgressBar = shoppingListScreenViewModel.loading.value

    Scaffold(
        topBar = {
            CustomAppBar(
                title = "Shopping List Screen",
                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 {
                LazyColumn(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(screenHeight)
                ) {
                    item {
                        if (allItems != null && allItems.itemCount == 0) {
                            Text("You don't have any items in this shopping list.")
                        }
                    }

                    items(
                        items = allItems!!,
                        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))) }
                }

            ConditionalCircularProgressBar(isDisplayed = showProgressBar)
        }
    }
}
Raj Narayanan
  • 2,443
  • 4
  • 24
  • 43

1 Answers1

0

Quick solution:

val showProgressBar = shoppingListScreenViewModel.loading.collectAsState() //Use collectAsState, with Flow state in the ViewModel

and add this condition to the "You don't have any items..." if like so:

if (allItems != null && allItems.itemCount == 0 && !showProgressBar) { ... }

The better solution would be to implement this with sealed class(es), where you would return different class for different state (e.g. Loading, Error, Empty, Data). And on the UI side, you just need to when over the possible types of data. Here you can find a perfect example.

Raj Narayanan
  • 2,443
  • 4
  • 24
  • 43
Simon
  • 1,657
  • 11
  • 16
  • The sealed class approach worked. Kudos for the link. Thanks. – Raj Narayanan Nov 15 '22 at 00:29
  • How would I assign the `Empty` state in the `ViewModel`? How can I check if the `PagingData` is empty? I can't find an answer on SO or Google that works. – Raj Narayanan Nov 15 '22 at 01:43
  • If you get zero entries from getAllShoppingListItemsUseCase(), then you set _shoppingListItemsState.value to .Empty. – Simon Nov 15 '22 at 06:29
  • I understand you set the state to `.Empty`, but how do I actually check to see if the `PagingData` is empty? I'm not sure how I would do it in the `ViewModel`. – Raj Narayanan Nov 15 '22 at 10:26