Skip to content

Conversation

@zsmb13
Copy link
Collaborator

@zsmb13 zsmb13 commented Jun 18, 2025

Replace the existing Compose Navigation library with the new Navigation 3 library.

@zsmb13 zsmb13 changed the title Very early WIP: Nav3 WIP: Nav3 Oct 2, 2025
@zsmb13 zsmb13 force-pushed the nav3 branch 2 times, most recently from 15a34ae to d9221a3 Compare November 3, 2025 12:33
@zsmb13 zsmb13 changed the title WIP: Nav3 Use Navigation 3 Nov 3, 2025
@zsmb13 zsmb13 marked this pull request as ready for review November 11, 2025 20:43
@zsmb13 zsmb13 requested review from kropp and terrakok November 11, 2025 20:44
)
}

AnimatedContent(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interested to understand why you didn't use NavDisplay here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, it's what I originally did, you can see the move away from it in this commit: ddeeb64

It's just slightly simpler to do "in-screen" switching between the four main screens here than to run a nested NavDisplay for the main screen within the global one. It took a couple lines of extra code to make sure saved state is handled correctly with this setup, but it was much much easier than I expected.

Another option would have been to move those 4 screens into the global navigation, and either

  1. Have them each contain their own copy of the bottom nav bar - this just feels... weird, and it would have taken extra work to make the bottom bar transition nicely between screens as far as I understand.
  2. Move the bottom bar outside the global NavDisplay and hide/show it based on the current screen - but this global bottom bar feels overkill when only 4 out of the dozens of screens will use it.

Also, there's special navigation behaviour between the 4 main screens (from any of them, back goes back to the first tab, then quits the app). Bolting that logic onto the global backstack would also seem like an unnecessary complication.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, you're using a NavDisplay for all other screens except MainScreen which uses an AnimatedContent to switch between the "main" screens all of which share a bottom nav bar.

Although it feels weird, I think option 1 is actually the option you want to aim for here (although not necessarily in this PR). Wrapping the bottom nav bar in a shared element will lock it in place for the main screens and animate it out for all others. To avoid the duplication of the bottom nav bar inside each entry, you could create a custom entry decorator (e.g. NavBarDecorator) that is only applied to MainRoutes. This would wrap the content of those main entries with the nav bar.

Note that this approach will currently only work if you don't use any scenes that render multiple entries (e.g. list-detail) because the nav bar will be rendered inside the entry, not at the scene level. Scene decorators (coming soon to Nav3) will cover this use case.

For the special navigation behavior you mention, this is the same as the behavior in our multiple back stacks recipe here: https://github.com/android/nav3-recipes/blob/main/app/src/main/java/com/example/nav3recipes/multiplestacks/NavigationState.kt#L74.

The real advantage of moving everything inside a single NavDisplay is you can consolidate your navigation logic. At the moment you have 2 separate navigation systems: one for the main NavDisplay and one inside MainScreen. Another advantage is that you could show the bottom nav bar on non-top-level screens - at the moment you always have to go back from a sub-screen to show the nav bar.

To be clear, I think your current implementation is fine, and there's definitely something nice about having a single back stack where it's always [MainScreen, other keys], but for the reasons above, plus if you ever wanted to support adaptive layouts (desktop maybe?), moving to multiple back stacks and a single NavDisplay is the way to go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed answer, that's a great overview! I think the way to go then is to keep this setup for this PR, and experiment with a decorator and merging the main screens into the "global" NavDisplay setup on another one to see how it works and feels.

The real advantage of moving everything inside a single NavDisplay is you can consolidate your navigation logic. At the moment you have 2 separate navigation systems: one for the main NavDisplay and one inside MainScreen.

It's true that we have two systems right now, but since the one in MainScreen is completely contained (in a single, fairly short file), it actually seems perfectly fine to me. I quite like the idea that the "global" setup using Nav3 doesn't have to know about any of the special things that MainScreen includes (except we're messing with a back handler a little, but that also doesn't require the code outside the screen to know about it).

Another advantage is that you could show the bottom nav bar on non-top-level screens - at the moment you always have to go back from a sub-screen to show the nav bar.

True, although we don't require this anywhere for now (or foreseeable future).

if you ever wanted to support adaptive layouts (desktop maybe?), moving to multiple back stacks and a single NavDisplay is the way to go

We are definitely thinking of going adaptive for large screens at some point, it's currently in the works. So if nothing else, that'll make us revamp the navigation story a bit, I'm sure 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dturner Nice topic!
Could you share your opinion, what is the best approach if I want the same but with a small addition: I want to switch home tabs as like viewpager's pages (by left/right gestures)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a big difference between a navbar navigation and a tabbar with a viewpager.
How to draw a line when parts of the screen should already be a navigation destination?

The close question is when a dialog should be a navigation destination instead of an internal part of a single screen.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that everything should be presented as a navigation entity

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question I always ask is: what does the user expect to happen when they hit back?

Taking your "dialog in single screen" example, if the dialog disappears then the dialog is navigable content. If the user is taken to the previous screen then it's not.

If the user can go forward to a piece of content and come back from it then it's navigable content and should be represented as a NavEntry.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting point of view! Thank you for sharing ❤️
Focused Material3 SearchBar is being closed by a click on the back button, but this is obviously not NavEntry. Why?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants