bhaisun
bhaisun

Reputation: 11

Jetpack Compose: Room returns null for list of items

I am trying to get list of todos from database with livedata however, while debugging it always shows null for value. I have provided my files below.

My Dao:

@Query("SELECT * FROM todo_table WHERE IIF(:isCompleted IS NULL, 1, isCompleted = :isCompleted)")
fun getTodos(isCompleted: Boolean?): LiveData<List<Todo>>

My ViewModel:

private var _allTodoList = MutableLiveData<List<Todo>>()
var allTodoList: LiveData<List<Todo>> = _allTodoList

init {
    viewModelScope.launch(Dispatchers.IO) {
        val list = todoRepository.getTodos(null)
        _allTodoList.postValue(list.value)
    }
}

fun onFilterClick(todoType: Constants.TodoType) {
    when (todoType) {
        Constants.TodoType.ALL -> {
            viewModelScope.launch(Dispatchers.IO) {
                val list = todoRepository.getTodos(null)
                _allTodoList.postValue(list.value)
            }
        }

        Constants.TodoType.COMPLETED -> {
            viewModelScope.launch(Dispatchers.IO) {
                val list = todoRepository.getTodos(true)
                _allTodoList.postValue(list.value)
            }
        }

        Constants.TodoType.INCOMPLETE -> {
            viewModelScope.launch(Dispatchers.IO) {
                val list = todoRepository.getTodos(false)
                _allTodoList.postValue(list.value)
            }
        }
    }
}

My MainActivity:

val allTodoList = viewModel.allTodoList.observeAsState()


allTodoList.value?.run {//value is always null
    if (!isNullOrEmpty()) {
        ...
    } else {
        ...
    }
}

While debugging I found that allTodoList.value is always null however, when I manually run same query in app inspection I the get the desired results.

Upvotes: 1

Views: 907

Answers (2)

Philippe Banwarth
Philippe Banwarth

Reputation: 17755

You are not observing the LiveData you get from Room. YourDao.getTodos() and LiveData.getValue() are not suspend functions, so you get the current value, which is null because Room has not yet fetched the values from SQLite.

A possible solution would be to set the todo type as a live data itself and use a switchMap transformation in the ViewModel :

private val todoType = MutableLiveData<Constants.TodoType>(Constants.TodoType.ALL)

val allTodoList: LiveData<List<Todo>> = androidx.lifecycle.Transformations.switchMap(todoType) { newType ->

    val typeAsBoolean = when(newType) {
        Constants.TodoType.ALL -> null
        Constants.TodoType.COMPLETED -> true
        Constants.TodoType.INCOMPLETE -> false
        else -> throw IllegalArgumentException("Not a possible value")
    }

    // create the new wrapped LiveData
    // the transformation takes care of subscribing to it
    // (and unsubscribing to the old one)
    todoRepository.getTodos(typeAsBoolean)
}

fun onFilterClick(todoType: Constants.TodoType) {
    // triggers the transformation
    todoType.setValue(todoType)
}

This is in fact the exact use case demonstrated in the reference doc

Upvotes: 0

Mario Huizinga
Mario Huizinga

Reputation: 826

You can simplify your code, see if it works. ViewModel only needs this:

val allTodoList: LiveData<List<Todo>> = todoRepository.getTodos(null)

MainActivity:

val allTodoList by viewModel.allTodoList.observeAsState()
if (!allTodoList.isNullOrEmpty()) {
    ...
} else {
    ...
}

Upvotes: 1

Related Questions