I've upgraded to `navigation-compose 2.4.0-alpha05...
# compose
d
I've upgraded to
navigation-compose 2.4.0-alpha05
and my LazyColumns were broken. I've investigated and it looks like
rememberSaveable
usage without a key has been broken. Upon navigation and back it is not restored because
currentCompositeKeyHash
has changed. This means
rememberLazyListState()
doesn't work if you leave a list into a detail and than back it scrolls back up cause it creates a new
LazyListState
@Ian Lake I'm tagging you because I saw you are on top of this kind of issues in other comments and I suppose this information can help you solve other issues with this release. If you do not want me to ping like that I won't do it again. I can (and will) create a bug report: just do not have time now and just wanted to let you guys know of the issue as fast as I could. -- I'll link it here in a response Other issues I noticed: I had a login flow working like this: routes: • EntryPoint • Login • Registration the Entry point sent directly to either login or registration with a
LaunchedEffect
controlled by a StateFlow in the viewmodel. When entering the screen I would mark it to exit when coming back. This allowed me to handle the logic of setting backStackEntry arguments as results of the auth flow. with the old navigation after navigating to login the launched effect didn't receive the "go back" event until the user re-entered the entry point screen while with alpha05 it immediately execute the back call. This was fixed using
lifecycle.whenStarted { }
to handle the navigation back -- but you might wanna mention this as side effect in the release notes.
Copy code
println("currentCompositeKeyHash = $currentCompositeKeyHash")
    val lazyListState = rememberLazyListState()
Before entering detail
Copy code
currentCompositeKeyHash = -1594222293
After going back from detail to the list
Copy code
currentCompositeKeyHash = 1495893918
And about the other issue, to make you understand a little better what I was saying, here's the code
Copy code
val navigateTo by authFlowViewModel.navigateTo.collectAsState()
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    LaunchedEffect(key1 = navigateTo) {
        when (navigateTo) {
            AuthFlowNavigateTo.TargetNotAuthenticated -> {
                lifecycle.whenStarted {
                    actions.navigateBackToTarget(authenticated = false)
                }
            }
            AuthFlowNavigateTo.TargetAuthenticated ->
                lifecycle.whenStarted {
                    actions.navigateBackToTarget(authenticated = true)
                }
            AuthFlowNavigateTo.Login -> {
                actions.navigateToLogin()
                authFlowViewModel.onEntryPointNavigated() // this trigger a change in navigateTo
            }
            AuthFlowNavigateTo.SignIn -> {
                actions.navigateToSignIn()
                authFlowViewModel.onEntryPointNavigated()
            }
        }
    }

    BackPressHandler {
        actions.navigateBackToTarget(authenticated = false)
    }
i
Keep in mind that with animations (like the crossfade we have now), both your old and new destination are going to be recomposed multiple times as that animation happens, rather than it being an instantaneous jump cut between composing the old vs composing the new
At all times though, there's only ever one destination that is STARTED - the one you're going to (the new one); the old one is stopped before the transition begins
So it sounds like everything is working exactly as intended
You might consider using
flowWithLifecycle
(from the Lifecycle 2.4 alphas) on your
navigateTo
flow to avoid collecting your flow at all when you aren't started
I'd also really make sure you aren't misusing state for events -
collectAsState()
should be used for flows that represent state - something you can apply hundreds of times over in an idempotent way. Your
navigateTo
flow seems like an event stream that you'd want to simply collect in a LaunchedEffect
(in that case, you'd really want to have your LaunchedEffect use the other new method of Lifecycle 2.4 -
repeatOnLifecycle
to run your collect only when started)
d
Thanks @Ian Lake I'll use those suggestion. But what about the
currentCompositeKeyHash
changing? That's the thing that I cannot workaround
(the first part of my message was the main reason I pinged you, the second was a bonus and you've been very helpful there!)
c
Keep in mind that with animations (like the crossfade we have now), both your old and new destination are going to be recomposed multiple times as that animation happens
This seems like a great way to test that my composable isn't doing something wrong. 🙃
d
Damn i regret writing the second part of my question. The first one was more important
a
it is hard to figure out what is wrong without seeing the code. could you please file a bug and share the code you have there? thanks
👍 1
i
Yeah, it would be helpful if you add a sample project that reproduces it, since our own demo apps with LazyColumn are working just fine
d
I'll try to reproduce it in a small project. I'll link the bug here when i file it!
Can't reproduce it in a simple example, this is gonna take more time cause I have to start from the project and remove
@Ian Lake sorry for taking so long with this but I couldn't work on trying to study the issue till very recently. So, first of all I'm now on navigation-compose
2.4.0-alpha08
which fixed the issue with
currentCompositeKeyHash
changing. I still had an issue with scroll position not being maintained when entering a detail from my list and coming back. I noticed it was happening ONLY where I was using the
paging-compose
library (version
1.0.0-alpha12
). I had an header and footer in my LazyList so the structure was like this
Copy code
val items = viewModel.pagedItems.collectAsLazyPagingItems()
LazyColumn(...) {
  item { Header() }
  items(items, ...) { ... }
  item { Footer() }
}
simply removing the Header and Footer made the issue go away: the was scroll correctly restored It looks like when the LazyColum restore its state it initially only find the Header and Footer items, it restores to a smaller list and thus when the paging items becomes available again it scrolled all the way up. As a workaround I used
.insertHeaderItem()
and
.insertFooterItem()
but I don't think I should need it. I plan to create a small project reproducing this and open a bug about it
a
@Daniele Segato thank, this issue is tracked here: https://issuetracker.google.com/issues/179397301. feel free to add a comment about your use case there. the only solution for now is to not display the whole LazyColumn or at least the header and footer when items.itemsCount == 0
👍 1