https://kotlinlang.org logo
Title
e

escodro

09/24/2021, 6:48 PM
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:
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

Zach Klippenstein (he/him) [MOD]

09/24/2021, 6:58 PM
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:
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

escodro

09/24/2021, 9:50 PM
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:
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:
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

Zach Klippenstein (he/him) [MOD]

09/27/2021, 3:26 PM
Can
isFeatureInstalled
change the value that it returns over time? If it can, this code could be tricky to reason about.
e

escodro

09/27/2021, 4:39 PM
Yes… It could change. Is it better, based on your previous suggestion?
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

Zach Klippenstein (he/him) [MOD]

09/27/2021, 5:28 PM
Nope, because
state
will still only read the value when it’s initialized, and not even then if it’s being restored.
e

escodro

09/27/2021, 5:44 PM
Do you have any suggestion in this case?
z

Zach Klippenstein (he/him) [MOD]

09/27/2021, 5:59 PM
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

escodro

09/27/2021, 6:12 PM
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

Dmitrii Smirnov

09/29/2021, 6:36 AM
You probably should better move logic to ViewModel or something, and only show state in your Composable function