Search code examples
androidandroid-roomkotlin-flowkotlin-stateflow

Unlike Livedata, using Flow in Room query doesn't trigger a refresh when updating a table entry but works if I delete or insert an entry


When using Livedata as a return type for a select* query on a table in Room, then I observe on it, I get triggers if I update/insert/delete an entry in that table. However, when I tried using Kotlin Flow, I only get 2 triggers.

The first trigger gives a null value as the initial value of the stateflow is a null. The second trigger is the list of entries in the Room table.

If I perform an insert/delete action on the DB, I receive a trigger from the StateFlow. However, If I update an entry, the Stateflow doesn't trigger.

N.B: The update operation works correctly on the DB. I checked using DB inspector.

Data class & DAO

@Entity
data class CartItem (
    @PrimaryKey
    val itemId: Int,
    var itemQuantity: Int=1
)

@Dao
interface CartDao {

    @Query("SELECT * FROM CartItem")
    fun getAllItems(): Flow<List<CartItem>>

    @Update
    suspend fun changeQuantityInCart(cartItem:CartItem)

    @Insert
    suspend fun insert(item: CartItem)

    @Delete
    suspend fun delete(cartItem:CartItem)
}

ViewModel

val cartItems: StateFlow<List<CartItem>?> =
        repo.fetchCartItems().stateIn(viewModelScope, SharingStarted.Lazily, null)

Fragment

viewLifecycleOwner.lifecycleScope.launchWhenStarted {
            viewModel.cartItems.collect {
              Log.e("Update","Update")
 }

Solution

  • My pitfall was that I was updating the object like this:

     currentItem.itemQuantity = currentItem.itemQuantity + 1
     changeQuantity(currentItem)
    

    (currentItem is an object of class CartItem which is received initially from the getAllItems Flow in the DAO.)

    (changeQuantity fun calls the changeQuantityInCart fun in the DAO.


    This caused the reference of the CartItem object in the StateFlow to hold the updated value of the object with the new itemQuantity value before calling the update on the DB.

    After that, when calling the Update fun in the DAO, the DB entry is updated and the Flow value changes, but when putting it in the Stateflow no changes are detected. Thus, the stateflow doesn't trigger as it is how stateflows differ from livedata.

    In the case of livedata, it will trigger regardless if the new value is the same or not.

    Thus, to solve this bug do not change the value of the object in the stateFlow before calling a DB update operation like this:

    val updatedCartItem = cartItem.copy(itemQuantity = cartItem.itemQuantity + 1)
    changeQuantity(updatedCartItem)