Home > Software design >  Update UI State in Realtime after Room Database Changes using Kotlin Flow
Update UI State in Realtime after Room Database Changes using Kotlin Flow

Time:07-04

I have a Jetpack Compose Picture App where the user views Cached Photos from ROOM Database. The database has a isFavorite column which stores Booleans and is updated when the user clicks to like the photo.

This is my dao

@Query("SELECT * FROM astrophotoentity")
suspend fun getSavedAstroPhotos(): Flow<List<AstroPhotoEntity>>

@Query("UPDATE astrophotoentity SET isFavorite =:isFavorite WHERE id=:id")
suspend fun updateIsFavoriteStatus(id: String,isFavorite:Boolean)

This is my Repository code

override suspend fun updateIsFavoriteStatus(photo: AstroPhoto, isFavorite:Boolean) {
        dao.updateIsFavoriteStatus(photo.date,isFavorite)
    }

I am using a use case class for clean architecture.

class UpdateIsFavoriteStatusUseCase (private val repository: AstroRepository) {

suspend operator fun invoke(photo: AstroPhoto, isFavorite:Boolean){

    repository.updateIsFavoriteStatus(photo, isFavorite)
}

}

I also use a simple data class to hold state for the ViewModel.

data class PhotoState(
    val astroPhotos: List<AstroPhoto> = emptyList(),
    val isPhotosListLoading: Boolean = false,
    val errorMessage: String? = null)

This is the ViewModel which initializes the Photo State list to a list fetched from the DB. I use mutableStateOf() to hold the photo objects list. Here I update isFavorite column in the database depending on the passed event.

 @HiltViewModel
    class OverviewViewModel @Inject constructor(
        private val useCaseContainer: UseCaseContainer
    ) : ViewModel() {
    
        var state by mutableStateOf(PhotoState())
            private set
    
    init {

//set the state to the retrieved list of photo objects
... getPhotosListFromDb() 
}
    
     fun onEvent(event: OverviewEvent) { ....

     is OverviewEvent.OnMarkFavorite -> { ....
    
      useCaseContainer.updateIsFavoriteStatus(event.photo, event.isFavorite)}
    
      is OverviewEvent.OnRemoveFromFavorites -> { ....
            
    useCaseContainer.updateIsFavoriteStatus(event.photo, event.isFavorite)}

I pass the Photos list to the below composable.

@Composable
fun AstroPhotoComposable(
photo: AstroPhoto,
onMarkAsFavorite: () -> Unit,
onRemovePhoto: () -> Unit = {}
) {
//retrieve the isFavorite state using photo.isFavorite and cache it
  var isFavorite by remember{ mutableStateOf(photo.isFavorite) ... }

The issue I am facing is that when the photo changes from isFavorite/!isFavorite the state is not reflecting live on the UI.

I have read somewhere about cold and hot kotlin flows but I'm yet to wrap my head around this. I also tried to replace mutableStateOf with MutableStateFlow but the state is not updating on Database Changes.

Basically, I am looking to observe for database changes and get an up-to-date state similar to LiveData.

Any help is appreciated.

CodePudding user response:

Have you ever tried updating the whole entity?

@Update
suspend fun update(myEntity: myEntity): Int

And in my opinion it has to be without suspend

@Query("SELECT * FROM astrophotoentity")
fun getSavedAstroPhotos(): Flow<List<AstroPhotoEntity>>

I'd do it like that.

fun getSavedAstroPhotos() = dao.getSavedAstroPhotos().map{ it.toPhotoState() }

And collect it in view model

 init {
   viewModelScope.launch { 
        getSavedAstroPhotos().collect{
            state = it
        }
    }
 }

Perhabs it helps you link

  • Related