Compose - 使用 Navigation2

文章详细介绍了JetpackCompose中NavController和NavHost的使用,包括如何创建和管理页面堆栈,以及如何处理页面跳转、参数传递和深链接。还提到了与底部导航栏的集成,以及与Hilt结合使用的方法,提供了示例代码来展示具体实现。

点击跳转 View 使用方式

一、概念

NavController 控制器用于在目的地之间跳转、处理深层链接、管理返回栈等。通过 rememberNavController() 获取。
NavHost 宿主
NavGraph 导航图一种数据结构,用于定义并连接节点。通过 navController.createGraph() 创建,NavHost的最后一行 Lambda 会传递给该方法来创建。
NavDestination 目的地导航图中的节点,可以是嵌套图(通过navigation()创建)、界面(通过composable()创建)、对话框(通过dialog()创建,悬浮在上一个界面的上方)。

1.1 控制器 NavController

@Composable
public expect fun rememberNavController(
    vararg navigators: Navigator<out NavDestination>
): NavHostController
val navController = rememberNavController()         //获取控制器。

1.2 宿主 NavHost

  • 会出现跳转的界面显示异常(全白或者LazyColumn显示不全,特别是在使用Pager的时候),重启AS没用的话,给 NavHost 设置屏幕级背景色。

@Composable
public fun NavHost(

    //绑定控制器
    navController: NavHostController,

    //起始页
    startDestination: Any,
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    route: KClass<*>? = null,
    typeMap: Map<KType, NavType<*>> = emptyMap(),

    //入场动画
    enterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = {
            fadeIn(animationSpec = tween(700))
        },

    //出场动画
    exitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = {
            fadeOut(animationSpec = tween(700))
        },
    popEnterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = enterTransition,
    popExitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = exitTransition,
    sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)? = null,

    //构建导航图
    builder: NavGraphBuilder.() -> Unit

NavHost(
    modifier = Modifier.fillMaxSize(),
    navController = navController,
    startDestination = MainScreenRoute.Home,
) {
    //构建导航图...
}

1.3 导航图 NavGraph

NavHost 的最后一行 Lambda 会自动传递给该方法来创建。也可单独创建后传递给 NavHost。

public inline fun NavController.createGraph(
    startDestination: Any,        //起始页
    route: KClass<*>? = null,
    typeMap: Map<KType, @JvmSuppressWildcards NavType<*>> = emptyMap(),
    builder: NavGraphBuilder.() -> Unit
): NavGraph
@Composable
public fun NavHost(
    navController: NavHostController,        //绑定控制器
    graph: NavGraph,        //绑定导航图
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    enterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = {
            fadeIn(animationSpec = tween(700))
        },
    exitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = {
            fadeOut(animationSpec = tween(700))
        },
    popEnterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = enterTransition,
    popExitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = exitTransition,
    sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)? = null
)
val navGraph = remember(navController) {
    navController.createGraph(startDestination = MyScreenRoute.Msg) {
        composable<MyScreenRoute.Msg> { MsgScreen() }
        composable<MyScreenRoute.Msg> { MsgScreen() }
    }
}
NavHost(navController = navController, graph = navGraph)

1.4 目的地 NavDestination

界面

public inline fun <reified T : Any> NavGraphBuilder.composable(
    typeMap: Map<KType, @JvmSuppressWildcards NavType<*>> = emptyMap(),

    //深层链接
    deepLinks: List<NavDeepLink> = emptyList(),

    //进场动画
    noinline enterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)? = null,

    //出场动画
    noinline exitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)? = null,
    noinline popEnterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)? = enterTransition,
    noinline popExitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)? = exitTransition,
    noinline sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)? = null,

    //目标可组合项
    noinline content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit
)

对话框public inline fun <reified T : Any> NavGraphBuilder.dialog(
    typeMap: Map<KType, @JvmSuppressWildcards NavType<*>> = emptyMap(),
    deepLinks: List<NavDeepLink> = emptyList(),
    dialogProperties: DialogProperties = DialogProperties(),
    noinline content: @Composable (NavBackStackEntry) -> Unit
)
嵌套图public inline fun <reified T : Any> NavGraphBuilder.navigation(
    startDestination: Any,
    typeMap: Map<KType, @JvmSuppressWildcards NavType<*>> = emptyMap(),
    noinline builder: NavGraphBuilder.() -> Unit
): Unit
NavHost(...) {
    //无参节点
    composable<MyScreenRoute.Msg> { MsgScreen() }
    //有参节点
    composable<MyScreenRoute.Profile> { navBackStackEntry ->
        val profile: MyScreenRoute.Profile = navBackStackEntry.toRoute()
        ProfileScreen(profile.id)
    }
    //嵌套导航图
    navigation<MyScreenRoute.Home>(MyScreenRoute.Home.Hot) {
        composable<MyScreenRoute.Home.Hot> { HomeHotScreen() }
        composable<MyScreenRoute.Home.Square> { HomeSquareScreen() }
    }
    //对话框节点
    dialog<MyScreenRoute.Msg> { MsgScreen() }
}

