Skip to content

Conversation

@dturner
Copy link
Collaborator

@dturner dturner commented Nov 28, 2025

Fixes #147

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @dturner, 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 conditional navigation recipe to ensure its state is fully saveable and restorable, addressing issue #147. By transitioning from a custom AppBackStack to a new Navigator class that leverages kotlinx.serialization for routes and rememberSaveable for state, the application's navigation flow, especially concerning login-protected routes, becomes robust against process death and configuration changes.

Highlights

  • Serialization of Routes: Routes are now defined as a Serializable sealed class Route with a requiresLogin property, enabling them to be saved and restored across process death.
  • New Navigator Class: A dedicated Navigator class has been introduced to encapsulate conditional navigation logic, replacing the previous custom AppBackStack implementation.
  • Saveable State Management: The isLoggedIn state and the NavBackStack itself are now managed using rememberSaveable and rememberSerializable respectively, ensuring persistence across process death and configuration changes.
  • rememberNavBackStack Utility: A new composable function rememberNavBackStack is provided to easily create a saveable NavBackStack for NavKey subtypes, leveraging NavBackStackSerializer and NavKeySerializer.
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 refactors the conditional navigation logic to be saveable, which is a great improvement. The separation of concerns into a Navigator class is well done. However, there is a critical issue where a piece of navigation state (onLoginSuccessRoute) is not saved, which defeats the purpose of the refactor on process death. I've also identified a high-severity bug in the login logic and a medium-severity performance issue. My review provides detailed suggestions to address these points and make the implementation robust.

private var isLoggedIn by isLoggedInState

// The route that we will navigate to after successful login.
private var onLoginSuccessRoute: Route? = null
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The onLoginSuccessRoute property is not persisted across process death. This breaks the conditional navigation flow if the app is killed while on the login screen, defeating the purpose of this refactor.

To fix this, this state should be hoisted to ConditionalActivity and stored using rememberSaveable. The Navigator would then accept this state in its constructor.

In ConditionalActivity.kt:

val onLoginSuccessRoute = rememberSaveable { mutableStateOf<Route?>(null) }
val navigator = remember { // ...
    Navigator(
        // ...
        onLoginSuccessRouteState = onLoginSuccessRoute
    )
}

In Navigator.kt:

class Navigator(
    // ...
    onLoginSuccessRouteState: MutableState<Route?>,
) {
    // ...
    private var onLoginSuccessRoute by onLoginSuccessRouteState
    // ...
}

Since this change spans multiple files, I'm providing the explanation and examples here. Please apply these changes to make the navigation state fully saveable.

Comment on lines +66 to +72
fun login() {
isLoggedIn = true
onLoginSuccessRoute?.let {
backStack.add(it)
backStack.remove(loginRoute)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The login() function has two issues:

  1. If a user navigates to the login screen manually (so onLoginSuccessRoute is null), after a successful login, they remain on the login screen. The login screen should be popped from the back stack.
  2. onLoginSuccessRoute is not cleared after being used. This could lead to unexpected redirection later if the user logs out and logs in again.
    fun login() {
        isLoggedIn = true
        onLoginSuccessRoute?.let { destination ->
            backStack.add(destination)
            backStack.remove(loginRoute)
            onLoginSuccessRoute = null
        } ?: run {
            // When login is successful, we should at least pop the login screen.
            if (backStack.lastOrNull() == loginRoute) {
                backStack.removeLast()
            }
        }
    }

Comment on lines +66 to +70
val navigator = Navigator(
backStack = backStack,
loginRoute = Login,
isLoggedInState = isLoggedInState
)
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 Navigator instance is created on every recomposition. This is inefficient and can lead to subtle bugs. It should be wrapped in remember to ensure the same instance is used across recompositions.

            val navigator = remember(backStack, isLoggedInState) {
                Navigator(
                    backStack = backStack,
                    loginRoute = Login,
                    isLoggedInState = isLoggedInState
                )
            }

// We use a sealed class for the route supertype because KotlinX Serialization handles polymorphic
// serialization of sealed classes automatically.
@Serializable
sealed class Route(val requiresLogin: Boolean = false) : NavKey
Copy link
Collaborator

Choose a reason for hiding this comment

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

Personally, I'm not really a fan of using "Route" in the context of nav3 because we don't use that term at all in nav3 library (kdocs or naming). I think sticking to "Key" helps clarify its role and where its used in the APIs.

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.

[Recipe request] Conditional navigation + rememberSavable

2 participants