I have a few items stored in Room, and I want to add a new one. Here is what I have tried:
interface ItemDao {
@Query("SELECT * FROM item_table")
fun getItems(): List<Intem>
@Insert(onConflict = IGNORE)
fun addItem(item: Item)
}
My repo is very simple:
interface ItemRepository {
fun getItemsFromRoom(): List<Item>
fun addItemToRoom(item: Item)
}
And inside the View Model class I do:
class ItemsViewModel @Inject constructor(
private val repo: ItemRepository
) : ViewModel() {
var items by mutableStateOf(emptyList<Item>())
fun getItems() = viewModelScope.launch {
items = repo.getItemsFromRoom()
}
fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
repo.addItemToRoom(ietm)
}
}
And inside the composable I do:
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(
items = viewModel.items
) { item ->
Text(item.name)
}
}
When I open the app, I get all the data from Room correctly. When I want to add another item, I press a button, a dialog is displayed, I write the data and I hit add. The problem comes when the dialog is closed, I cannot see the new data, unless I close and reopen the app. Why isn't the state changed when I add a new item in Room? How to solve this?
CodePudding user response:
Your query returns List
of items. To refresh UI list you need to call getItemsFromRoom
function again. It's not updated automatically because it's only simple List
.
I would suggest to return Flow<List<Item>>
instead of List<Item>
and collect from that flow to update items automatically then database changes
Where's official android codelab, where you can follow step by step to implement this.
CodePudding user response:
You should emit a reactive data stream (like LiveData
or Flow
) from your Dao if you want to get data updates. It's better to use a Flow
here.
interface ItemDao {
@Query("SELECT * FROM item_table")
fun getItems(): Flow<List<Intem>>
@Insert(onConflict = IGNORE)
fun addItem(item: Item)
}
interface ItemRepository {
fun getItemsFromRoom(): Flow<List<Item>>
fun addItemToRoom(item: Item)
}
And in your ViewModel, you don't need a new getItems
function to be called from the UI. Instead you can directly do this:
class ItemsViewModel @Inject constructor(
private val repo: ItemRepository
) : ViewModel() {
val items = repo.getItemsFromRoom()
fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
repo.addItemToRoom(ietm)
}
}
Finally, in the UI, you can collect this Flow like this:
val items by viewModel.items.collectAsState(initialValue = emptyList())
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(items = items) { item ->
Text(item.name)
}
}