Home > Back-end >  How to refresh data from Room when using Jetpack Compose?
How to refresh data from Room when using Jetpack Compose?

Time:09-28

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