Status Update
Comments
al...@google.com <al...@google.com> #2
Triage notes: Mirrors some other discussion, we should consider it.
mg...@google.com <mg...@google.com> #3
Thank you for your request.
You’ve described what you want (access to the internal keys
), but not a concrete use case. We need more context on why you need access to the keys
. I'm particularly interested in understanding how you would use keys
to remove a subset without introducing state management to track them.
Could you please:
- Describe your current use case.
- Hypothetically, if you have access to the internal
keys
, explain how you would use it to fix your described use case.
This information will help us understand the problem before considering how to fix it.
Thanks!
ma...@gmail.com <ma...@gmail.com> #4
Thanks for the response!
My use case is a navigation framework with a back stack. What I have access to inside the composition is the current state of the back stack. The UI composition receives a statesToKeep: ImmutableSet<String>
created by something like: backStack.map { it.key }.toPersistentSet()
.
I'm not keeping a detailed ledger of what's been removed from the stack and thus needs to be removed from UI state. This would add a lot of complexity to the API boundary between the layers and both sides of the implementation.
I'm currently wiring this up to SaveableStateHolder
like so:
@Composable
fun updateSaveableStateHolder(
statesToKeep: ImmutableSet<String>,
): SaveableStateHolder {
val stateHolder = rememberSaveableStateHolder()
// Intentionally not State so that we don't trigger recomposition.
val stateKeys = remember { mutableSetOf<String>() }
// Effect probably isn't necessary. Just being safe.
SideEffect {
stateKeys.filter { it !in statesToKeep }.forEach(stateHolder::removeState)
stateKeys.clear()
stateKeys.addAll(statesToKeep)
}
return stateHolder
}
This isn't really that bad. I'm fine shipping this and I'm glad that enough hooks already exist to make this work! But this additional state, whether as shown here or in a hypothetical BackStackWithLedger
implementation, is extra work that is ultimately duplicating the internal state of SaveableStateHolder
.
If SaveableStateHolder
exposed something like val keys: Set<String>
, then the above would be simplified to:
val stateHolder = rememberSaveableStateHolder()
SideEffect {
stateHolder.keys.filter { it !in statesToKeep }.forEach(stateHolder::removeState)
}
Or if a retainAll
was added:
val stateHolder = rememberSaveableStateHolder()
SideEffect {
stateHolder.retainAll(statesToKeep)
}
Or maybe something like @Composable fun updateSaveableStateHolder(statesToKeep: ImmutableSet<Any>)
is added instead.
Hopefully that helps? Let me know if I can provide any additional context.
Description
Jetpack Compose version:
1.7.8
and1.8.0-beta02
Jetpack Compose component used:
androidx.compose.runtime:runtime-saveable
SaveableStateHolder
exposes an API to remove state (removeState(key: Any)
) but does not give you a way to query the current list of keys. This means that the calling code needs to do a bunch of state management in order to keep track of what keys need to be removed which is error prone and seemingly unnecessary considering that it's already stored insideSaveableStateHolder
.Possible API changes
Option 1
Expose the current set of keys:
Option 2
Expose an inverse API: