簡體   English   中英

Jetpack Compose Navigation:直接導航以在不是 startDestination 的嵌套圖中路由

[英]Jetpack Compose Navigation: Direct navigation to route in a nested graph which is not startDestination

我正在開發 Jetpack Compose Navigation 演示,並且我有一個嵌套導航圖,其中包含兩個不同的嵌套路由和每個嵌套路由的屏幕:

  • 登錄圖
  • 主圖

Login Graph 有 3 條路線,用於顯示三個不同的屏幕

  • 路由“登錄”以顯示 LoginScreen
  • 用於顯示 RegisterScreen 的路由“注冊”
  • 用於顯示 RecoverPasswordScreen 的路由“recoverPassword”

主圖有兩條路線用於這些屏幕

  • 用於顯示 HomeScreen 的路由“home”
  • 用於顯示 SettingsScreen 的路由“設置”

嵌套圖創建在MainActivity.kt中調用

setContent {
        NavigationDemoTheme {

            val navController = rememberNavController()
            SetupNavGraph(navController = navController)
        }
    }

文件NestedNavGraph.kt中的 function 如下所示:

fun SetupNavGraph(navController: NavHostController) {
    NavHost(navController = navController, startDestination = "login_route")
    {
        loginGraph(navController = navController)
        mainGraph(navController = navController)
    }
}

LoginNavGraph.kt文件中,我定義了路線和起始目的地

fun NavGraphBuilder.loginGraph(navController: NavController) {
    navigation(startDestination = "login", route = "login_route") {
        composable(route = "login") {
            LoginScreen(navController = navController)
        }

        composable(route = "register") {
            RegisterScreen(navController = navController)
        }

        composable(route = "recover") {
            RecoverPasswordScreen(navController = navController)
        }
    }
}

MainNavGraph.kt文件中,我定義了這兩條路線和這個起始目的地:

 navigation(startDestination = "home", route = "main_route") {

        composable(route = "home") { 
            HomeScreen(navController = navController)
        }

        composable(route = "settings") { 
            SettingsScreen(navController = navController)
        }
    }

我現在的問題是:如何從 SettingsScreen 顯示 RecoverPasswordScreen。 我知道我可以從 SettingsScreen 導航到“login_route”,但隨后將顯示 startDestination,即 LoginScreen。

// shows the LoginScreen because the startDestination in the "login_route" is set to "login"
navController.navigate(route = "login_route")
   

那么,如何直接導航到嵌套圖形路由“login_route”中的路由“recover”? 我想到了以下“解決方法”:

將參數傳遞給“login_route”,例如:

navController.navigate(route = "login_route?destination=recover")

然后,我將只有一條路線作為目的地,例如“LoginView”。 這將像這樣更改 loginGraph:

fun NavGraphBuilder.loginGraph(navController: NavController) {

    navigation(startDestination = "login_view, route = "login_route/{destination}) {

        composable(
            route = "login_view",
            arguments = listOf(
                navArgument("destination") { defaultValue = "login" },
            )
        ) { backStackEntry ->

            val destination =  backStackEntry.arguments?.getString("destination");

            destination?.let { destination ->  
                LoginView(destination = destination)
            }
        }
    }
}

LoginView 是可組合的,它將有一個自己的 NavHost,我可以在其中使用上一條路由中的查詢參數設置 startDestination:

fun LoginView( destination : String = "login"){

    val navController = rememberNavController()
    var startDestination = destination;

    Scaffold ()
    {

        NavHost(
            navController = navController,
            startDestination = startDestination
        ) {

           composable(route = "login") {
             LoginScreen(navController = navController)
           }

           composable(route = "register") {
             RegisterScreen(navController = navController)
           }

           composable(route = "recover") {
             RecoverPasswordScreen(navController = navController) 
           }
    }
}

現在我應該可以從 SettingsScreen 調用 RecoverPasswordScreen 了:

navController.navigate(route = "login_route?destination=recover")

另一種可能性是在 MainGraph 中為 RecoverPassword Screen 定義額外的路徑。 是否有任何其他可能直接訪問嵌套圖中的路線? 如果可以在路由到“login_route”時動態更改 startDestination,那就太好了,但我不知道這如何或是否可能。

Compose 允許您(使用參數導航) 這使您可以導航到您所謂的“嵌套路由”,即屏幕中的特定部分。

現在,這是一個簡單的解釋,我可以離開你,讓你弄清楚。 但是我認為這對您沒有幫助,因為我認為您已經以一種困難的方式實現了導航。 因此,為什么嘗試導航有點復雜。

這是一種更好的實現方式,以便像您想要的那樣導航(從設置屏幕恢復密碼屏幕)更容易。

免責聲明

將任何稱為Main的內容更改為您的 AppName。

我還沒有添加你所有的屏幕

主屏 class

//you could pass in parameters if needed into this constructor
enum class MainScreen(){
//these are your screens
   LogIn(),
   Settings(),
   Recover(),
   Home();

 companion object {
        fun fromRoute(route: String?): MainScreen =
            when (route?.substringBefore("/")) {
                LogIn.name -> LogIn
                Home.name -> Home
                Settings.name -> Settings
                Recover.name -> Recover
                //add the remaining screens
                // a null route resolves to LogInScreen.
                null -> LogIn
                else -> throw IllegalArgumentException("Route $route is not recognized.")
            }
    }

}

主要活動 Class

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MainApp()
        }
    }
}

@Composable
fun MainApp() {
    MainTheme {
        val allScreens = MainScreen.values().toList()
        val navController = rememberNavController()
        val backStackEntry = navController.currentBackStackEntryAsState()
        // currentScrren user is on good if app is large
        val currentScreen = MainScreen.fromRoute(
            backStackEntry.value?.destination?.route
        )
        //Using scaffold is a good idea
        Scaffold(
           //add topAppBar and all other things here
        ) { innerPadding ->
            MainNavHost(navController = navController, modifier = Modifier.padding(innerPadding))

        }
    }
}

//Scaffold requires innerPadding so remove if you decide not to use scaffold
@Composable
fun MainNavHost(navController: NavHostController, modifier: Modifier = Modifier) {
    NavHost(
        navController = navController,
        startDestination = LogIn.name,
        modifier = modifier
    ) {
        composable(LogIn.name) {
            /**
             Your body for logIn page
            **/

        }
//this is how you will navigate to Recover Screen from settings
        composable(Settings.name) {
            SettingsBody(onClickRecoverScreen = {navController.navigate(Recover.name)})

            }
        }
          composable(Recover.name) {
             /**
             Your body for Recover page
            **/
        }
        composable(Home.name) {
             /**
             Your body for Home page
            **/
        }
        


}


設定畫面

@Composable
fun SettingsBody(
    //this callback is how you will navigate from Settings to RecoverPassword
    onClickRecoverScreen: () -> Unit = {},
) {
    Column(
       //Add your designs for this screen
    ) {
        Button(onClick = {onClickRecoverScreen})
    }
}

這是實現導航的最簡單的方法(在我看來),因為您可以簡單地添加回調以導航到應用程序中的不同位置,並且它更具可測試性(如果您測試;))和可擴展性。 您還可以添加深層鏈接並使用 arguments(如上所述)導航到應用程序的特定部分(例如,帳戶屏幕中的特定帳戶)

如果你想了解更多,我強烈推薦這個Navigation Codelab

一種可能的解決方案是使用導航圖中定義的深度鏈接——它們也適用於嵌套目的地。 然后,您可以使用navController.navigate(deepLinkUri)而不是導航到路由名稱

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM