You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compose Navigation 3中如何获取AnimatedContentScope?

关于Jetpack Navigation 3中获取AnimatedContentScope的问题

问题描述

Jetpack Navigation 3已稳定,迁移后在实现共享元素动画时遇到问题:需要获取AnimatedContentScope,但在EntryProviderScope<NavKey>作用域中直接传递this会出现类型不匹配错误,提示实际类型为EntryProviderScope,但期望AnimatedContentScope。

依赖配置(libs.versions.toml)

[versions]
navigation2 = "2.9.6"
navigation3 = "1.0.0"
# 其他库版本

[libraries]
androidx-navigation2 = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation2" }
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
# 其他库

[plugins]
# 插件配置

AppNavHost.kt

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun AppNavHost() {
    SharedTransitionLayout {
        val navController = rememberNavController()
        NavHost(
            navController = navController,
            startDestination = DashboardNavRoute,
        ) {
            dashboard(
                navController = navController,
                sharedTransitionScope = this@SharedTransitionLayout
            )
            // 其他路由
        }
    }
}

DashboardRoot.kt

@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.dashboard(
    navController: NavController,
    sharedTransitionScope: SharedTransitionScope,
) {
    composable<DashboardNavRoute> {
        val viewModel = hiltViewModel<DashBoardViewModel>()
        val state by viewModel.uiState.collectAsState()
        val snackbarHostState = remember { SnackbarHostState() }
        // 其他组件
        DashboardScreen(
            onEvent = viewModel::onEvent,
            state = state,
            snackbarHostState = snackbarHostState,
            sharedTransitionScope = sharedTransitionScope,
            animatedContentScope = this,
        )
    }
}

DashboardScreen.kt

@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class)
@Composable
internal fun DashboardScreen(
    snackbarHostState: SnackbarHostState,
    sharedTransitionScope: SharedTransitionScope,
    animatedContentScope: AnimatedContentScope,
    onEvent: (DashboardUiEvent) -> Unit,
    state: DashboardUiState,
) {
    // 页面内容
}

AppNavDisplay.kt

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun AppNavDisplay() {
    SharedTransitionLayout {
        val backStack = rememberNavBackStack(DashboardNavRoute)
        NavDisplay(
            backStack = backStack,
            entryProvider = entryProvider {
                dashboardEntry(
                    backStack = backStack,
                    sharedTransitionScope = this@SharedTransitionLayout
                )
                // 其他条目
            }
        )
    }
}

DashboardRoot.kt(存在错误的代码)

@OptIn(ExperimentalSharedTransitionApi::class)
fun EntryProviderScope<NavKey>.dashboardEntry(
    backStack : NavBackStack<NavKey>,
    sharedTransitionScope: SharedTransitionScope,
) {
    entry<DashboardNavRoute> {
        val viewModel = hiltViewModel<DashBoardViewModel>()
        val state by viewModel.uiState.collectAsState()
        val snackbarHostState = remember { SnackbarHostState() }
        // 其他组件
        DashboardScreen(
            onEvent = viewModel::onEvent,
            state = state,
            snackbarHostState = snackbarHostState,
            sharedTransitionScope = sharedTransitionScope,
            animatedContentScope = this, // 此处报错:类型不匹配
        )
    }
}

错误提示

Argument type mismatch: actual type is 'EntryProviderScope', but 'AnimatedContentScope' was expected.

解决方案

在Navigation 3中,entry<DashboardNavRoute>的lambda函数作用域是EntryContentScope,而EntryContentScope继承了AnimatedContentScope,因此只需确保传递的是lambda内部的this(而非外层EntryProviderScope的this)即可。

修改后的DashboardRoot.kt代码:

@OptIn(ExperimentalSharedTransitionApi::class)
fun EntryProviderScope<NavKey>.dashboardEntry(
    backStack : NavBackStack<NavKey>,
    sharedTransitionScope: SharedTransitionScope,
) {
    entry<DashboardNavRoute> {
        val viewModel = hiltViewModel<DashBoardViewModel>()
        val state by viewModel.uiState.collectAsState()
        val snackbarHostState = remember { SnackbarHostState() }
        // 其他组件
        DashboardScreen(
            onEvent = viewModel::onEvent,
            state = state,
            snackbarHostState = snackbarHostState,
            sharedTransitionScope = sharedTransitionScope,
            animatedContentScope = this, // 此处的this是EntryContentScope,符合AnimatedContentScope类型要求
        )
    }
}

如果担心作用域混淆,也可以显式指定lambda参数:

@OptIn(ExperimentalSharedTransitionApi::class)
fun EntryProviderScope<NavKey>.dashboardEntry(
    backStack : NavBackStack<NavKey>,
    sharedTransitionScope: SharedTransitionScope,
) {
    entry<DashboardNavRoute> { entryScope ->
        val viewModel = hiltViewModel<DashBoardViewModel>()
        val state by viewModel.uiState.collectAsState()
        val snackbarHostState = remember { SnackbarHostState() }
        // 其他组件
        DashboardScreen(
            onEvent = viewModel::onEvent,
            state = state,
            snackbarHostState = snackbarHostState,
            sharedTransitionScope = sharedTransitionScope,
            animatedContentScope = entryScope,
        )
    }
}

说明

Navigation 3的entry函数提供的lambda作用域是专门为导航条目内容设计的EntryContentScope,它内置了导航相关的上下文,同时继承了AnimatedContentScope,完全满足共享元素动画对作用域的需求。之前的错误是因为误将外层EntryProviderScope的this传递给了参数,而正确的作用域是entry函数lambda内部的上下文。


内容的提问来源于stack exchange,提问作者Pierre Vieira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 00:05:19