Skip to content

Migrate to Navigation 3#1646

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

Migrate to Navigation 3#1646
claraf3 wants to merge 2 commits intoandroid:mainfrom
claraf3:nav3-migration

Conversation

@claraf3
Copy link

@claraf3 claraf3 commented Feb 5, 2026

Migration to Navigation 3 with simple deeplink implementation

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @claraf3, 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 undertakes a significant architectural update by migrating the Jetnews application to Navigation 3. This change streamlines how routes are defined and managed, introduces a more robust navigation state, and sets up a clear pattern for handling navigation actions. The update also includes foundational support for deeplinking, enhancing the app's ability to respond to external navigation requests.

Highlights

  • Navigation System Migration: The application's navigation system has been fully migrated to use Navigation 3, replacing the previous navigation implementation.
  • Deeplink Implementation: A simple deeplink mechanism has been integrated, allowing the application to handle incoming URIs and navigate to specific content, specifically the home screen with a pre-selected post.
  • Dependency Updates: New dependencies for Navigation 3 (androidx.lifecycle.viewmodel.navigation3, androidx.navigation3.ui) and Kotlin Serialization (kotlinx.serialization.core) have been added, and the gradle/libs.versions.toml file has been updated accordingly.
  • Navigation Logic Refactoring: The core navigation logic has been refactored into new files (JetnewsNavDisplay.kt, JetnewsNavigation.kt) within a dedicated ui/navigation package, replacing the older JetnewsNavGraph.kt and JetnewsNavigation.kt files.
  • UI Component Adaptation: UI components like AppDrawer.kt and AppNavRail.kt have been updated to interact with the new Navigation 3 NavKey system, ensuring seamless navigation within the updated architecture.

🧠 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 kotlin.serialization plugin.
    • Included kotlinx.serialization.core dependency for data serialization.
    • Added androidx.lifecycle.viewmodel.navigation3 and androidx.navigation3.ui dependencies to support the new navigation framework.
  • JetNews/app/src/main/AndroidManifest.xml
    • Added a new intent-filter to enable deeplinking for the /jetnews/home path, specifically allowing for a postId parameter.
  • JetNews/app/src/main/java/com/example/jetnews/ui/AppDrawer.kt
    • Updated the currentRoute parameter to currentKey of type NavKey to align with Navigation 3.
    • Modified the selection logic for navigation items to use currentKey is Home and currentKey == Interests for type-safe route checking.
    • Imported new NavKey, Home, and Interests types from the ui.navigation package.
  • JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsApp.kt
    • Removed the old navController and JetnewsNavigationActions components.
    • Introduced a deeplinkKey parameter to the JetnewsApp composable to handle initial navigation from deeplinks.
    • Initialized NavigationState and Navigator using the new Navigation 3 components.
    • Updated AppDrawer and AppNavRail calls to use the new currentKey and navigator functions for navigation actions.
    • Replaced the JetnewsNavGraph composable with the new JetnewsNavDisplay.
  • JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavGraph.kt
    • This file has been removed as its functionality is now handled by JetnewsNavDisplay.kt.
  • JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavigation.kt
    • This file has been removed, with its responsibilities absorbed by the new JetnewsNavigation.kt in the ui.navigation package.
  • JetNews/app/src/main/java/com/example/jetnews/ui/MainActivity.kt
    • Added necessary imports for Intent, Uri, NavKey, Home, and POST_ID to support deeplinking.
    • Modified the JetnewsApp call to pass the result of getDeepLinkKey(intent) for initial navigation.
    • Implemented a new private function getDeepLinkKey to parse incoming URI data and convert it into a NavKey.
  • JetNews/app/src/main/java/com/example/jetnews/ui/components/AppNavRail.kt
    • Changed the currentRoute parameter to currentKey of type NavKey for consistency with Navigation 3.
    • Updated the selected logic for navigation items to use currentKey is Home and currentKey == Interests.
    • Imported NavKey, Home, and Interests from the new navigation package.
  • JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavDisplay.kt
    • New file added, defining Home (with optional postId) and Interests as NavKeys using @Serializable.
    • Introduces the JetnewsNavDisplay composable, which leverages NavDisplay from androidx.navigation3.ui to render UI based on the current NavigationState.
    • Contains homeEntry and interestsEntry functions to provide NavEntry configurations for each destination.
  • JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavigation.kt
    • New file added, defining rememberNavigationState to manage navigation back stacks.
    • Introduces the NavigationState class, which encapsulates the current navigation keys and their associated back stacks.
    • Defines the Navigator class, providing functions like toHome, toInterests, and pop for programmatic navigation actions.
  • JetNews/gradle/libs.versions.toml
    • Added androidx-navigation3 version definition.
    • Updated the naming convention for kotlinx-serialization-json to kotlinx-serialization and adjusted its reference.
    • Added library entries for androidx-lifecycle-viewmodel-navigation3 and androidx-navigation3-ui.
    • Ensured kotlinx-serialization-core and kotlinx-serialization-json now correctly reference the updated kotlinx-serialization version.
