繁体   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