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

dimsuz

03/21/2022, 1:35 PM
When using composable navigation, how do I remember something only for the duration of "screen visible"?
Copy code
composable("/my/route") {
  val value = remember { HeavyValue() }
  MyScreen(value)
}
Whenever I navigate back and then forward to this screen, I'd like
HeavyValue
to be recreated. In code above it is created once and persists: push/pop/push <-- will get the same value
c

chansek

03/21/2022, 1:37 PM
How about:
Copy code
fun MyScreen() {
    val value = remember { HeavyValue() }
    MyScreen(value)
}

fun MyScreen(value: HeavyValue) {
    ...
}
d

dimsuz

03/21/2022, 1:41 PM
isn't this the same? what would this change?
h

hfhbd

03/21/2022, 1:48 PM
add a key to
remember
, which invalides the calculation
i

Ian Lake

03/21/2022, 1:56 PM
Sounds like exactly the use case for a ViewModel, which specifically gives you a scope that outlives individual compositions (i.e., isn't cleared when your screen is no longer composed when you move to another screen) but is still cleared when you pop that destination off the back stack
z

Zach Klippenstein (he/him) [MOD]

03/21/2022, 2:15 PM
Navigation saves composable state for destinations that have been popped? I wouldn't think popped destinations would have any state saved - even
rememberSaveable
should be dropped at that point. Otherwise it would end up leaking a ton of memory for destinations that are never pushed back onto the stack.
🤔 1
i

Ian Lake

03/21/2022, 2:40 PM
Once popped, all state associated with that instance is dropped, yes. That's exactly what I said when I said "still cleared"
Yep, rememberSaveable does save across these cases, but you usually wouldn't want to - heavy objects are very dangerous to make part of your saved instance state (if it is even possible to create a minimal parcelable state from your heavy value for your particular case)
d

dimsuz

03/21/2022, 2:59 PM
The thing is that I'm not using arch components and want to create my own
ViewModel
there, that's the
HeavyObject
I'm talking about 🙂. And as per example above, that
remember
will never be disposed.
add a key to
remember
, which invalides the calculation
yep, but what would that key be? Trying to figure this out. Currently I have an idea to try
navBackStackEntry
(which is given to the
composable
body as a key
i

Ian Lake

03/21/2022, 4:21 PM
If you aren't using arch components, what is the
composable
in your code snippet? That looks like an Arch Component 🙂
If you are using Navigation Compose, the only state that is kept after your screen is disposed (no longer visible) is the state associated with any
rememberSaveable
(not the objects themselves, the saved state) and the entire
ViewModel
object. You really need to put your
HeavyObject
in a ViewModel if you want the whole object to exist the entire time period that the NavBackStackEntry exists
d

dimsuz

03/21/2022, 4:26 PM
Hah, yeah, I meant I'm not using arch view models. But I guess I can try putting a ViewModel (which is my heavy object) in a ViewModel :)
i

Ian Lake

03/21/2022, 4:52 PM
Yep, you can even write something like this (assuming you are using Lifecycle ViewModel Compose 2.5.0-alpha03 or higher:
Copy code
private class StashViewModel<T>(val stashed: T): ViewModel()

@Composable
fun <T> stash(stashInitializer: () -> T): T {
    // Use the lambda based constructor introduced in 2.5.0-alpha03
    val stashViewModel = viewModel {
        StashViewModel(stashInitializer())
    }
    return stashViewModel.stashed
}

// Use it like
val value = stash { HeavyValue() }
Of course you'll probably want to make sure you support process death and recreation as well - Arch ViewModels already have integration with
SavedStateHandle
and will support custom Savers here in the next release thanks to https://issuetracker.google.com/195689777 - not sure what your custom ViewModel has to support that case
d

dimsuz

03/22/2022, 10:33 AM
Thank you, I will ponder on this!
7 Views