https://kotlinlang.org logo
#compose
Title
# compose
l

Lilly

08/18/2022, 9:22 PM
Is there a construct to determine when a screen starts?
r

Russell Stewart

08/18/2022, 9:28 PM
LaunchedEffect
might be what you're looking for: https://developer.android.com/jetpack/compose/side-effects
l

Lilly

08/18/2022, 9:34 PM
It does not give me the possibility to make a call when screen enters. I would like to call a viewModel function as soon as screen enters
l

Landry Norris

08/18/2022, 9:35 PM
What do you mean by ‘screen enters’? Are you including when the user turns the screen off and back on?
r

Russell Stewart

08/18/2022, 9:36 PM
I'm not sure what you mean by "screen enters". Assuming you're using pure Compose, that would be the point at which the Composable loads, which is handled by
LaunchedEffect
. If you're using Fragments with Compose layouts, then you probably want to look at your Fragment lifecycle methods, like
onViewCreated()
or something.
l

Landry Norris

08/18/2022, 9:36 PM
A
LaunchedEffect(Unit)
will run the first time the composition runs.
l

Lilly

08/18/2022, 9:41 PM
In the early days there was a composable that was called
...Active
or something similiar, I can't remember. This was deprecated by
DisposableEffect
.
A
LaunchedEffect(Unit)
will run the first time the composition runs.
ok didn't know. The name does not imply that behavior
By screen enters I mean when screen is visible. Something that renders before anything else
l

Landry Norris

08/18/2022, 9:45 PM
I think the current implementation of Composition is tied to a view. I don’t think setContent can run without the screen being visible.
With LaunchedEffect, the lambda should run on any composition where the key is different from the past composition. In the case of LaunchedEffect(Unit), this will be the first composition. You can also key on a variable if you want it to run the next composition when that variable changes.
l

Lilly

08/18/2022, 10:05 PM
Ok got you. Let me be more specific:
Copy code
vm:
var state by mutableStateOf(State.Initial)

fun loadState()
  isLoading = true
  // do stuff
  state = State()
  isLoading = false
}

ui:
@Composable
fun Screen() {
    LaunchedEffect(Unit) {
        vm.loadState()
    }

    if (!vm.isLoading) {
      ScreenContent(vm.State) // shows a list by using state
    }
}
When I leave the screen and re-enter it, I can see for a second the list with old state until
vm.loadState()
is called and overwrites the old state. Rendering
ScreenContent
is faster than calling
vm.loadState()
. At the time ScreenContent is rendered, isLoading is false. It should be true, but call to vm.loadState() is delayed. Any ideas how I can solve it in UI?
i

Ian Lake

08/18/2022, 10:55 PM
If you want to load something only once for the entire existence of the screen, then you need to do that in the initialization of the ViewModel, not as part of composition (as otherwise it will run every time your screen comes into composition - i.e., when you return to it from the back stack)
l

Lilly

08/18/2022, 11:02 PM
If you want to load something only once for the entire existence of the screen, then you need to do that in the initialization of the ViewModel
That's the problem, I want the opposite. State should be reset on every first composition
i

Ian Lake

08/18/2022, 11:05 PM
That is generally a sign that things are terribly, terribly wrong in your architecture
l

Lilly

08/18/2022, 11:06 PM
Can you elaborate on that please
i

Ian Lake

08/18/2022, 11:07 PM
If the user rotates their device (a config change) or swaps to the camera to take a picture and comes back to your app, has your data actually changed? Should the user really lose their scroll position? No, they should not
You should be reloading data when it actually changes and that usually means your source of truth is providing a Flow of data that automatically updates as your underlying data changes
l

Lilly

08/18/2022, 11:09 PM
You are right, but it's an use case that the list needs to be empty on re-enter
i

Ian Lake

08/18/2022, 11:10 PM
Maybe you can explain why you want to throw away all of the user's state and scroll position first
l

Lilly

08/18/2022, 11:11 PM
It's a scanner screen, scanning for bluetooth devices. The customer wants to have a clear list when screen starts because the name of the bluetooth devices contains a serial number, and the serial number can change.
So it can happen that the discovered devices in the list have another name and are not up to date anymore
i

Ian Lake

08/18/2022, 11:14 PM
Isn't that also the case when you are on the screen and you move around? Don't you just...update the list with the new name?
l

Lilly

08/18/2022, 11:16 PM
Isn't that also the case when you are on the screen and you move around
Our Bluetooth devices don't change their name without interaction, so no. Hmm updating the name might be possible, but why bother with it, when I just need to reload the whole state object
i

Ian Lake

