mfusco
mfusco

Reputation: 57

Accessing DataStore flow variables kotlin jetpack compose

I'm trying to access Flow data in a component and I'm not sure how to do so. I have the following code here that defines a flow from a DataStore string:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val ACCOUNT_TYPE = stringPreferencesKey("account_type")
        val accountTypeFlow: Flow<String> = applicationContext.dataStore.data
            .map { preferences ->
                preferences[ACCOUNT_TYPE] ?: ""
            }
        
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Text(text = "") // display flow value here
                }
            }
        }
    }
}

I'm also unsure whether preferences[ACCOUNT_TYPE] ?: "" above is what I need or something else. Any help would be greatly appreciated. Thank you.

Upvotes: 1

Views: 291

Answers (1)

tyg
tyg

Reputation: 15470

As already mentioned in the other question, the flow doesn't belong in the composable. First thing would be to move it to the view model where you should make it to a StateFlow with:

.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5_000),
    initialValue = /* some initial value to use until the underlying flow produces its first value */,
)

(In clean architecture, the data store is part of the data layer, so the data store flow should be moved to a dedicated repository class. Only the stateIn() actually belongs in the view model and is applied to the flow that the view model retrieved from the repository.)

Now, let's assume in your activity the variable viewModel holds an instance of your view model, you can collect the flow inside a composable (f.e. as a first line in the setContent lambda) by calling:

val accountType by viewModel.accountTypeFlow.collectAsStateWithLifecycle()

This requires the artifact androidx.lifecycle:lifecycle-runtime-compose in your gradle dependencies. It is a lifecycle-aware collection of the flow tailored to the use in compose (more details). It returns a compose State object that is directly delegated to the accountType variable (note the delegation operator by instead of the assignment operator =), so that variable now holds the content of the state object. It is of the same type as the content of your flow, in your case a String.

This variable is always up-to-date with the current value of your flow. As soon as the value of the flow changes, this variable also changes its value.

Since all of this happens in a composable, a recomposition is triggered whenever a new value arrives, so everything that follows will get updated with the new value. As an example, this will always display the newest value that the flow produced:

Text(text = accountType)

Regarding your last question: preferences[ACCOUNT_TYPE] retrieves the current preference for "account_type". This preference may very well be missing, as it will be for a freshly installed app. In that case null is returned. It's up to you what you want as a fallback. ?: "" falls back to an empty string.

Upvotes: 0

Related Questions