简要介绍一下 Compose 导航的类型安全API

社区移动开发Android

picture.image

Compose 导航的类型安全API

Navigation Compose 现在提供类型安全 API, 以提高编译时安全性并减少运行时错误.

索引

  • 类型安全
  • 导航图谱
  • 参数
  • 底部导航栏
  • DeepLink

核心概念

picture.image

picture.image

picture.image

🎯Navigation Compose 中的类型安全API将 Kotlin 的强大类型优势应用到导航结构中, 使代码更可靠, 更易于使用.

picture.image

类型安全 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")
        }
    }
}

获取参数

使用 NavBackStackEntrySavedStateHandle 上的 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()
            }
        }
    }
}

picture.image

picture.image

底部导航栏

Android 应用中常见的 UI 元素, 允许用户在应用的顶层部分之间轻松导航. 导航栏中的每个项目通常对应不同的屏幕或目的地.

DeepLink

即使应用尚未打开, 也可将用户直接带至应用内特定屏幕的特殊 URL.

picture.image

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!

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论