Hello, again! :wave: This might be a silly questio...
# compose
e
Hello, again! 👋 This might be a silly question, but I’m having a hard time trying to understand. Does the recomposition for a
State
only occurs when inside an action from a
@Composable
? For example:
Copy code
var state by rememberSaveable { mutableStateOf(StateA) }
when (state) {
    StateA -> { if(condition) { state = StateB } else { state = StateC } } // This does not trigger
    StateB -> Button(onClick = state = StateD) { Text("Click me") }        // This works fine when clicked
    ...
}
I’m trying to do some condition check before showing UI for the user and tried using
State
. Any thoughts? Thanks a lot! ❤️
z
I thought reading a state and then writing to it later in the same composition caused a compiler error, but even if it doesn’t it’s still a code smell for other reasons. This sort of code is hard to read because it’s not a simple mapping of state to UI (things go backwards), there are kind of multiple sources of truth, and I think it can also cause flicker because that recomposition might not happen until the next frame (although i’m not actually sure what will happen).
You could, for example, do something like this instead:
Copy code
var primaryState by rememberSaveable { mutableStateOf(StateA) }
val secondaryState by remember {
    derivedStateOf {
        if(condition) StateB else StateC
    }
}
And then it would make sense for
StateA
and `StateB`/`StateC` to not share a parent type.
e
Thanks a lot, Zach! I'll try this solution on Monday. Have a great weekend. ❤️
Hello, again Zach. Thanks for your insight! Thanks to that I was able to come with a solution closer to your recommendation. 😊 To give more context, I’m trying to handle a Dynamic Feature installation, which may show a couple of
AlertDialogs
to inform the user about the download process. The
State
are:
Copy code
RequestDownload -> show a dialog to the user
Downloading -> show a dialog with progress
FeatureReady -> open the DFM
Previously, I had another
State
called
FeatureVerification
, which checked if the feature is already installed so it could jump to
FeatureReady
state. Based on your comment, I updated to the following code:
Copy code
val isFeatureReady = isFeatureInstalled(featureName = featureName)
val initialState = if (isFeatureReady) FeatureReady else RequestDownload

var state by rememberSaveable { mutableStateOf(initialState) }

when (state) {
    RequestDownload -> ...
    Downloading -> ...
    FeatureReady -> ...
}
What do you think about it? 😊
z
Can
isFeatureInstalled
change the value that it returns over time? If it can, this code could be tricky to reason about.
e
Yes… It could change. Is it better, based on your previous suggestion?
Copy code
val isFeatureReady = isFeatureInstalled(featureName = featureName)
val initialState = remember {
    derivedStateOf { if (isFeatureReady) FeatureReady else RequestDownload }
}

var state by rememberSaveable { mutableStateOf(initialState.value) }

when (state) {
    RequestDownload -> ...
    Downloading -> ...
    FeatureReady -> ...
}
z
Nope, because
state
will still only read the value when it’s initialized, and not even then if it’s being restored.
e
Do you have any suggestion in this case?
z
Well, first this seems like logic that should probably live in your view model, not your composable.
But the same logic applies wherever you do it. Track isFeatureReady so you know when it changes, and when that happens then update the mutable state
e
I see… The idea behind this Composable is to act like a library: you pass the DFM name and the Compsable shows the dialog and handles the UI/UX. Thanks a lot, Zach. I will try to investigate other solutions.
d
You probably should better move logic to ViewModel or something, and only show state in your Composable function