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

Navigation 3中向Hilt ViewModel传递参数的可行方案咨询

我之前升级到Navigation 3时也碰到过一模一样的问题,折腾了好一阵才捋清楚门道,分享几个亲测有效的解决办法给你:


1. 结合@AssistedInject + 绑定导航条目解决缓存问题

你提到用@Assisted会导致ViewModel被 memoized 不响应新路由,核心问题其实是没把ViewModel实例和当前导航条目绑定起来。正确的操作应该是:

首先,给ViewModel加上@AssistedInject构造,同时声明依赖和@Assisted修饰的SavedStateHandle:

class MyViewModel @AssistedInject constructor(
    @Assisted private val savedStateHandle: SavedStateHandle,
    private val myRepository: MyRepository // 你的其他DI依赖
) : ViewModel() {
    // 从SavedStateHandle获取路由参数
    val param = savedStateHandle.get<String>("param_key")
}

// 配套定义一个Factory类供Hilt使用
@AssistedFactory
interface MyViewModelFactory {
    fun create(savedStateHandle: SavedStateHandle): MyViewModel
}

然后在Compose页面中,先获取当前导航条目,再通过这个条目获取ViewModel,确保每个路由实例对应专属的ViewModel:

composable("my_route/{param_key}") { navBackStackEntry ->
    val viewModel = hiltViewModel<MyViewModel>(navBackStackEntry)
    MyScreen(viewModel)
}

这样每次进入新路由条目,都会创建/复用对应条目的ViewModel,不会再出现缓存导致的旧内容问题。

2. 直接用SavedStateHandle获取路由参数(无需@Assisted)

其实Navigation 3里SavedStateHandle依然可以正常使用,你之前可能是没正确把导航条目和ViewModel关联起来。只要在获取ViewModel时传入当前的navBackStackEntry,Hilt会自动把对应条目的SavedStateHandle注入到ViewModel中:

ViewModel定义:

@HiltViewModel
class DetailViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: ItemRepository
) : ViewModel() {
    // 直接从SavedStateHandle读取路由参数
    val itemId = savedStateHandle.get<String>("item_id") ?: ""
}

Compose页面中:

composable(
    route = "detail/{item_id}",
    arguments = listOf(navArgument("item_id") { type = NavType.StringType })
) { backStackEntry ->
    // 传入backStackEntry,让Hilt绑定对应的SavedStateHandle
    val viewModel = hiltViewModel<DetailViewModel>(backStackEntry)
    DetailScreen(viewModel)
}

这种方式最简洁,适合传递路由参数的场景,完全不需要额外的@Assisted配置。

3. 正确使用rememberViewModelStoreNavEntryDecorator()

你提到用这个方法会影响其他ViewModel,是因为可能全局应用了这个装饰器。正确的做法是只针对需要按路由作用域的ViewModel单独使用:

composable("specific_route") { navEntry ->
    val viewModel = rememberViewModelStoreNavEntryDecorator(navEntry) { viewModelStoreOwner ->
        hiltViewModel<SpecificViewModel>(viewModelStoreOwner = viewModelStoreOwner)
    }
    SpecificScreen(viewModel)
}

这样只会让SpecificViewModel的生命周期绑定到当前路由条目,其他ViewModel依然沿用默认的作用域,不会互相干扰。


总结一下,最推荐的是第二种方式(直接用SavedStateHandle+绑定导航条目),既满足DI需求,又能轻松获取路由参数;如果需要传递非路由参数(比如自定义对象),再用第一种@AssistedInject的方案。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 12:24:34