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

Chuck Stein

08/18/2022, 2:18 AM
I'm using
rememberSaveable
with
inputs
but despite what the javadoc says, the
init
block is not rerun when the
inputs
change. I need the value to be reinitialized when we return to this screen by popping the backstack and giving it new
inputs
. Is this not possible?
i

Ian Lake

08/18/2022, 3:36 AM
When you go to another screen, the old screen isn't being composed anymore (only its state is saved), so when you return back to that screen, the rememberSaveable is reentering composition for the very first time, restoring the state you specifically told it not to reset when it re-enters composition. The inputs used in the previous composition aren't somehow remembered themselves to know that they somehow changed in the indeterminate amount of time when it was on the back stack and that screen wasn't part of the composition
You'd see the same thing if your inputs changed during a config change or after process death and recreation (the first composition just restores the saved value)
c

Chuck Stein

08/18/2022, 4:02 AM
Gotcha, makes sense. I think it would help if the javadoc clarified this, but I understand what's going on now. Is it possible (whether via
rememberSaveable
or anything else) to remember a value across config changes or process death but not when we return to the screen by popping the backstack?
i

Ian Lake

08/18/2022, 4:03 AM
They are all the same process, so no
Maybe you could explain exactly what you're doing in concrete terms or show your code
c

Chuck Stein

08/18/2022, 4:42 AM
Sure thing. Screen A contains a lazy list and a button that navigates to Screen B. The purpose of Screen B is to add more items to the list you're building in Screen A. I want to be able to pop back to screen A from screen B, telling it what the initial lazy list position should be, so that we are automatically scrolled to the first new item in the list (which we just added in Screen B). To do so, I'm calling
remember { LazyListState(firstNewItemIndex) }
. I can't use
rememberLazyListState(firstNewItemIndex)
because that uses
rememberSaveable
under the hood, meaning we can't recreate the
LazyListState
when we return to screen A with a different
firstNewItemIndex
. But now I realize this makes the scroll position less persistent than using
rememberLazyListState
. So is there any way to achieve my goal of auto-scrolling to the first new item in the list, without losing the scroll position on config changes or process death? Originally I was thinking a I could call
lazyListState.scrollToItem(firstVisibleItemIndex)
inside of a
LaunchedEffect
, but then that
LaunchedEffect
would be called again after a config change or process recreation, rather than maintaining the scroll position the list was in just before the config change / process death.
i

Ian Lake

08/18/2022, 4:47 AM
Is there a reason you aren't using Navigation's API for returning a result (https://developer.android.com/guide/navigation/navigation-programmatic#returning_a_result) and using that to return the position to smooth scroll to? You'd only do the scroll to if there is a result to handle, so the config change/process death changes wouldn't add a result
c

Chuck Stein

08/18/2022, 5:43 AM
That's exactly the API I'm using to pass back
firstNewItemIndex
. It's the "you'd only do the scroll to if there is a result to handle" part that I must be doing wrong. This result is part of the saved state handle, so that saved state persists across config changes / process death. If we return to Screen A from Screen B with a
firstNewItemIndex
result, then auto-scroll to this item via
LaunchedEffect
, then the user manually scrolls to a new position, then there's a config change, we would then want to maintain that new position that the user scrolled to, but since
LaunchedEffect
will be re-entering the composition, it will instead auto-scroll to
firstNewItemIndex
again. I suppose I could set the
firstNewItemIndex
value in the `navBackStackEntry`'s saved state handle to null after I retrieve it from the saved state handle for the first time, but in practice when I tried that originally, it seemed that the code that pulled the result from the saved state handle then nullified it was getting called multiple times every time we navigate back to Screen A, so after the first time the code is called and nullified the
firstNewItemIndex
, it would compose Screen A with a null position to scroll to, so the auto-scroll wouldn't work. Through rubber ducking I'm now realizing the code was probably called multiple times because upon navigation back to that destination composable, the destination might recompose more than once before settling. So the solution is probably nullifying the result in the saved state handle only after we perform the auto-scroll inside
LaunchedEffect
, via callbacks leading all the way back to the composable destination where we have access to the `navBackStackEntry`'s saved state handle.
i

Ian Lake

08/18/2022, 6:04 AM
Yes, animations will cause you to recompose many times; that's expected. You'd want to use
savedStateHandle.remove(YOUR_RESULT_KEY)
after you're done processing it e.g. at the end of your
LaunchedEffect
block after the scroll to call
223 Views