I’m developing an app using Jetpack Compose, Room, Flow, Kotlin, and following the MVVM architecture. My app includes the following screens:
HomeScreen: Displays the budget and a list of products.
NewItemScreen: Allows adding new products.
ItemDetailsScreen: Shows details of a selected product.
EditItemScreen: Allows editing an existing product.
The app uses a ViewModel to manage UI-related data, and I’m facing an issue with the EditItemScreen.
Problem Description:
When I try to update an item’s details in the EditItemScreen, the values seem to enter a loop, alternating between the new and old values. Sometimes it does not update at all or needs some time between updates.
Code:
Here is the relevant code:
EditItemViewModel
class EditItemViewModel(
savedStateHandle: SavedStateHandle,
private val itemsRepository: ItemsRepository
): ViewModel() {
companion object {
private const val TAG = "Edit Item"
}
var editItemUiState by mutableStateOf(ItemUiState())
private set
private val itemId: Int = checkNotNull(savedStateHandle[EditItemDestination.itemIdArg])
init {
viewModelScope.launch {
val item = itemsRepository.getItemStream(itemId)
.filterNotNull()
.first()
editItemUiState = item.toItemUiState(isEntryValid = true)
}
}
fun updateEditUiState(itemDetails: ItemDetails) {
editItemUiState =
editItemUiState.copy(
itemDetails = itemDetails,
isEntryValid = validateInput(itemDetails)
)
}
fun updateEditTag(tag: String) {
editItemUiState =
editItemUiState.copy(tag = tag)
}
fun updateItem() {
val item = editItemUiState.itemDetails.toItem()
if (validateInput()) {
viewModelScope.launch {
try {
itemsRepository.updateItem(item)
// Añadir un pequeño retraso para asegurar que la actualización se complete
delay(100)
// Recargar el item después de la actualización
val updatedItem = itemsRepository.getItemStream(item.id).filterNotNull().first()
editItemUiState = updatedItem.toItemUiState(isEntryValid = true)
} catch (e: Exception) {
Log.e(TAG, "Item could not be updated", e)
}
}
}
}
private fun validateInput(itemDetails: ItemDetails = editItemUiState.itemDetails): Boolean {
return with(itemDetails) {
name.isNotBlank() && price.isNotBlank()
}
}
}
ItemDao
@Dao
interface ItemDao {
@Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun insert(item: Item)
@Update
suspend fun update(item: Item)
@Delete
suspend fun delete(item: Item)
@Query("SELECT * FROM items")
fun getAll(): Flow<List<Item>>
@Query("SELECT * FROM items WHERE id = :id")
fun getItem(id: Int): Flow<Item>
@Query("SELECT * FROM items WHERE price < :price")
fun getByPrice(price: Double): Flow<List<Item>>
}
ItemsRepository
interface ItemsRepository {
fun getAllItemsStream(): Flow<List<Item>>
fun getItemStream(id: Int): Flow<Item?>
fun getByPrice(price: Double): Flow<List<Item>>
suspend fun insertItem(item: Item)
suspend fun updateItem(item: Item)
suspend fun deleteItem(item: Item)
}
class OfflineItemsRepository(
private val itemDao: ItemDao
): ItemsRepository {
override fun getAllItemsStream(): Flow<List<Item>> = itemDao.getAll()
override fun getItemStream(id: Int): Flow<Item?> = itemDao.getItem(id)
override fun getByPrice(price: Double): Flow<List<Item>> = itemDao.getByPrice(price)
override suspend fun insertItem(item: Item) = itemDao.insert(item)
override suspend fun updateItem(item: Item) {
withContext(Dispatchers.IO) {
itemDao.update(item)
}
}
override suspend fun deleteItem(item: Item) = itemDao.delete(item)
}
I have tried changing the way to handle the state in EditItemViewModel, I have changed the updateItem() function, I have changed OnConflictStrategy in ItemDao and I have tried WithContext in the repository.
No changes
I would appreciate some help 🙂