Reputation: 57
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
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