Fabio Berta
01/27/2023, 10:41 PMVideosList composable that receives isAutoPlayEnabled as a parameter. isAutoPlayEnabled comes from the viewModel, it's source of truth is a preference data store. Now I need to react to changes in this parameter to play/pause videos. When I put the logic in the VideosList composable, I can have a LaunchedEffect that is keyed on isAutoPlayEnabled , it will run every time that property changes. Now, because there are more states involved (e.g. lifecycle onResume/onPause), I would like to move this (and other logic) into a state holder class, let's call it VideoListState. But how can I listen to changes of isAutoPlayEnabled inside this class? I can re-instantiate it whenever the param changes, but that seems a bit wasteful, especially considering that I would also like to subscribe to lifecycle events in the class. Optimally, I could pass a Flow<Boolean> and I guess I could get that by either exposing a Flow from the viewModel and passing that down or by using a combination of rememberUpdatedState and snapshotFlow .However both options somehow feel weird. Maybe I'm missing something obvious here that would make all of this simpler?Alex Vanyo
01/27/2023, 10:55 PMinterface VideoListState {
val something: String
fun doSomething()
// other methods and values of the state holder
}
And then having a @Composable rememberVideoListState(): VideoListState that looks something like this:
@Composable
fun rememberVideoListState(
isAutoPlayEnabled: Boolean
): VideoListState {
var internalState by remember { mutableStateOf() }
var otherInternalState by rememberSaveable { mutableStateOf() }
LaunchedEffect(isAutoPlayEnabled) {
// do something
}
return remember {
object : VideoListState {
override val something: get() = // ...
override fun doSomething() {
}
}
}
}
So to anyone consuming VideoListState, it looks like you have a normal class implementation, but the implementation can use more composition tools without the recreating problem.Fabio Berta
01/27/2023, 11:02 PMLaunchedEffect. I don't really need isAutoPlayEnabled anywhere in the UI, it's just for the side-effect of playing/pausing, hence subscribing to a flow would feel more natural to me.Fabio Berta
01/27/2023, 11:04 PMisMuted state. This state was held higher up than the VideoList because it needed to be changed there. What I ended up was creating a VideoMutedState class that basically just wraps a mutableStateOf(muted). I then pass this into VideoListState and finally into VideoState where I can properly subscribe to changes using snapshotFlow because it's backed by compose state.Fabio Berta
01/27/2023, 11:04 PMFabio Berta
01/27/2023, 11:14 PMAlex Vanyo
01/27/2023, 11:51 PMFlow makes more sense, snapshotFlow is the tool for that.
I’m not sure if there’s a single “proper” way, if you’re taking care to avoid cyclical dependency loops and have a single source of truth. And it can be tricky to find the place where you have all of the info you need to make some decisionFabio Berta
01/28/2023, 10:57 AMsnapshotFlow requires compose state which isAutoPlayEnabled is not in my example. I could use rememberUpdatedState to create a compose state but that feels a bit off?Alex Vanyo
01/28/2023, 5:57 PM@Composable function, you can use rememberUpdatedState to get it back to a State that you can observe in a snapshotFlow