Search code examples
androidandroid-architecture-componentsandroid-jetpackandroid-jetpack-datastore

Read a value from DataStore once to update options menu checkbox state in a fragment


I save the state of an options menu checkbox item (checked/unchecked) in Jetpack DataStore:

override fun onOptionsItemSelected(item: MenuItem) =
    when (item.itemId) {
        R.id.action_hide_completed_tasks -> {
            item.isChecked = !item.isChecked
            viewModel.hideCompleted(item.isChecked)
            true
        }
        else -> super.onOptionsItemSelected(item)
    }

which calls through to this method:

suspend fun updateHideCompleted(hideCompleted: Boolean) {
    dataStore.edit { preferences ->
        preferences[PreferencesKeys.HIDE_COMPLETED] = hideCompleted
    }
}

Now I want to restore this checkbox state when my fragment comes onto the screen. But since Jetpack DataStore provides the user preferences in form of a Flow, this feels a bit hacky. I collect from this Flow in onPrepareOptionsMenu and use the take(1) operator to only do it once, because after that I don't need to update the checkbox from the preferences anymore, the state automatically changes when I click the options menu item:

override fun onPrepareOptionsMenu(menu: Menu) {
    lifecycleScope.launch {
        viewModel.preferencesFlow
            .take(1) // I want this to update only when the fragment comes onto the screen, afterward it's unnecessary
            .collect {
                menu.findItem(R.id.action_hide_completed_tasks).isChecked = it.hideCompleted
            }
    }
}

Is this the correct approach? It feels a bit hacky. Will the asynchronicity cause problems in certain cases?


Solution

  • I believe the proper way to do it is using the Flow<T>.first() operator.

    override fun onPrepareOptionsMenu(menu: Menu) {
        lifecycleScope.launch {
            val value = viewModel.preferencesFlow.first()
            menu.findItem(R.id.action_hide_completed_tasks).isChecked = value.hideCompleted
        }
    }