Home > OS >  Jetpack Compose: Room returns null for list of items
Jetpack Compose: Room returns null for list of items

Time:04-21

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.

CodePudding user response:

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 {
    ...
}

CodePudding user response:

I think that you are not listening long enough to the LiveData you get from Room, so you only get the initial value, which is always null.

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)
}

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

  • Related