08/18/2022, 11:17 PM
If I turn the screen off, move to a different location and turn my screen on, shouldn't the list also update? It sounds like this whole list should be dynamically updating all of the time. That's the Flow of data that should be happening
l

Lilly

08/18/2022, 11:20 PM
Yes you are right, but the flow is only collected when "Scan" button is clicked because discovery drains the battery and the discovery flow is the producer of the items of the list
Another example where I'm resetting the state uses a
SnapshotStateMap
. In this Map are 200 pairs. I can't update all of these so I simply create a new state object with an empty Map and refill the Map when new data is fetched
i

Ian Lake

08/18/2022, 11:26 PM
It seems hard to justify saying that 'yes, the data can be old and wrong if I turn the screen off, put it in my pocket, and then turn the screen back on 20 minutes later' and saying 'if the user rotates their device, we gotta throw away all of the data because it might have changed in the 1 second the rotation takes'
l

Lilly

08/18/2022, 11:30 PM
True story as long as I update the name of the discovered devices when changed in backend/domain layer
But the source of truth of the device name is the bluetooth API and not my state. Lets say I have changed the serial number, reflect the name change to my ui state, but the bluetooth device didn't commit the change without my knowing. I would have inconsistent state at this point. Only triggering the discovery and the changed device is discovered is reliable.
I could remove the changed device from the list while keeping the rest of the items but how would I do this with the other screen where I'm using the
SnapshotStateMap
? See
Another example where I'm resetting the state uses a
SnapshotStateMap
. In this Map are 200 pairs. I can't update all of these so I simply create a new state object with an empty Map and refill the Map when new data is fetched
Another reason why it might be a good idea to reset the state in this specific use case is like you said
If I turn the screen off, move to a different location and turn my screen on
When the listed device is out of range it is wrong to keep it in list. A user would try to connect because the device is in the list but since the user is out of range it is not possible
i

Ian Lake

08/18/2022, 11:47 PM
note that turning your screen off and on does not trigger any of this code; no state changed in Compose land
You are either actively listening and care about replacing old data immediately or you require the users to hit scan to do a one time scan and you cannot care about stale data
l

Lilly

08/18/2022, 11:51 PM
and you cannot care about stale data
Interesting point of view. Why would it be a problem to reset the state? It isn't really error prone so why isn't it a legit approach for you?
i

Ian Lake

08/18/2022, 11:56 PM
I think the point is that you need to be consistent. Rotating your device throws away all your data but keeping the screen on as you walk out of the building doesn't throw away all your data?
You're trying to "fix" the one case where you actually really don't want to throw away your data (when it actually hasn't changed at all) instead of actually either fixing the stale data problem (by actively listening) or realizing that there are tons of cases where your data is going to be out of date simply because you aren't actively listening and you should really be looking at improving the messaging to your users of when they last scanned so that they know it isn't actively looking
l

Lilly

08/19/2022, 12:00 AM
Good point! You got me
Do you have an advice for my other screen?
The state object of my other screen has a
mutableStateMapOf<String, Any>()
. On first composition a one shot vm function which in turn calls a backend operation is called to fetch the data. The Map is then filled with this data (~ 200 pairs). When a bluetooth operation on the device is performed the device is reconnected and I reset the state. Any ideas how to handle this scenario?
i

Ian Lake

08/19/2022, 1:02 AM
We already talked about where to do a one time fetch the first time the screen is shown: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1660863319502519?thread_ts=1660857750.873029&amp;cid=CJLTWPH7S And it seems like you already know where you want to reset it.
l

Lilly

08/19/2022, 1:09 AM
Sure just wanted to explain how it is done currently. You said it is discouraged to reset the state object so I need to update the state but how, this time I have a Map with ~200 entries
Or should I drop the vm call from composable, load state object once on vm
init
(like you described) and
clear
the Map on re-fetch?
Sorry I'm a bit confused now, do you have a problem with creating a new state object (reset state) in general or just on creating a new state object on composition?
i

Ian Lake

08/19/2022, 2:29 AM
The important part is when you reload your data, not how
l

Lilly

08/21/2022, 3:39 PM
@Ian Lake Could you please give me an advice for a related question? I have a
StateFlow
coming from domain/backend layer, that holds the status of the backend like
CONNECTED, DISCONNECTED
. In Compose it is common to let vm expose the
StateFlow
and
collectAsState
in composable. Is it also legit to collect the
StateFlow
in vm for example in
init
with
launchIn
to be able to merge the value with my state object? The former approach prohibits the possibility to merge the value with my state object, because the state object is constructed in vm. Or should I never collect a
StateFlow
in vm?
96 Views