HorizontalPager中每个Compose页面如何实现独立ViewModel实例?
解决方案
问题根因
默认调用
hiltViewModel()时,会以ViewModel类名作为默认key,从最近的上层ViewModelStoreOwner(即MainScreen所属的Activity/Fragment/导航图节点)中获取实例,因此所有ChildScreen拿到的都是同一个对象。
方案1:指定唯一key生成独立ViewModel(最常用)
这是成本最低的适配方案,无需修改现有架构逻辑,仅需给每个页面的ViewModel传入独有的key参数即可:
- 给
ChildScreen传入当前页面的唯一标识,优先使用页面对应的业务ID,没有业务ID的场景可临时使用Pager的下标 - 调用
hiltViewModel()时将唯一标识拼接为key,Hilt会为不同key创建独立的ViewModel实例
修改后的代码如下:
MainScreen 调整
@Composable fun MainScreen(mainViewModel: MainViewModel = hiltViewModel()) { val pages = mainViewModel.pages.collectAsState() val pagerState = rememberPagerState(pageCount = pages.size) HorizontalPager(state = pagerState) { pageIndex -> // 优先用pages[pageIndex].id这类业务唯一ID,避免页面增删时position变化导致的ViewModel错乱 val pageUniqueId = pages[pageIndex].id ?: pageIndex ChildScreen(pageUniqueId = pageUniqueId) } }
ChildScreen 调整
@Composable fun ChildScreen( pageUniqueId: Any, childViewModel:ChildViewModel = hiltViewModel(key = "child_vm_${pageUniqueId}") ){ // 原有业务逻辑不变,每个页面拿到的都是独立的ViewModel实例 }
该方案的ViewModel生命周期和外层宿主(Activity/Fragment)完全一致,和原有View系统中子Fragment与外层Fragment生命周期绑定的逻辑完全对齐。
方案2:自定义ViewModelStoreOwner实现页面级生命周期绑定
如果需要ViewModel严格和Pager页面的生命周期对齐(比如页面滑出回收范围后就销毁对应ViewModel释放资源),可以给每个ChildScreen绑定独立的ViewModelStoreOwner:
@Composable fun ChildScreen(pageUniqueId: Any) { // 为每个页面创建独立的ViewModelStoreOwner,页面被Compose回收时ViewModel也会同步销毁 val viewModelStoreOwner = rememberSaveable(key = pageUniqueId) { object : ViewModelStoreOwner { override val viewModelStore = ViewModelStore() } } val childViewModel: ChildViewModel = hiltViewModel(viewModelStoreOwner = viewModelStoreOwner) // 原有业务逻辑 }
该方案更节省资源,适合页面数量多、单个ViewModel占用资源较高的场景。
内容的提问来源于stack exchange,提问作者Vahid
相关产品推荐
相关产品推荐