Activity
  • The pull request initiates a significant refactoring effort by migrating the application's navigation system to a new framework.
  • It involves the addition of several new dependencies and the removal of existing navigation-related files.
  • New Kotlin files have been introduced to encapsulate the new Navigation 3 logic and patterns.
  • Existing UI components have been modified to integrate seamlessly with the updated navigation paradigm.
  • A basic deeplinking capability has been implemented, demonstrating how external links can now drive in-app navigation.
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 app to Navigation 3, introducing type-safe navigation with NavKeys and a custom state management solution for handling bottom navigation and deeplinks. The refactoring is extensive and well-executed. However, I've identified a critical issue in the rememberNavigationState implementation where remember is called inside a loop, which violates Compose's rules and can lead to unpredictable behavior. I've also included a couple of medium-severity suggestions to improve code robustness and structure. Overall, great work on this significant migration.

Comment on lines 35 to 52
fun rememberNavigationState(startKey: NavKey): NavigationState {
val currentKeys = rememberNavBackStack(startKey)
val backStacks = buildMap {
getNavigationKeys(startKey)
.forEach {
put(it::class.toString(), rememberNavBackStack(it))
}
}.toMutableMap()
return remember(startKey) { NavigationState(currentKeys, backStacks) }
}
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

Calling a @Composable function like rememberNavBackStack inside a loop (forEach) is a violation of Compose rules. Composable functions must be called from another composable function's scope, not from a regular lambda. This can lead to incorrect state management, performance issues, and unpredictable behavior.

The back stack for each root destination (tab) should be remembered at the top level of the composable. To handle deeplinks correctly, you can use a LaunchedEffect to initialize the specific back stack when startKey changes.

@Composable
fun rememberNavigationState(startKey: NavKey): NavigationState {
    val currentKeys = rememberNavBackStack(startKey)

    // Remember a NavBackStack for each root destination.
    // It's a rule of Compose to call remember functions at the top level, not in loops.
    val homeBackStack = rememberNavBackStack(Home())
    val interestsBackStack = rememberNavBackStack(Interests)

    val backStacks = remember(homeBackStack, interestsBackStack) {
        mutableMapOf(
            Home::class.toString() to homeBackStack,
            Interests::class.toString() to interestsBackStack
        )
    }

    // Use a LaunchedEffect to handle deeplink initialization safely.
    // This will run when the composable first enters the composition and if startKey changes.
    LaunchedEffect(startKey) {
        val targetBackStack = backStacks[startKey::class.toString()]
        if (targetBackStack != null && targetBackStack.lastOrNull() != startKey) {
            targetBackStack.clear()
            targetBackStack.add(startKey)
        }
    }

    return remember(currentKeys, backStacks) { NavigationState(currentKeys, backStacks) }
}

@claraf3
Copy link
Author

claraf3 commented Feb 5, 2026

@dturner Hi Don I wasn't sure how complicated you wanted the deepLink implementation to be. In a small app with only one deep link, I imagine the implementation would probably be similar to what I have right now - a small helper function to parse the postId.

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.

1 participant