Skip to content

JetNews: Migrate from Navigation 2 to Navigation 3#1644

Open
stevenelliottjr wants to merge 2 commits intoandroid:mainfrom
stevenelliottjr:jetnews-nav3-migration
Open

JetNews: Migrate from Navigation 2 to Navigation 3#1644
stevenelliottjr wants to merge 2 commits intoandroid:mainfrom
stevenelliottjr:jetnews-nav3-migration

Conversation

@stevenelliottjr
Copy link

Summary

  • Replaces NavHost/NavController with Nav3's NavDisplay and a developer-owned SnapshotStateList backstack
  • Replaces string routes (JetnewsDestinations) with @Serializable NavKey types (Home, Interests) for type-safe navigation
  • Deep link handling moved from navDeepLink {} DSL to manual intent parsing in MainActivity

Changes

File What changed
libs.versions.toml Added navigation3-runtime, navigation3-ui, lifecycle-viewmodel-navigation3
build.gradle.kts Swapped Nav2 dep for Nav3 deps, added kotlin.serialization plugin
JetnewsNavigation.kt Deleted JetnewsDestinations/JetnewsNavigationActions, added sealed interface JetnewsRoute : NavKey
JetnewsNavGraph.kt NavHostNavDisplay with entryProvider lambda and Nav3 decorators
JetnewsApp.kt rememberNavController()mutableStateListOf<JetnewsRoute>(), navigation via list ops
AppDrawer.kt currentRoute: StringcurrentRoute: JetnewsRoute, is type checks
AppNavRail.kt Same as AppDrawer
MainActivity.kt Manual deep link parsing (intent?.data?.getQueryParameter("postId"))

Test plan

  • ./gradlew :JetNews:app:assembleDebug builds successfully
  • App launches to Home screen
  • Drawer navigation: Home ↔ Interests with correct selection state
  • Expanded screen: NavRail shows correct selection state
  • Deep link: adb shell am start -a android.intent.action.VIEW -d "https://developer.android.com/jetnews/home?postId=post5"
  • System back button pops from Interests → Home correctly

Fixes #1632

Replace NavHost/NavController with NavDisplay and a developer-owned
SnapshotStateList backstack. String routes are replaced with
@serializable NavKey types for type-safe navigation.

Fixes android#1632
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @stevenelliottjr, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the JetNews application's navigation architecture. The core change involves migrating from the traditional Navigation Compose (Navigation 2) to the newer Navigation 3 library. This transition enhances type safety for navigation routes and provides developers with direct control over the navigation backstack, leading to a more robust and predictable navigation experience.

