Compose NavHost的使用:

一、依赖引入

Gradle (Module level build.gradle.kts)

kotlin

scss 复制代码
dependencies {
    // Compose Navigation
    implementation("androidx.navigation:navigation-compose:2.7.7")
}

二、核心概念

  1. NavController:导航控制器,负责跳转、回退、获取当前路由
  2. NavHost:导航容器,承载所有页面,绑定 NavController
  3. route:路由字符串,唯一标识页面
  4. composable:页面路由条目,对应一个 Compose 页面
  5. 导航参数:支持必传参数、可选参数、类型转换

三、基础最简使用

kotlin

ini 复制代码
val navController = rememberNavController()

kotlin

scss 复制代码
NavHost(
    navController = navController,
    startDestination = "home" // 初始页面路由
) {
    // 首页
    composable(route = "home") {
        HomeScreen(navController)
    }
    // 详情页
    composable(route = "detail") {
        DetailScreen(navController)
    }
}

3. 页面内跳转

kotlin

arduino 复制代码
// 跳转到详情页
navController.navigate("detail")

// 返回上一页
navController.popBackStack()

// 返回指定路由(清除中间所有页面)
navController.popBackStack("home", inclusive = false)

四、带参数导航(最常用)

方式 1:路径拼接传参

1. 定义带参路由

kotlin

ini 复制代码
// {id} 占位符,必填参数
composable(route = "detail/{id}") { backStackEntry ->
    // 取出参数
    val id = backStackEntry.arguments?.getString("id") ?: "0"
    DetailScreen(id = id)
}

2. 跳转时传参

kotlin

arduino 复制代码
navController.navigate("detail/1001")

方式 2:可选参数(query 参数)

kotlin

ini 复制代码
// ?name={name} 可选参数
composable(route = "detail/{id}?name={name}") { backStackEntry ->
    val id = backStackEntry.arguments?.getString("id")
    val name = backStackEntry.arguments?.getString("name")
}

// 跳转
navController.navigate("detail/1001?name=test")

方式 3:类型安全路由(推荐,避免字符串写错)

1. 定义路由密封类

kotlin

kotlin 复制代码
sealed class Screen(val route: String) {
    object Home : Screen("home")
    object Detail : Screen("detail/{id}") {
        // 拼接完整路由
        fun createRoute(id: Int) = "detail/$id"
    }
}

kotlin

scss 复制代码
NavHost(navController, startDestination = Screen.Home.route) {
    composable(Screen.Home.route) { HomeScreen(navController) }
    composable(Screen.Detail.route) { entry ->
        val id = entry.arguments?.getString("id")
        DetailScreen(id)
    }
}

3. 跳转

kotlin

less 复制代码
navController.navigate(Screen.Detail.createRoute(1001))

五、复杂对象传参(Parcelable)

字符串路由只能传基础类型,对象需要用 navArgument + Parcelable

  1. 数据类实现 Parcelable

kotlin

kotlin 复制代码
@Parcelize
data class User(val name: String, val age: Int) : Parcelable
  1. 注册路由并声明参数

kotlin

typescript 复制代码
composable(
    route = "user",
    arguments = listOf(
        navArgument("user") {
            type = NavType.ParcelableType(User::class.java)
        }
    )
) { entry ->
    val user = entry.arguments?.getParcelable<User>("user")
    UserScreen(user)
}
  1. 跳转携带对象

kotlin

javascript 复制代码
val user = User("张三", 20)
navController.navigate("user") {
    arguments?.putParcelable("user", user)
}

六、跳转常用进阶 API

1. 清除回退栈(登录→首页,不能返回登录)

kotlin

javascript 复制代码
navController.navigate("main") {
    popUpTo("login") { inclusive = true } // 把login页面从栈中删除
}

2. 单一实例(避免重复打开同一页面)

kotlin

javascript 复制代码
navController.navigate("home") {
    launchSingleTop = true
}

3. 组合使用

kotlin

ini 复制代码
navController.navigate(Screen.Detail.createRoute(123)) {
    popUpTo(Screen.Home.route)
    launchSingleTop = true
    restoreState = true // 恢复页面状态
}

七、获取当前路由 / 监听页面切换

kotlin

scss 复制代码
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route

// 监听路由变化
LaunchedEffect(navController) {
    navController.currentBackStackEntryFlow.collect { entry ->
        val route = entry.destination.route
        println("当前页面:$route")
    }
}

kotlin

ini 复制代码
@Composable
fun MainNav() {
    val navController = rememberNavController()
    val items = listOf("home", "mine")

    Scaffold(
        bottomBar = {
            NavigationBar {
                items.forEach { route ->
                    NavigationBarItem(
                        selected = navController.currentDestination?.route == route,
                        onClick = { navController.navigate(route) { launchSingleTop = true } },
                        icon = { Icon(Icons.Default.Home, null) },
                        label = { Text(route) }
                    )
                }
            }
        }
    ) { padding ->
        NavHost(
            navController = navController,
            startDestination = "home",
            modifier = Modifier.padding(padding)
        ) {
            composable("home") { HomePage() }
            composable("mine") { MinePage() }
        }
    }
}

