导航 (Navigation)
Compose Navigation 组件 (navigation-compose) 是管理屏幕切换的标准库。
版本要求
从 Navigation 2.8.0 开始,官方强烈推荐使用 Kotlin Serialization 实现类型安全的导航,彻底告别容易出错的字符串路由(String Routes)。
1. 依赖配置
需要在 build.gradle 中添加 Navigation 和 Serialization 插件。
kotlin
plugins {
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.23"
}
dependencies {
implementation("androidx.navigation:navigation-compose:2.8.0-beta01")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}2. 定义路由 (Routes)
不再使用字符串,而是定义 Object (无参数) 或 Data Class (有参数)。所有类必须标记 @Serializable。
kotlin
import kotlinx.serialization.Serializable
// 1. 无参数页面
@Serializable
object Home
@Serializable
object Settings
// 2. 有参数页面
@Serializable
data class Profile(val userId: String, val name: String?)3. 设置 NavHost
使用类型对象来定义目的地。
kotlin
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Home) {
// 定义 Home 页面
composable<Home> {
HomeScreen(
onNavigateToProfile = { id, name ->
// 导航到 Profile,直接传对象
navController.navigate(Profile(userId = id, name = name))
},
onNavigateToSettings = {
navController.navigate(Settings)
}
)
}
// 定义 Profile 页面
composable<Profile> { backStackEntry ->
// 获取参数:直接从 toRoute<T>() 获取对象
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(
userId = profile.userId,
name = profile.name
)
}
// 定义 Settings 页面
composable<Settings> {
SettingsScreen()
}
}
}4. 自定义参数类型 (Custom Types)
默认支持 Int, String, Boolean, Float 等基本类型。如果要传递自定义对象(虽然不推荐传递大对象),需要实现 NavType。
通常情况下,建议只传递 ID,然后在目标页面通过 ID 加载数据。
5. 集成底部导航栏
对于底部导航栏,我们通常需要遍历路由列表。
kotlin
// 定义密封类或接口来管理底部 Tab
@Serializable
sealed class BottomTab(val title: String, val icon: ImageVector)
@Serializable
object TabHome : BottomTab("首页", Icons.Default.Home)
@Serializable
object TabProfile : BottomTab("我的", Icons.Default.Person)
// 在 Scaffold 中使用
val items = listOf(TabHome, TabProfile)
NavigationBar {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
NavigationBarItem(
icon = { Icon(screen.icon, null) },
label = { Text(screen.title) },
// 检查当前路由层次结构中是否包含该 Tab 的路由
// 这样即使进入详情页,底部 Tab 依然保持选中状态
selected = currentDestination?.hierarchy?.any { it.hasRoute(screen::class) } == true,
onClick = {
navController.navigate(screen) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}6. 深层链接 (Deep Links)
依然支持,需要手动映射路径参数。
kotlin
composable<Profile>(
deepLinks = listOf(
navDeepLink<Profile>(basePath = "example://profile")
)
) { ... }这会自动匹配 example://profile/{userId}?name={name}。