二、使用方式一(v2.8 版本支持类型安全路径,推荐)

2.1 添加依赖

最新版本

在模块的 build.gradle 中添加插件和依赖。

[versions]
kotlinSerialization = "2.2.10"
kotlinSerializationJson = "1.9.0"

[libraries]
kotlin-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinSerializationJson" }

[plugins]
kotlin-serialization = {  id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinSerialization"  }

2.2 定义路径

sealed interface MyScreenRoute{
    //无参
    @Serializable
    data object Msg : MyScreenRoute
    //有参
    @Serializable
    data class Profile( val id: Long) : MyScreenRoute
    //嵌套导航图
    @Serializable
    sealed interface Home : MyScreenRoute {
        @Serializable data object Hot : Home
        @Serializable data object Square : Home
    }
}

2.3 定义容器&导航图

@Composable
fun SwitchRegion(
    navController: NavHostController = rememberNavController(), //提供默认实现(如果外部没有调用跳转就不用传了)
) {
    NavHost(
        modifier = Modifier.fillMaxSize(),
        navController = navController,
        startDestination = MainScreenRoute.Home,
    ) {
        //无参节点
        composable<MyScreenRoute.Msg> { MsgScreen() }
        //有参节点
        composable<MyScreenRoute.Profile> { navBackStackEntry ->
            //获取参数
            val profile: MyScreenRoute.Profile = navBackStackEntry.toRoute()
            ProfileScreen(profile.id)
        }
        //嵌套导航图
        navigation<MyScreenRoute.Home>(MyScreenRoute.Home.Hot) {
            composable<MyScreenRoute.Home.Hot> { HomeHotScreen() }
            composable<MyScreenRoute.Home.Square> { HomeSquareScreen() }
        }
        //对话框节点
        dialog<MyScreenRoute.Msg> { MsgScreen() }
    }
}

2.4 跳转

navController.navigate(route = MyScreenRoute.Profile(id = 123))

三、使用方式二(字符串路径,传参困难)

由于 NavController 和 NavHost 可以有很多个,NavHost 中的界面要跳转的话,需要使用自己锁绑定的 NavController,不能混用。跳转通过调用 navController.navigate()。

3.1 定义节点配置

使用配置文件便于管理路线名称。

object RouteConfig {
    const val PAGE_ONE = "pageOne"
    const val PAGE_TWO = "pageTwo"
    const val PAGE_THREE = "pageThree"
}

3.2 创建被跳转的界面

被跳转的界面需要调用跳转,将跳转抽取成函数参数。

//定义三个页面
@Composable
fun PageOne() {
    Box(modifier = Modifier.background(Color.Blue).fillMaxSize().wrapContentSize(align = Alignment.Center)) {
        Text(text = "Page One", fontSize = 100.sp, color = Color.White)
    }
}
@Composable
fun PageTwo() {
    Box(modifier = Modifier.background(Color.Green).fillMaxSize().wrapContentSize(align = Alignment.Center)) {
        Text(text = "Page Two", fontSize = 100.sp, color = Color.White)
    }
}
@Composable
fun PageThree(onclicked: () -> Unit) {
    Column(modifier = Modifier.background(Color.Red).fillMaxSize().wrapContentSize(align = Alignment.Center)) {
        Text(text = "Page Three", fontSize = 100.sp, color = Color.White)
        Button(onClick = { onclicked() }){ Text( text = "界面3跳转到界面1" ) }    //子界面中也有跳转时,将逻辑抛给外部
    }
}

3.3 创建用于显示的容器

@Composable
fun SwitchRegion(
    navController: NavHostController = rememberNavController(), //提供默认实现(如果外部没有调用跳转就不用传了)
    startDestination: String = RouteConfig.PAGE_ONE,
    modifier: Modifier = Modifier
) {
    NavHost(
        navController = navController,
        startDestination = startDestination,
        modifier = modifier
    ) {
        composable(route = RouteConfig.PAGE_ONE) { PageOne() }
        composable(route = RouteConfig.PAGE_TWO) { PageTwo() }
        composable(route = RouteConfig.PAGE_THREE) {
            PageThree(
                onClick = { navController.navigate(RouteConfig.PAGE_ONE) }    //实现子界面中的跳转
            )
        }
    }
}

3.4 调用跳转

默认情况下,navigate() 会将目的地添加到回退栈中,可通过 popUpTo() 在跳转前将当前位置到回退栈中的目标位置之间的 NavBackStackEntry 全部弹出来避免添加过多的回退,配置 inclusive = true 包含目标位置也弹出,还可以配置 launchSingleTop = true 实现栈顶复用。

  • 用了 inclusive 就要用 launchSingleTop,否则被跳转的页面会挂载两次(2023-12-13)
// 回退栈会弹出当前位置到"home"之间的所有界面,再进入"friendslist"界面。
navController.navigate("friendslist") {
    popUpTo("home")
}
// 回退栈会弹出当前位置到"home"之间的所有界面,包括”home“,再进入"friendsList"界面。
navController.navigate("friendslist") {
    popUpTo("home") { inclusive = true }
}
// 对应 Android 的 SingleTop,如果回退栈顶部已经是 "search",就不会重新创建。
navController.navigate("search") {
    launchSingleTop = true
}

// 对应 Android 的 SingleTask,如果回退栈里已经有 "home",就会弹出它上面的所有界面。
//方式一
navController.navigate("home") {
    popUpTo("home") { inclusive = true }
}
//方式二
navController.navigate("home") {
    popUpTo("home")
    launchSingleTop = true
}
@Composable
fun Screen(
    modifier: Modifier = Modifier
) {
    val navController = rememberNavController()
    Column(modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Top) {
        SwitchButton(
            onButtonOneClick = { navController.navigate(RouteConfig.PAGE_ONE) },
            onButtonTwoClick = { navController.navigate(RouteConfig.PAGE_TWO) },
            onButtonThreeClick = { navController.navigate(RouteConfig.PAGE_THREE) }
        )
        SwitchRegion(navController = navController)
    }
}
//三个按钮点击切换
@Composable
fun SwitchButton(
    onButtonOneClick: () -> Unit,
    onButtonTwoClick: () -> Unit,
    onButtonThreeClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) {
        Button(onClick = onButtonOneClick) { Text(text = "Button One") }
        Button(onClick = onButtonTwoClick) { Text(text = "Button Two") }
        Button(onClick = onButtonThreeClick) { Text(text = "Button Three") }
    }
}

