关于大前端,不管是Android还是IOS,Web端都会涉及到页面之间的跳转或传值
今天我们要讲的就是Compose中及其重要的组件 Navigation
(页面导航)
Compose Navigation 是一个用于 Jetpack Compose 的导航组件,它提供了简单而强大的 API 来处理页面跳转和用户界面交互。以下是 Compose Navigation 的核心概念和组件的更详细说明:
rememberNavController()
方法创建 NavController 实例。composable()
函数构建路线(节点)。Screen
组件创建 Screen 实例。navigate()
方法传递 Params。在目标 Screen 中接收和使用这些参数。NavDestinotion
需要集中在一起,统一由Graph
管理这些核心概念和组件为开发者提供了构建复杂的导航结构和用户界面交互的工具,使得在 Jetpack Compose 应用中实现高效的页面跳转和数据传递变得简单而强大。通过合理使用这些概念和组件,可以创建出具有良好用户体验的应用程序。
多个页面相当于传统View里面的Fragment
HomeScreen.kt
@Composable
fun HomeScreen(navController: NavController){
Column(modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
Text(modifier = Modifier.clickable {},
text = "Home",
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.displayLarge)
Text(modifier = Modifier.padding(top = 150.dp).clickable {},
text = "Login/SinUp",
color = Color.Black,
style = MaterialTheme.typography.headlineMedium)
}
}
LoginScreen.kt
@Composable
fun LoginScreen(navController: NavController){
Column(modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
Text(modifier = Modifier.clickable {},
text = "Login",
color = Color.Magenta,
style = MaterialTheme.typography.displayLarge)
Text(modifier = Modifier.padding(top = 150.dp).clickable {},
text = "Go Back",
color = Color.Black,
style = MaterialTheme.typography.headlineMedium)
}
}
SignUpScreen.kt
@Composable
fun SignUpScreen(navController: NavController){
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text(modifier = Modifier.clickable {},
text = "SingUp",
color = Color.Green,
style = MaterialTheme.typography.displayLarge)
}
}
DetailScreen.kt
@Composable
fun DetailScreen(navController: NavController){
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text(modifier = Modifier.clickable {},
text = "Detail",
color = Color.Red,
style = MaterialTheme.typography.displayLarge)
}
}
class NavigationActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeProjectTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
SetupNavGraph(rememberNavController())
}
}
}
}
}
注:在Activity中写了一个SetupNavGraph方法,该方法就是设置导航的统一管理方法
Screen.kt
const val AUTHENTICATION_ROUTE = "authentication"
const val ROOT_ROUTE = "root"
const val HOME_ROUTE = "home"
const val HOME = "home_screen"
const val LOGIN = "login_screen"
const val SIGNUP = "signup_screen"
const val DETAIL = "detail_screen"
sealed class Screen(val route:String){
object Home:Screen(route = HOME)
object Login:Screen(route = LOGIN)
object Signup:Screen(route = SIGNUP)
object Detail:Screen(route = DETAIL)
}
NavGraph.kt
@Composable
fun SetupNavGraph(navController: NavHostController){
NavHost(navController = navController, startDestination = Screen.Home.route){
composable(route = Screen.Home.route){
HomeScreen(navController = navController)
}
composable(route = Screen.Detail.route){
DetailScreen(navController = navController)
}
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
注:
至此,就可以运行项目,显示主页面了。
但是细心的小伙伴,发现了没有,其实我们平时做项目,可能是多模块开发,
所以,我们现在可以把 Home
和 Detail
封装成一模块,把 Login
和 Signup
分成一个模块
@Composable
fun SetupNavGraph(navController: NavHostController){
NavHost(navController = navController, startDestination = HOME_ROUTE, route = ROOT_ROUTE){
navigation(startDestination = Screen.Home.route,route = HOME_ROUTE){
composable(route = Screen.Home.route){
HomeScreen(navController = navController)
}
composable(route = Screen.Detail.route){
DetailScreen(navController = navController)
}
}
navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
}
注:
看上面代码,其实还可以优化,可以通过kotlin的扩展函数,来做分组
HomeNavGraph.kt
fun NavGraphBuilder.homeNavGraph(navController: NavHostController){
navigation(startDestination = Screen.Home.route,route = HOME_ROUTE){
composable(route = Screen.Home.route){
HomeScreen(navController = navController)
}
composable(route = Screen.Detail.route){
DetailScreen(navController = navController)
}
}
}
HomeNavGraph.kt
fun NavGraphBuilder.authNavGraph(navController: NavHostController){
navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
NavGraph.kt
@Composable
fun SetupNavGraph(navController: NavHostController){
NavHost(navController = navController, startDestination = HOME_ROUTE, route = ROOT_ROUTE){
homeNavGraph(navController = navController)
authNavGraph(navController = navController)
}
}
注:
现在看下Graph类的代码是不是简洁很多,而且子模块的分组逻辑清晰,
上面代码有个注意的点: startDestination = HOME_ROUTE
,这里指向了 navigation
扩展函数的route
的值
HomeScreen
@Composable
fun HomeScreen(navController: NavController){
Column(...) {
Text(modifier = Modifier.clickable {
navController.navigate(AUTHENTICATION_ROUTE)
},
...)
...
}
}
AuthNavGraph.kt
fun NavGraphBuilder.authNavGraph(navController: NavHostController){
navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
注:navController.navigate
指向 navigation
的 route
的值,startDestination
的值是 Screen.Login.route
,因此跳转到了登录页面
LoginScreen.kt
@Composable
fun LoginScreen(navController: NavController){
Column(...) {
Text(modifier = Modifier.clickable {
navController.navigate(route = Screen.Signup.route)
},
...)
...
}
}
SignUpScreen.kt
@Composable
fun SignUpScreen(navController: NavController){
Box(...) {
Text(modifier = Modifier.clickable {
navController.popBackStack()
},
...)
}
}
LoginScreen
@Composable
fun LoginScreen(navController: NavController){
Column(...) {
Text(...)
Text(modifier = Modifier.padding(top = 150.dp).clickable {
navController.navigate(HOME_ROUTE){
popUpTo(HOME_ROUTE)
}
},
...)
}
}
注:popUpTo
清除当前栈顶到节点 HOME_ROUTE
之间所有节点(不包含 Screen.Home.route
)
@Composable
fun LoginScreen(navController: NavController){
Column(...) {
Text(...)
Text(...)
Text(modifier = Modifier.padding(top = 30.dp).clickable {
navController.popBackStack()
navController.navigate(Screen.Detail.route)
},
...)
}
}
注:
navController.popBackStack()
返回不同模块上一级,navController.navigate(Screen.Detail.route)
跳转到不同模块下级页面
@Composable
fun SignUpScreen(navController: NavController){
Column(...) {
Text(...)
Text(modifier = Modifier.clickable {
navController.navigate(HOME_ROUTE){
popUpTo(HOME_ROUTE){ inclusive = true}
}
},
...)
Text(modifier = Modifier.clickable {
navController.navigate(HOME_ROUTE){
launchSingleTop
popUpTo(HOME_ROUTE)
}
},
...)
}
}
注:
至此,页面之间跳转的各种情况都讲到了,如有发现其他问题和补充,欢迎反馈和评论区留言
后续关于页面之间的传参,单独写篇文章讲解,后续再会…
rememberNavController()
创建navController.navigate(route路径)
导航页面navController.popBackStack()
返回上一页面popUpTo
清除 当前栈顶 到 当前节点 之间的 所有节点