androidkotlincoroutinedatastorekotlin-flow

Datastore - flow no emits new value after change


I cannot receive changes in the data store. After changing the database mode in the settings on the main screen the app should observe another source of data.

@Singleton
class SettingsRepositoryImpl @Inject constructor(
    @ApplicationContext private val context: Context
) : SettingsRepository {
    private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs")

 override val databaseMode: Flow<String>
        get() =
            context.dataStore.data.map { preferences ->
                preferences[DATABASE_MODE_KEY] ?: DatabaseMode.LOCAL.name
            }.distinctUntilChanged()

/// Viewmodel ->
viewModelScope.launch(Dispatchers.Default) {
            observeDatabaseModeUseCase().collect { databaseMode ->
                observeAllProductsUseCase(databaseMode).map { products ->
                    getFilteredProductListUseCase(
                        products,
                        filterProductDataState.value.filterProduct
                    )
                }.collect {
                    _uiState.value = MyPantryProductsUiState.Loaded(
                        MyPantryModel(
                            groupsProduct = getGroupProductListUseCase(it),
                            loadingVisible = false
                        )
                    )
                }
            }
        }

I tried all dispatchers. I don't know why It's not emitting new values after


Solution

  • You're collecting an infinite flow inside another flow's collection block. So when the first item from the outer flow is emitted, it starts collecting the inner flow forever and a second item of the outer flow can never be emitted. Use flatMapLatest instead. This will make it interrupt the secondary flow whenever the first flow emits a new item.

    //In ViewModel init:
    observeDatabaseModeUseCase()
        .flatMapLatest { databaseMode ->
            observeAllProductsUseCase(databaseMode)
        }.map { products ->
            getFilteredProductListUseCase(
                products,
                filterProductDataState.value.filterProduct
            )
        }.onEach {
            _uiState.value = MyPantryProductsUiState.Loaded(
                MyPantryModel(
                    groupsProduct = getGroupProductListUseCase(it),
                    loadingVisible = false
                )
            )
        }
        .flowOn(Dispatchers.Default) // probably unnecessary since you don't seem to be doing blocking work.
        .launchIn(viewModelScope)
    

    You didn't show what _uiState is, but possibly you can make it a StateFlow using stateIn on the above to simplify your code.