Hello all, :wave::skin-tone-2: My name is Christo...
# compose
c
Hello all, 👋🏻 My name is Christoffer and I am a fullstack developer from Helsinki, Finland. I am currently working on an Android app that uses Compose. I have worked with declarative UIs a lot in the past (mainly React/React Native) and I have loved it, Compose is no exception to this. During the past weeks I have worked my way through all of the Compose related material on https://developer.android.com/ and browsed through the sample apps, but I am still struggling to find a good solution for handling UI state changes in my composables. The best example that I have found so far is the Jetnews sample app that uses MutableStateFlow for its UI state. At the moment I am working on the Login screen in our app and I need to react to the following: • I want to hide the keyboard and show a loading state when the user submits the form • I want to show an error and focus the first field if an error occurred • I want to navigate to the overview page if authentication is successful Currently, I have a ViewModel with a MutableStateFlow that is exposed as a StateFlow to the composable. In the composable I consume the state with collectAsState. To detect changes I use remember to store the previous state and compare it to the current state, and execute logic when the state changes using an if and a when statement. I feel that there should be a better way to do this, e.g. subscribe to the UI state, filter out distinct changes to the state, and execute logic when the state changes. Does anyone have any good resources that I can read or view to learn how to properly handle state changes in composable? Thank you in advance. Link to Gist: https://gist.github.com/crisu83/3f3fb978f59ddbda7d74d7a329a4bd44
🧵 2
t
What do you think about having a sealed class to emit ur login events? Here’s an example where I used this approach to load contents from a REST API. Its a simple movie app. with 2 screens (movieList and movieDetail)
c
For the
statusText
there is no need to check the previous state. Just check the current state map it to a string resource and emit the
StatusText()
composable. Clearing focus and hiding keyboard are side effects. You should not call these directly in you composable functions. For example you can use:
Copy code
LaunchedEffect(uiState.status) {
   // manage focus and keyboard
}
This effect will take a key, and it only launches when the key changes. There is no need to store the previous state in your case. BTW, compose will only recompose the parts of your compose tree, when the input changes. https://developer.android.com/jetpack/compose/lifecycle https://developer.android.com/jetpack/compose/side-effects
c
@theapache64 I see that you have modelled your request status similar to how I have. This got me thinking, maybe I should refactor the screen to show a different composable depending on the state instead of trying to do everything in one composable function.
t
Yes. Exactly. You don’t need to compare states to show your UI. You’ll simply render the UI for the current state.
6
c
@Csaba Kozák Thanks for the feedback and thanks for the links. I will wrap those calls in side-effects. It seems that I need to read those articles to refresh my memory.
👍 2
I will rewrite the screen and post the solution here in case someone else is ponding about the same problem.
🔝 3
I am not sure if I am pleased yet, but here is the 2nd iteration: https://gist.github.com/crisu83/3f3fb978f59ddbda7d74d7a329a4bd44
z
Some comments on that revision: I would just use
MutableState
instead of
MutableStateFlow
. Line 41-48: You don’t need to
remember
basic computations like this. It makes the code more complex, and you’re really not saving anything by doing the remember here (remember isn’t free as well, and i’d wager running the
when
on every recomposition might actually be cheaper than remembering it). Line 60: It’s a bit of a code smell for a state change in your view model to trigger an “event” callback from a composition. It’s unnecessarily complex, and breaks layering/UDF. The code path in the view model itself that triggers the internal state change to Success should be responsible for dispatching other events downstream. Your composition should just be responsible for rendering the success state. That said, when you actually do need to invoke a callback from an effect, your
LaunchedEffect
is only capturing the value of the
onSuccess
parameter on each recomposition where it’s (re)started. If
onSuccess
changes without
requestStatus
, you’ll have an outdated function. Always use
rememberUpdatedState
for (non-composable) callbacks that are invoked from long-lived effect handlers (or anything else that can change via recomposition during the lifetime of the effect but shouldn’t cause the effect to restart).
🙏 1
👍 2
c
Thanks for the feedback @Zach Klippenstein (he/him) [MOD]. How would suggest that I handle the success after a successful login request over the network? In TS I would just await the response and redirect the user if the result was a success, but here as we are running the request in a separate thread it is not as simple as that. I guess I could pass a onResult lambda to the login function but that feels a bit dirty to me.
Do you have any thoughts about or comments on my latest changes? https://gist.github.com/crisu83/3f3fb978f59ddbda7d74d7a329a4bd44
z
My point was your view/compose layer shouldn’t be handling network responses at all. Your view model should handle the success event and update some state, which should in turn cause the Composable to update to display that state
c
I see, that makes sense. Is it better to navigate based on a boolean property in the view model inside a side-effect (LaunchedEffect)?
c
I'm still eagerly awaiting updated docs for this sort of stuff. happy im not the only one having this sort of issue
c