九、常见问题

  1. 参数为空崩溃 :使用 ?: 默认值 兜底
  2. 重复创建页面 :跳转加 launchSingleTop = true
  3. 返回栈错乱 :合理使用 popUpTo 清理页面
  4. 页面状态丢失 :开启 restoreState = true,或用 ViewModel 保存数据
  5. 跨模块路由:统一用常量 / 密封类管理路由字符串

十、ViewModel 在导航中的使用

每个 composable 作用域内可以独立获取 ViewModel,参数通过 savedStateHandle 获取:

kotlin

scss 复制代码
composable("detail/{id}") { entry ->
    val vm: DetailViewModel = viewModel(
        factory = DetailViewModelFactory(entry.savedStateHandle)
    )
    DetailPage(vm)
}

ViewModel:

kotlin

kotlin 复制代码
class DetailViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    val id = savedStateHandle.get<String>("id")
}

十一、composable 完整作用详解

1. 本质是什么

composable()NavHost 内部的路由注册函数 ,专门用来给 NavHost 注册一个页面路由规则。它是 NavGraphBuilder 的扩展函数,只能写在 NavHost { ... } 大括号内部。

2. 核心三大作用

① 绑定路由标识 route

route = "home":给这个页面定义唯一字符串标识,后续 navController.navigate("home") 跳转时,NavHost 靠这个 route 找到对应页面。

② 定义该路由对应的 Compose 页面内容

花括号 { HomeScreen(navController) } 就是这个路由要渲染的界面:

  • 当导航跳转到 home 路由时,NavHost 会执行这个 lambda,渲染里面的 HomeScreen
  • 页面入栈、出栈、动画、生命周期全部由 NavHost 管理

③ 支持配置参数、页面过渡动画、参数类型转换

完整参数列表:

kotlin

javascript 复制代码
composable(
    route: String,                // 路由唯一标识
    arguments: List<NamedNavArgument> = emptyList(), // 页面接收的参数
    deepLinks: List<NavDeepLink> = emptyList(),     // 深度链接
    enterTransition: (() -> EnterTransition)? = null,  // 进入动画
    exitTransition: (() -> ExitTransition)? = null,    // 退出动画
    popEnterTransition: (() -> EnterTransition)? = null,// 返回进入动画
    popExitTransition: (() -> ExitTransition)? = null  // 返回退出动画
) { backStackEntry ->
    // 当前页面的UI代码
}

kotlin

scss 复制代码
NavHost(navController, startDestination = "home") {
    // 这里整个大括号是 NavGraphBuilder 构建导航图
    // 每一个 composable 就是导航图里的一个节点(页面)
    composable(route = "home") {
        HomeScreen(navController)
    }
    composable(route = "detail") {
        DetailScreen()
    }
}

可以简单理解:

  • NavHost = 页面容器(存放所有页面)
  • composable = 容器里单个页面的注册条目

4. backStackEntry 参数是什么

lambda 里默认携带 backStackEntry: NavBackStackEntry,用来获取当前页面传递过来的导航参数:

kotlin

scss 复制代码
composable("detail/{id}") { backStackEntry ->
    // 取出路由传递的 id
    val id = backStackEntry.arguments?.getString("id")
    DetailScreen(id)
}

5. 通俗比喻

把 NavHost 想象成一个手机多任务窗口管理器

  1. composable("home"):登记一个叫「home」的窗口,窗口内容是 HomeScreen
  2. navController.navigate("home"):打开登记好的 home 窗口
  3. popBackStack():关闭当前窗口,回到上一个登记的窗口

补充区分

  • @Composable 注解:标记普通可组合函数(页面、控件)
  • NavGraphBuilder.composable():导航专用注册函数,二者完全不是一个东西,只是名字都带 composable
相关推荐
hunterandroid1 小时前
Room 数据库迁移:让本地数据升级更稳
android·前端
雁鸣零落1 小时前
浏览器扩展 CaptionGo,在网页视频上显示双语字幕,支持 PC 和手机端使用
android·chrome·edge·firefox·安卓
2501_915106322 小时前
iOS 软件测试工具性能监控、日志分析 KeyMob、Instruments等
android·ios·小程序·https·uni-app·iphone·webview
__Witheart__2 小时前
lunch选择的版型与版型对应的配置文件
android·linux
stevenzqzq3 小时前
快递盒模型
android
黄林晴3 小时前
Composables CLI 发布!一站式创建与管理 Compose Multiplatform 项目
android
再让我睡两分钟4 小时前
【无标题】
android·java·数据库·人工智能·prompt·ai应用开发
louisgeek15 小时前
Android Studio 和 Git
android
solo_9920 小时前
Android Event 日志完全指南
android