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.isMuted
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.Alex 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