frankelot
07/08/2021, 2:01 PM.map on a StateFlow returns a Flow ?iamthevoid
07/08/2021, 2:02 PM.map defined as extension to Flow ?frankelot
07/08/2021, 2:03 PM.map a StateFlow and still keep it a StateFlowiamthevoid
07/08/2021, 2:05 PMmap that creates new instance of StateFlow and return it explicitlyiamthevoid
07/08/2021, 2:06 PMiamthevoid
07/08/2021, 2:08 PMFlow , just flow), so it must be referenced to Flow abstraction and returns Flow, but not an subtype of Flowiamthevoid
07/08/2021, 2:10 PMfrankelot
07/08/2021, 2:11 PMfrankelot
07/08/2021, 2:13 PMclass MainViewModel {
val sf : StateFlow = ...
fun observeFooEvents() : StateFlow = sf.map { it is Foo }
fun observeBarEvents() : StateFlow = sf.map { it is Bar }
}iamthevoid
07/08/2021, 2:13 PMFlow from these functionsiamthevoid
07/08/2021, 2:14 PMobserveFooEvents and observeBarEvents must don’t care about kind of abstraction in your ViewModeliamthevoid
07/08/2021, 2:14 PMiamthevoid
07/08/2021, 2:15 PMfrankelot
07/08/2021, 2:16 PMviewModel.observeBarEvents.collectAsState(initial = ???)frankelot
07/08/2021, 2:16 PMfrankelot
07/08/2021, 2:16 PMfrankelot
07/08/2021, 2:16 PMcollectAsState doesn’t ask for an initial stateiamthevoid
07/08/2021, 2:17 PMfrankelot
07/08/2021, 2:17 PMfrankelot
07/08/2021, 2:18 PMiamthevoid
07/08/2021, 2:18 PMiamthevoid
07/08/2021, 2:19 PMiamthevoid
07/08/2021, 2:19 PMfrankelot
07/08/2021, 2:19 PMfrankelot
07/08/2021, 2:19 PMfrankelot
07/08/2021, 2:19 PMiamthevoid
07/08/2021, 2:21 PMiamthevoid
07/08/2021, 2:24 PMfun <T, R> StateFlow<T>.observeAsState(mapper: (T) -> R) {
return map(mapper).observeAsState(mapper(value))
}iamthevoid
07/08/2021, 2:26 PMiamthevoid
07/08/2021, 2:27 PMfrankelot
07/08/2021, 2:27 PMvalue in this case?frankelot
07/08/2021, 2:27 PMiamthevoid
07/08/2021, 2:27 PMAdam Powell
07/08/2021, 2:31 PM... in val sf : StateFlow = ...?Adam Powell
07/08/2021, 2:32 PMMutableStateFlow or other hot data source you might be better off using snapshot state here instead of StateFlows. More conversation on this specific use case here: https://github.com/Kotlin/kotlinx.coroutines/issues/2631louiscad
07/08/2021, 2:33 PMmap returns a Flow no matter what because its lambda can suspend. The best it could statically do, unless you can have non suspending lambda overloads, is return a SharedFlow.Adam Powell
07/08/2021, 2:35 PMfrankelot
07/08/2021, 2:36 PMvalue is accessible! but collectAsState is only available on a Composable context
@Adam Powell
… is a MutableStateFlow in deed. Will explore using SnapShotstate insteadiamthevoid
07/08/2021, 2:36 PM@Composable annotation to your function🤷iamthevoid
07/08/2021, 2:37 PMfrankelot
07/08/2021, 2:38 PMfrankelot
07/08/2021, 2:39 PMiamthevoid
07/08/2021, 2:40 PMAdam Powell
07/08/2021, 2:41 PMMutableStateFlow then using snapshot state instead this simplifies to:
class MainViewModel {
var source by mutableStateOf(...)
private set
val isFoo: Boolean get() = source is Foo
val isBar: Boolean get() = source is Bar
}
any reads of isFoo or isBar are observable. Instead of using collectAsState you can just write vm.isFoo without any special collect calls or mapping operatorsAdam Powell
07/08/2021, 2:41 PMget() = parts in that example though 🙂 )frankelot
07/08/2021, 2:45 PMmutableStateOf instead will see how it goesfrankelot
07/08/2021, 2:45 PMAdam Powell
07/08/2021, 2:46 PMfrankelot
07/08/2021, 3:09 PMmutableStateOf won’t cut it for me either (I think)
The example I show above is not actually what I’m doing, this is what I’m actually doing
class MainVm {
val stateFlow = ...
val fooHandler = FooHandler(stateFlow.map { it is Foo })
val batHandler = BarHandler(stateFlow.map { it is Bar })
}Adam Powell
07/08/2021, 3:10 PMAdam Powell
07/08/2021, 3:11 PM() -> Boolean, leading to
val fooHandler = FooHandler({ state is Foo })
but what other support you might want/need depends on what else FooHandler does with itAdam Powell
07/08/2021, 3:11 PMfrankelot
07/08/2021, 3:15 PMfrankelot
07/08/2021, 3:16 PMval filterFlow: StateFlow<Map<FILTER, FloatArray>>
Each contextual gets passed a set of filters (it’s an image editing app)frankelot
07/08/2021, 3:17 PMLightContextual gets passed down “EXPOSURE” “BRIGHTNESS”Adam Powell
07/08/2021, 3:19 PMfrankelot
07/08/2021, 3:21 PMfrankelot
07/08/2021, 3:22 PMfrankelot
07/08/2021, 3:22 PMfrankelot
07/08/2021, 3:22 PMstateIn .. I was trying something outAdam Powell
07/08/2021, 3:24 PMfrankelot
07/08/2021, 3:24 PMAdam Powell
07/08/2021, 3:32 PMopen class SliderContextualViewModel(
private val getFilters: () -> Map<FILTER, FloatArray>,
val onFilterChanged: (FILTER, FloatArray) -> Unit,
val onFilterCommitted: () -> Unit = {}
) {
val filters: Map<FILTER, FloatArray>
get() = getFilters()
}
then use it like
// note: mutableStateMapOf is a snapshot state map
private var appliedFilters = mutableStateMapOf<FILTER, FloatArray>()
val lightContextualViewModel = SliderContextualViewModel(
onFilterChanged = ::onValueChanged,
getFilters = { appliedFilters.filterKeys { listOf(FILTER.EXPOSURE, FILTER.GRAIN, FILTER.VIBRANCE).contains(it) } },
onFilterCommitted = history::onCommit
)Manuel Vivo
07/08/2021, 4:12 PMmutableStateOf instead of `StateFlow`/`LiveData`. If you’re not working on a hybrid screen, using the latter makes no sense to me 🙂Manuel Vivo
07/08/2021, 4:12 PMfrankelot
07/08/2021, 4:36 PMfrankelot
07/08/2021, 4:38 PMfrankelot
07/08/2021, 4:38 PMprivate val getFilters: () -> Map<FILTER, FloatArray>Adam Powell
07/08/2021, 4:40 PMfrankelot
07/08/2021, 6:27 PMViewModel-like class that holds the UI state using@Manuel Vivo what do you mean by hybrid in this context?instead of `StateFlow`/`LiveData`. If you’re not working on a hybrid screen, using the latter makes no sense to memutableStateOf
Manuel Vivo
07/08/2021, 7:24 PMStateFlow or LiveData. So if you’re mixing both approaches, you could also consume that stream for Compose. But if you’re in a Compose-only screen, you could just use the Compose State APIs.frankelot
07/08/2021, 7:58 PM