Search code examples
androidkotlinkotlin-coroutinesandroid-viewmodel

How can I delay a loading spinner and start it conditionally inside of a coroutine?


I currently have a way that works, but it uses a boolean that is external to the coroutine, and it just feels like there must be a more elegant way to do it. I plan on using this pattern in many places in my app so I want it to be "right". Currently I solve the problem like this:

private loadingFinished: Boolean = false
private fun loadData() {
    showLoading.value = false
    loadingFinished = false
    viewModelScope.launch {
        delay(500)
        if (!loadingFinished)
            showLoading.value = true
    }
    viewModelScope.launch {
        try {
            data = api.thisIsASuspendFunction
            updateUI.call()
            loadingFinished = true
            showLoading.value = false
        }
        catch (e: Exception){
            loadingFinished = true
            showLoading.value = false
            showError.value = errorHelper.getErrorMessageType(e)
        }
    }
}

This works exactly as I expect it to right now: If the api call takes longer than half a second, the progress spinner shows and is stopped once the suspend function finishes. But is there a more correct way to accomplish this?


Solution

  • You can use job cancellation for it:

    private fun loadData() {
        showLoading.value = false
        viewModelScope.launch {
            val loadingJob = launch {
                delay(500)
                showLoading.value = true
            }
            try {
                data = api.thisIsASuspendFunction
                loadingJob.cancel()
                updateUI.call()
                showLoading.value = false
            }
            catch (e: Exception){
                loadingJob.cancel()
                showLoading.value = false
                showError.value = errorHelper.getErrorMessageType(e)
            }
        }
    }