Flow emitting value even when there are no change

Viewed 776

I have a datastore in my android app where I am storing my profile details. and retrieving as follows

suspend fun saveUser(user: User) {
        dataStore.edit {
            it[USER_ID] = user.id
            it[USER_NAME] = user.name
            it[USER_MOBILE] = user.phone
            it[USER_EMAIL] = user.email
            it[USER_IMAGE] = user.image
            it[USER_ADDRESS] = user.address
        }
    }



val userDate = dataStore.data
        .catch { e ->
            if (e is IOException) {
                Log.e("PREFERENCE", "Error reading preferences", e)
                emit(emptyPreferences())
            } else {
                throw e
            }
        }
        .map { pref ->
            val userId = pref[USER_ID] ?: ""
            val userName = pref[USER_NAME] ?: ""
            val userEmail = pref[USER_EMAIL] ?: ""
            val userImage = pref[USER_IMAGE] ?: ""
            val userPhone = pref[USER_MOBILE] ?: ""
            val userAddress = pref[USER_ADDRESS] ?: ""
            User(
                name = userName,
                image = userImage,
                address = userAddress,
                phone = userPhone,
                id = userId,
                email = userEmail
            )
        }

Along with it I am saving the availibility status of the User

 suspend fun saveIsAvailable(boolean: Boolean) {
        dataStore.edit {
            it[USER_IS_AVAILABLE] = boolean
        }
    }

I am collecting user profile details like this in my viewmodel

viewModelScope.launch(Default) {
            RiderDataStore.userDate.collect {
                user.postValue(it)
            }
        }

Whenever I change the User availibility my user details flow also gets triggered which is unneccessary and causes ui jittering (image reloads). Why does this happen and how to enable the flow to only trigger if the data changes specifically of the user detail.

1 Answers

This is because you update a user property (in DataStore) and at the same time with userDate.collect you're observing all changes made to the user (in DataStore). Your current code has no way to distinguish between "good" and "bad" updates of the user.

Since you seem to ignore the availability in your DataStore Flow called userDate, your returned User objects should indeed stay identical after availability changes. The default behavior for Kotlin Flow is to emit on every change, even if the data is identical. But you can fix that simply by adding a .distinctUntilChanged() after the map operator like:

val userDate = dataStore.data
        .catch { e ->
            if (e is IOException) {
                Log.e("PREFERENCE", "Error reading preferences", e)
                emit(emptyPreferences())
            } else {
                throw e
            }
        }
        .map { pref ->
            val userId = pref[USER_ID] ?: ""
            val userName = pref[USER_NAME] ?: ""
            val userEmail = pref[USER_EMAIL] ?: ""
            val userImage = pref[USER_IMAGE] ?: ""
            val userPhone = pref[USER_MOBILE] ?: ""
            val userAddress = pref[USER_ADDRESS] ?: ""
            User(
                name = userName,
                image = userImage,
                address = userAddress,
                phone = userPhone,
                id = userId,
                email = userEmail
            )
        }.distinctUntilChanged()

See also docs. It makes sure identical data is not emited over and over.

Related