zak.taccardi
09/01/2020, 5:26 PMflow.collectAsState("initialState")
requires an initial state (when not using a StateFlow<T>
. How can we avoid having this initial state?
For example, I would like to have a composable not be considered “ready” until the Flow<T>
emits. By ready, I mean shown to the user. Is something like this possible? The idea would be something like fragment.postponeEnterTransition(..)
(link) which allows the Fragment
to asynchronously load its data before appearing to the user. This way an initial state would not need to be provided and only fully loaded data need be displayeddimsuz
09/01/2020, 5:43 PMSurface
or something like this.zak.taccardi
09/01/2020, 5:43 PMdimsuz
09/01/2020, 5:44 PMzak.taccardi
09/01/2020, 5:44 PMComposableA
while ComposableB
is still in its “no state” loading statedimsuz
09/01/2020, 5:46 PMflow.collectAsState(Optional.None).filter { it !is None }
but this can probably be considered a work around 🙂zak.taccardi
09/01/2020, 5:46 PMState<T?>
at all, just State<T>
dimsuz
09/01/2020, 5:51 PM.filterSome
which works like this
myStream // Observable<Optional<T>>
.filterSome() // Observable<T>
.map { value: T -> /* do something */ }
So filterSome
does the necessary casting inside. Perhaps something like this can be done here.Adam Powell
09/01/2020, 5:52 PMval thing = flow.observeAsState(null).value ?: return
?@Composable suspend fun
and what kinds of fallback display constructs that might entail if it suspends rather than completes, but any of that is going to be post-1.0 stuffflow<@Composable () -> Unit> { ... }
and
flow.observeAsState({ Fallback() }).value()
Halil Ozercan
09/01/2020, 6:02 PMStateFlow
to another Flow
. Since it becomes a regular Flow
, collectAsState
expects an initial state but in reality it already has an initial state.Adam Powell
09/01/2020, 6:04 PMHalil Ozercan
09/01/2020, 6:04 PM@Composable
inline fun <T, R> StateFlow<T>.collectMap(
context: CoroutineContext = Dispatchers.Main,
crossinline block: T.() -> R
): State<R> = map { it.block() }.collectAsState(value.block(), context)
This was what I had come up with 😄, looks similarzak.taccardi
09/01/2020, 6:07 PMreturn
out of a composeable before rendering anything?Adam Powell
09/01/2020, 6:08 PMremember
🙂remember(this) { map { it.block() } }.collectAsState(...)
.collectAsState
will cancel the old collection and launch a new oneRicardo C.
09/01/2020, 7:30 PMThere's a StateFlow implementation that omits that initial parameter already:we need this on Flow though. It's very easy to land on Flow after applying some operators to StateFlow. And I'm also not sure if we should be exposing StateFlow instead of Flow as it kinda seems we're leaking implementation details
Adam Powell
09/01/2020, 8:32 PMFlow
🙂 we can't know it will emit without suspending until we try it, and beginning collection is a side effect that happens deferred once a composition lands, not synchronously.gildor
09/01/2020, 11:44 PMHalil Ozercan
09/03/2020, 8:35 PMremember { commentsViewModel.isCommentHidden(comment.id) }.collectAsState(initial = false)
Although I understand how remember works and its general functionality in composables, I feel like subscribing to a flow shouldn't require me to be careful about that this call(.collect
) will be re-executed in case of recomposition. Is there any plan in the future like introducing a new API for flow subscriptions without a need for remember
?Adam Powell
09/03/2020, 11:10 PMremember
, the assembly doesremember(commentsViewModel, comment.id) { commentsViewModel.isCommentHidden(comment.id) }.collectAsState(initial = false)
val current by produceState(initial = false, commentsViewModel, comment.id) {
commentsViewModel.isCommentHidden(comment.id)
.collect { setValue(it) }
}
MyComposable(flow.map { thing(it) })
instead of
MyComposable(remember(flow) { flow.map { thing(it) } })
flow.rememberLet { it.map { thing(it) } }.collectAsState(initial)
Halil Ozercan
09/04/2020, 9:54 AMflow.rememberLet { it.map { thing(it) } }.collectAsState(initial)
This looks way more fluent and recognizable.
My example resides in an item view(keyed) of a list, so I didn't think I needed to use viewModel instance and id as arguments to remember. I'm not sure how LazyFor
operates but if my first item gets deleted, would the first composable gets recomposed with second item's properties or does it just leave the UI? If former is the case, remember should definitely receive those arguments.Adam Powell
09/04/2020, 1:48 PMkey
so individual items won't get recomposed for content with a different identity. Sometimes you can situationally omit these things when using remember if you know exactly how they'll be used, but it's a potential surprise for later if you use them elsewhere and those assumptions no longer hold.