3.5 返回

//返回上一级界面(推荐)
navController.navigateUp()

//可以指定返回的界面,不指定就相当于navigateUp()
//最后一个界面弹出显示空白页面,通过返回的Boolean=false将activity给finish()掉
navController.popBackStack().also {
    if (!it){
        LocalActivity.current?.finish()
    } 
}

3.6 携带参数跳转

跳转官方可携带数据类型说明

        界面跳转应该传递最少的必要的数据(如唯一标识符ID),需要传递复杂的数据,应该将这些数据保存在数据层,跳转到新界面后根据ID到数据层获取。通过路线处理参数的结构意味着组合将完全独立于 Navigation 并且更易于测试。

3.6.1 必传参数

直接将传递的数据名称使用 "/" 拼写在地址后面添加占位符即可,由于地址是字符串形式,使用 arguments 来指定数据的类型,它接收 NamedNavArgument 类型的列表,可通过 navArgument() 创建元素。从 composable() 的 Lambda 中提取这些参数。跳转时将参数添加到路线中。

//使用配置文件方便管理参数名称
object ParamConfig {
    const val ID = "id"
    const val NAME = "name"
}
NavHost(
    navController = rememberNavController(),
    //路径写法一:使用路线和参数配置文件
    startDestination = "${RouteConfig.PAGE_ONE}/${ParamConfig.ID}/${ParamConfig.NAME}"
) {
    composable(
        //路径写法二:直接写
        route = "pageOne/{id}/{name}", //向路线中添加占位符
        arguments = listOf(    //往集合中添加参数(NamedNavArgument类型)
            navArgument(name = "id") {
                type = NavType.LongType   //指定具体类型
                defaultValue = "123456" //默认值(选配)
                nullable = false    //可否为null(选配)
            },
            navArgument(name = ParamConfig.NAME)    //参数是String类型可以不用额外指定
        )
    ){ navBackStackEntry ->    //从Lambda中提取参数
         val arguments = requireNotNull(navBackStackEntry.arguments)
         val id = arguments.getLong(ParamConfig.ID)
         val name = arguments.getString(ParamConfig.NAME)
         PageProfile(id, name!!)
    }
}
//跳转时将参数添加到路线中
val id = 123456L
val name = "张三"
navController.navigate("${RouteConfig.PAGE_ONE}/$id/$name")

