Compose 导航的类型安全API
Navigation Compose 现在提供类型安全 API, 以提高编译时安全性并减少运行时错误.
索引
- 类型安全
- 导航图谱
- 参数
- 底部导航栏
- DeepLink
核心概念
🎯Navigation Compose 中的类型安全API将 Kotlin 的强大类型优势应用到导航结构中, 使代码更可靠, 更易于使用.
类型安全 API 通过为目的地和参数提供类型安全来增强 Kotlin DSL. 这可以通过 Kotlinx 序列化库来实现, 从而最大限度地减少导航代码在整个项目中的扩散.
🎯 实现
应用插件并执行依赖关系
libs.version.toml
[versions]
kotlinx-serialization-json = "1.7.1"
kotlinxSerialization = "2.0.20"
androidx-navigation-compose = "2.8.0"
[libraries]
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation-compose" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
[plugins]
jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinxSerialization" }
build.gradle.kts
(:app)
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
// other plugins ..
alias(libs.plugins.jetbrains.kotlin.serialization)
// ..
}
build.gradle.kts
(项目级别)
plugins {
// other plugins ..
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
// ..
}
将导航目的地声明为数据类或对象, 并用 @Serializable
进行注释. 该库会将这些对象序列化为字符串以用作路径, 并在检索参数时将其反序列化为对象.
// Define your serializable screens
@Serializable
sealed class Screen {
@Serializable
data object Home : Screen()
@Serializable
data class Details(val itemId: Int) : Screen()
}
构建 NavGraph
使用 NavHost 中的可组合函数定义目的地, 使用@Serializable
路由类作为类型参数.
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home) {
composable {
HomeScreen(navController)
}
composable { backStackEntry ->
val detailsScreen = backStackEntry.toRoute()
DetailsScreen(itemId = detailsScreen.itemId)
}
}
}
使用参数导航
使用路由类的实例导航, 为参数提供值.
@Composable
fun HomeScreen(navController: NavHostController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Home Screen")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { navController.navigate(Screen.Details(itemId = 123)) }) {
Text("Go to Details")
}
}
}
获取参数
使用 NavBackStackEntry
或 SavedStateHandle
上的 toRoute()
扩展函数检索路由对象并访问其参数.
val detailsScreen = backStackEntry.toRoute()
val detailId = detailsScreen.itemId
@Composable
fun HomeScreen(navController: NavHostController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Home Screen")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { navController.navigate(Screen.Details(itemId = 123)) }) {
Text("Go to Details")
}
}
}
@Composable
fun DetailsScreen(itemId: Int) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Details Screen")
Spacer(modifier = Modifier.height(16.dp))
Text("Item ID: $itemId")
}
}
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// splash screen
installSplashScreen()
enableEdgeToEdge()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = false
}
// Setting the content
setContent {
// Starting Point
NAppTheme {
AppNavigation()
}
}
}
}
底部导航栏
Android 应用中常见的 UI 元素, 允许用户在应用的顶层部分之间轻松导航. 导航栏中的每个项目通常对应不同的屏幕或目的地.
DeepLink
即使应用尚未打开, 也可将用户直接带至应用内特定屏幕的特殊 URL.
Deep Links in Compose Navigation
Compose 导航中的DeepLink
Jetpack Compose Navigation 简化了DeepLink的处理. 以下是其工作原理:
- navDeepLink: 在你的可组合目的地中使用这个可组合函数来定义相关的DeepLink.
- 自动生成 URL 参数: 该库可根据路由参数智能创建 URL 参数.
- 路径参数: 没有默认值的参数会成为 URL 路径的一部分(例如,
/user/{userId}
). - 查询参数: 带有默认值或类型为
CollectionNavType
的参数会被添加为查询参数(例如,/products?category=electronics
).
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home) {
composable {
HomeScreen(navController)
}
composable(
route = "profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType }),
deepLinks = listOf(navDeepLink { uriPattern = "myapp://profile/{userId}" })
) { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId")
ProfileScreen(userId)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProfileScreen(userId: String?) {
var profile by remember { mutableStateOf(null) }
// Fetch user profile (replace with your actual data source)
LaunchedEffect(userId) {
if (userId != null) {
profile = fetchUserProfile(userId)
}
}
Scaffold(
topBar = {
TopAppBar(title = { Text("Profile") })
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (profile == null) {
CircularProgressIndicator()
} else {
Text("User ID: ${profile?.id}")
Text("Name: ${profile?.name}")
// Add more profile details as needed
}
}
}
}
data class UserProfile(
val id: String,
val name: String,
// ... other profile attributes
)
// Replace with your actual profile fetching logic
suspend fun fetchUserProfile(userId: String): UserProfile? {
return UserProfile(id = userId, name = "John Doe")
}
今天的内容就分享到这里啦!
一家之言, 欢迎拍砖!
Happy Coding! Stay GOLDEN!