Home > OS >  Composable visibility not changing on State change
Composable visibility not changing on State change

Time:11-07

I have a isLoading state and I'm trying to show a CircularProgressIndicator when the value is true.

@Composable
fun ProductDetailScreen(
    viewModel: ProductDetailViewModel = hiltViewModel()
) {
    val productState = viewModel.productState.value
    LazyColumn{
        item {
            if (productState.isLoading)
                CircularProgressIndicator()
        }
    }
}

I'm using a Resource class for my API call results and in the repository I use this class to wrap my request result.
The problem is, although I'm returning Resource.Loading from the repository, the isLoading state is not being updated from ViewModel and the ProgressIndicator is not shown in my screen. What could be causing this behavior?

sealed class Resource<T>(
    val data: T? = null,
    val message: String? = null,
    val errorType: ExceptionMapper.Type? = null
) {
    class Success<T>(data: T?) : Resource<T>(data)
    class Error<T>(message: String, errorType: ExceptionMapper.Type, data: T? = null) : Resource<T>(data, message, errorType)
    class Loading<T>(isLoading: Boolean = true) : Resource<T>()
}  

Repository:

override suspend fun getProductComments(productId: Int): Resource<List<Comment>> {
        return try {
            Resource.Loading<List<Comment>>()
            delay(3000)
            Resource.Success(apiService.getComments(productId))
        } catch (t: Throwable) {
            val mappedException = ExceptionMapper.map(t)
            Resource.Error(message = t.message!!, errorType = mappedException.type)
        }
    }    

ViewModel:

@HiltViewModel
class ProductDetailViewModel @Inject constructor(
    state: SavedStateHandle,
    private val productRepository: ProductRepository
) : ViewModel() {

    private val passedProduct = state.get<Product>(EXTRA_KEY_DATA)
    var productId = passedProduct?.id

    var productState = mutableStateOf(ProductState())
        private set

    init {
        getProductComments()
    }
    private fun getProductComments() {
            viewModelScope.launch {
                productId?.let { pId ->
                    when (val commentResult = productRepository.getProductComments(pId)) {
                        is Resource.Success -> {
                            commentResult.data?.let { comments ->
                                productState.value =
                                    productState.value.copy(
                                        comments = comments,
                                        error = null,
                                        isLoading = false
                                    )
                            }
                        }
                        is Resource.Error -> {
                            productState.value = productState.value.copy(
                                isLoadFailed = true,
                                isLoading = false,
                                error = commentResult.message
                            )
                        }
                        is Resource.Loading -> {
                            productState.value = productState.value.copy(
                                isLoadFailed = false,
                                isLoading = true,
                                error = null
                            )
                        }
                    }
                }
            }
        }
}

CodePudding user response:

Your'e only checking this

is Resource.Loading -> {
        ...
}

when the repository returns, at this point its useless because when the call to getProductComments is done, it's already Resource.Success.

 return try {
      Resource.Loading<List<Comment>>() // you'll never get this value
      delay(3000)
      Resource.Success(apiService.getComments(productId))

So I'd suggest to update the ProductState before you call the repository

private fun getProductComments() {

      productState.value = productState.value.copy(isLoading = true)

      viewModelScope.launch {
      ...
      ...

or set isLoading to true as its initial state.

data class ProductState(
     ...
     ...
     val isLoading : Boolean = true
     ...
)
  • Related