3.6.2 可选参数

必须使用查询参数语法来添加("?argName={argname}"),第一个是参数名,第二个是 key。必须具有 defaultValue集 或 nullablility = true(将默认值隐式设为null),这意味着所有可选参数都必须以列表的形式显示添加到 composable( ) 函数。多个可选参数之间用 & 隔开,例如:"?argName1={argName1}&argName2={argName2}",传递的字符串不要包含 / 符号。

composable(
    route = "pageProfile?userId={userId}",
    arguments = listOf(
        navArgument("userId") {
            defaultValue = "123456"
            nullable = false
        }
    )
) { backStackEntry ->
     backStackEntry.arguments?.getString("userId")
}

navController.navigate("pageProfile?userId=${"123456"}")

3.7 嵌套导航图

        导航图中嵌套导航图,将目的地设为另一张图来对特定流程进行模块化。在 NavHost 中使用 navigation() 来配置,与根图一样嵌套图必须设置起始页。

        无法跳转到嵌套导航图中的某个特定页面,只能跳转到它的起始页,这种特性使其适合用于封装特定流程的界面组合,比如登录和支付流程。

//一般写法
NavHost(navController, startDestination = "home") {
    navigation(startDestination = "username", route = "login") {
        composable("username") { ... }
        composable("password") { ... }
        composable("registration") { ... }
    }
}
//建议用扩展函数方便使用
fun NavGraphBuilder.loginGraph(navController: NavController) {
    navigation(startDestination = "username", route = "login") {
        composable("username") { ... }
        composable("password") { ... }
        composable("registration") { ... }
    }
}
//NavHost中使用
NavHost(navController, startDestination = "home") {
    loginGraph(navController)
}

四、深层链接 Deep Link

深层链接可以响应其他界面或外部APP的跳转,当其他应用触发该深层链接时 Navigation 会自动深层链接到相应的可组合项。composable() 的 deepLinks 参数接收 NavDeepLink 类型的列表,可通过 navDeepLink() 创建元素。

4.1 本应用内跳转

val uri = "https://www.example.com"
composable(
    "profileScreen?id={id}",
    deepLinks = listOf(navDeepLink { uriPattern = "$uri?userId={id}" })
) { backStackEntry ->
    val id = backStackEntry.arguments?.getString("id")
    ProfileScreen(id)
}

4.2 响应外部跳转

默认情况下,深层链接不会向外部公开,需要向 Manifest 中添加相应的 <intent-filter>。

<activity …>
  <intent-filter>
    ...
    <data android:scheme="myapp" android:host="profileScreen" />
  </intent-filter>
</activity>
composable(
    "profileScreen/{id}",
    deepLinks = listOf(navDeepLink { uriPattern = "myapp://profileScreen/{id}" })
) { backStackEntry ->
    val id = backStackEntry.arguments?.getString("id")
    ProfileScreen(id)
}
navController.navigate("myapp://profileScreen/123456")

4.3 PendingIntent

可以像使用任何其他 PendingIntent 一样,使用此 deepLinkPendingIntent 在相应深层链接目的地打开您的应用。

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/$id".toUri(),
    context,
    MyActivity::class.java
)

val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

六、与底部导航栏集成

@Composable
private fun BottomBar(
    navController: NavHostController,
    routes: List<String>,       //导航路线
    labels: Array<String>,      //按钮名称
    normalIcons: List<Int>,     //未选中图标
    selectedIcons: List<Int>,   //选中图标
    modifier: Modifier = Modifier
) {
    //确保各项传入的数量一致
    require(routes.size == labels.size && routes.size == normalIcons.size && routes.size == selectedIcons.size)
    //获取当前的 NavBackStackEntry 来访问当前的 NavDestination
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentDestination = navBackStackEntry?.destination
    Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceAround, verticalAlignment = Alignment.CenterVertically
    ){
        routes.forEachIndexed { index, route ->
            BottomBarItem(
                label = labels[index],
                normalIcon = normalIcons[index],
                selectedIcon = selectedIcons[index],
                //与层次结构进行比较来确定是否被选中
                isSelected = currentDestination?.hierarchy?.any { it.route == route },
                onItemClicked = {
                    navController.navigate(route) {
                        //当页面不在起始页时,按返回键回到起始页
                        popUpTo(navController.graph.findStartDestination().id) {
                            //跳转时保存页面状态
                            saveState = true
                        }
                        //栈顶复用,避免重复点击同一个导航按钮,回退栈中多次创建实例
                        launchSingleTop = true
                        //回退时恢复页面状态
                        restoreState = true
                    }
                }
            )
        }
    }
}

