androidkotlinandroid-jetpack-compose

Correctly updating a mutablestate uistate to set current selected item in a viewmodel


How to update a mutablestate uistate to set current selected item in a viewmodel? I have this uistate class, and need to change selectedBookId value.

sealed interface BooksGridUiState {
    data class Success(
        val booksList: List<BookData>,
        val selectedBookId: String?
    ) : BooksGridUiState
    object Error : BooksGridUiState
    object Loading : BooksGridUiState
}

This is the variable in the viewmodel:

var uiState: BooksGridUiState by mutableStateOf(BooksGridUiState.Loading)
        private set

Can't do the trick of calling

.update {
    it.copy(

Because this is not a mutablestateflow, and I'm following a codelab and must use mutablestate (editado)

is it correct to do it this way?

fun selectBook(id: String) {
    (uiState as BooksGridUiState.Success).let {
        uiState = BooksGridUiState.Success(it.booksList, id)
    }
}

maybe is better to do it this way?

fun selectBook(id: String) {
    (uiState as BooksGridUiState.Success).let {
        uiState = it.copy(selectedBookId = id)
    }
}

Solution

  • Use this:

    uiState = when (val state = uiState) {
        is BooksGridUiState.Success -> state.copy(
            selectedBookId = id,
        )
        is BooksGridUiState.Error -> TODO()
        is BooksGridUiState.Loading -> TODO()
    }
    

    You said you are doing this because you follow a Codelab. This approach is ok if it is just used for academic purposes, but in any real-world application you shouldn't use a Compose State in your view model, you should use a MutableStateFlow instead.

    Regarding your update to the question: If you just want to ignore the cases where the current state is Error or Loading then I would suggest a variation of your second proposed solution:

    fun selectBook(id: String) {
        (uiState as? BooksGridUiState.Success)?.let {
            uiState = it.copy(selectedBookId = id)
        }
    }
    

    I used as? with a ? which won't fail with a runtime exception if uiState isn't castable to Success (which is the case for Error and Loading). Instead it will return null so that the following ?.let is skipped entirely.