Highlights

  • Navigation System Update: The application's navigation system has been upgraded from Navigation 2 to Navigation 3, adopting NavDisplay and a developer-managed SnapshotStateList for backstack control.
  • Type-Safe Navigation: String-based routes (JetnewsDestinations) have been replaced with @serializable NavKey types (Home, Interests), enabling type-safe navigation throughout the app.
  • Deep Link Handling: Deep link processing has been refactored from the navDeepLink {} DSL to manual intent parsing within MainActivity, providing more explicit control.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • JetNews/app/build.gradle.kts
    • Added the kotlin.serialization plugin.
    • Replaced Navigation 2 dependencies with Navigation 3 runtime, UI, and ViewModel integration libraries.
    • Added kotlinx.serialization.json dependency.
  • JetNews/app/src/main/java/com/example/jetnews/ui/AppDrawer.kt
    • Updated the currentRoute parameter type from String to JetnewsRoute.
    • Modified navigation item selection logic to use is type checks for Home and Interests routes.
    • Updated the preview function to use Home() instead of JetnewsDestinations.HOME_ROUTE.
  • JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsApp.kt
    • Replaced rememberNavController() with a mutableStateListOf<JetnewsRoute>() to manage the navigation backstack.
    • Removed currentBackStackEntryAsState and JetnewsNavigationActions.
    • Implemented custom navigateToHome and navigateToInterests functions using backstack list operations.
    • Updated JetnewsNavGraph to accept the new backStack instead of navController.
    • Added a startRoute parameter to JetnewsApp to handle initial navigation state.
  • JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavGraph.kt
    • Replaced NavHost with NavDisplay.
    • Removed navController and startDestination parameters.
    • Introduced a backStack parameter of type SnapshotStateList<JetnewsRoute>.
    • Updated entryDecorators to include rememberSaveableStateHolderNavEntryDecorator and rememberViewModelStoreNavEntryDecorator.
    • Refactored composable blocks into an entryProvider lambda using when (route) for Home and Interests types.
    • Removed navDeepLink {} DSL and POST_ID constant.
    • Updated HomeViewModel factory to use route.preSelectedPostId.
  • JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavigation.kt
    • Deleted the JetnewsDestinations object and JetnewsNavigationActions class.
    • Defined a sealed interface JetnewsRoute : NavKey for type-safe navigation.
    • Introduced @Serializable data class Home(val preSelectedPostId: String? = null) : JetnewsRoute.
    • Introduced @Serializable data object Interests : JetnewsRoute.
  • JetNews/app/src/main/java/com/example/jetnews/ui/MainActivity.kt
    • Added logic to manually parse deep link postId from the incoming intent.data.
    • Created a Home route with the preSelectedPostId and passed it as startRoute to JetnewsApp.
  • JetNews/app/src/main/java/com/example/jetnews/ui/components/AppNavRail.kt
    • Updated the currentRoute parameter type from String to JetnewsRoute.
    • Modified navigation rail item selection logic to use is type checks for Home and Interests routes.
    • Updated the preview function to use Home() instead of JetnewsDestinations.HOME_ROUTE.
  • JetNews/gradle/libs.versions.toml
    • Added version references for androidx-navigation3 and androidx-lifecycle-viewmodel-navigation3.
    • Added library definitions for androidx-navigation3-runtime, androidx-navigation3-ui, and androidx-lifecycle-viewmodel-navigation3.
Activity
  • The pull request introduces a significant architectural change by migrating the navigation system.
  • The author has provided a detailed summary of changes and a test plan, indicating thorough preparation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully migrates the JetNews app from Jetpack Navigation 2 to Navigation 3, adopting type-safe routes with Kotlin Serialization and a developer-owned backstack. The changes are well-implemented and follow the new Navigation 3 paradigm. I've provided a couple of suggestions to improve UI consistency and optimize navigation logic.

val navigationActions = remember(navController) {
JetnewsNavigationActions(navController)
val backStack = remember { mutableStateListOf<JetnewsRoute>(startRoute) }
val currentRoute: JetnewsRoute = backStack.lastOrNull() ?: Home()
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

When the backStack becomes empty (e.g., when the user presses back on the last screen), currentRoute defaults to Home(). This causes AppDrawer and AppNavRail to show "Home" as selected, while NavDisplay shows a blank screen because its backstack is empty. This creates a UI inconsistency.

To fix this, currentRoute should be nullable. When it's null, no navigation item will be selected, which correctly reflects the empty state.

You'll also need to update the currentRoute parameter in AppDrawer and AppNavRail to be nullable (JetnewsRoute?). The is checks for selection will continue to work correctly.

Suggested change
val currentRoute: JetnewsRoute = backStack.lastOrNull() ?: Home()
val currentRoute: JetnewsRoute? = backStack.lastOrNull()

Comment on lines 49 to 51
// Pop to Home, then add Interests on top (single top behavior)
while (backStack.size > 1) backStack.removeLast()
backStack.add(Interests)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation for navigateToInterests always pops back to the home route before adding the Interests route. If the user is already on the Interests screen, this causes the screen to be unnecessarily removed and re-added, which can lead to inefficient recomposition and potential UI flicker. This behavior doesn't fully match the launchSingleTop flag from Navigation 2, which would prevent this.

To optimize this and align closer with launchSingleTop, you can add a check to do nothing if Interests is already the current route.

Suggested change
// Pop to Home, then add Interests on top (single top behavior)
while (backStack.size > 1) backStack.removeLast()
backStack.add(Interests)
if (backStack.lastOrNull() == Interests) return@lambda
while (backStack.size > 1) backStack.removeLast()
backStack.add(Interests)

Make currentRoute nullable to avoid showing Home as selected when the
backstack is empty. Add single-top guard to navigateToInterests to
prevent unnecessary remove-and-re-add when already on Interests.
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.

[FR]: migrate navigation-compose to Jetpack Navigation 3

1 participant