@Composable
private fun BottomBarItem(
    label: String,          //按钮名称
    normalIcon: Int,        //未选中图标
    selectedIcon: Int,      //选中图标
    isSelected: Boolean?,   //是否选中
    onItemClicked: () -> Unit,  //按钮点击监听
    modifier: Modifier = Modifier
) {
    Column(
        //去除点击水波纹
        modifier = modifier.clickable(indication = null, interactionSource = remember { MutableInteractionSource() }, onClick =  onItemClicked),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Icon(
            modifier = modifier.size(30.dp),
            painter = painterResource(id = if (isSelected == true) selectedIcon else normalIcon),
            contentDescription = label,
            tint = if (isSelected == true) AppTheme.colors.textPrimary else AppTheme.colors.textSecondary,
        )
        Text(
            text = label,
            color = if (isSelected == true) AppTheme.colors.textPrimary else AppTheme.colors.textSecondary,
            fontSize = 10.sp,
        )
    }
}

七、与 Hilt 搭配使用

始终使用 hiltViewModel() 来获取带有 @HiltViewModel 注解的实例,该函数可以与带有 @AndroidEntryPoint 注解的 Activity/Fragment 搭配使用。

implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'

7.1 获取作用域限定为目的地的ViewModel实例

@Composable
fun Demo() {
    NavHost(
        navController = navController,
        startDestination = "pageOne"
    ) {
        composable("pageTwo") { navBackStackEntry->
            val viewModel = hiltViewModel<DemoViewModel>()
            PageTwoScreen(viewModel)
        }
    }
}

7.2 获取作用域限定为导航路线或导航图的ViewModel实例

使用 hiltViewModel() 将相应的 backStackEntry 作为参数传递。

@Composable
fun Demo() {
    NavHost(
        navController = navController,
        startDestination = "Page1"
    ) {
        navigation(startDestination = "Page2", route = "Page3") {
            composable("Page4") { backStackEntry ->
                val parentEntry = remember(backStackEntry) {
                    navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
                Page4Screen(parentViewModel)
            }
        }
    }
}

八、监听返回键

BackHandler{
    // 什么都不写就是:拦截返回键事件,不让它回退到上一个界面
    // 返回桌面
    context.startActivity(Intent(Intent.ACTION_MAIN).apply {
    addCategory(Intent.CATEGORY_HOME)
    flags = Intent.FLAG_ACTIVITY_NEW_TASK
    })
}

Duplicate class androidx.navigation.compose.BackStackEntryIdViewModel found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposableSingletons$ComposeNavigatorKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposableSingletons$ComposeNavigatorKt$lambda$127448943$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposableSingletons$DialogNavigatorKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposableSingletons$DialogNavigatorKt$lambda$-1092249270$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposeNavGraphNavigator found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposeNavGraphNavigator$ComposeNavGraph found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposeNavigator found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposeNavigator$Companion found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposeNavigator$Destination found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposeNavigator$Destination$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.ComposeNavigatorDestinationBuilder found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogHostKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogHostKt$DialogHost$1$2 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogHostKt$DialogHost$1$2$2 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogHostKt$DialogHost$1$2$invoke$lambda$2$lambda$1$$inlined$onDispose$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogHostKt$DialogHost$2$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogHostKt$PopulateVisibleList$lambda$12$lambda$11$lambda$10$$inlined$onDispose$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogNavigator found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogNavigator$Companion found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogNavigator$Destination found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.DialogNavigatorDestinationBuilder found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavBackStackEntryProviderKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavGraphBuilderKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavGraphBuilderKt$composable$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostControllerKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostControllerKt__NavHostControllerKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$25$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$25$1$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$28$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$29$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$29$1$1$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$32 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$32$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$33$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$lambda$48$lambda$47$$inlined$onDispose$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.NavHostKt$NavHost$lambda$69$lambda$68$$inlined$onDispose$1 found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Duplicate class androidx.navigation.compose.internal.WeakReference found in modules navigation-compose-jvmstubs-2.9.0.jar -> navigation-compose-jvmstubs-2.9.0 (androidx.navigation:navigation-compose-jvmstubs:2.9.0) and navigation-compose-release.aar -> navigation-compose-release-runtime (androidx.navigation:navigation-compose-android:2.9.0) Learn how to fix dependency resolution errors at https://d.android.com/r/tools/classpath-sync-errors
05-28
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值