如何在Composable函数中注入ViewModel?是否推荐仅在主组件注入?
推荐仅在MainScreen注入ViewModel,再传递给子组件
直接说结论:优先选择在MainScreen注入ViewModel,然后通过参数传递数据和回调给子组件,而不是让每个子组件各自注入同一个ViewModel,原因如下:
- 子组件复用性更高:TopBar、ItemsContent这类UI组件只需要关心接收数据和触发回调,不用绑定特定的ViewModel。比如TopBar可以直接拿到一个
onClick回调,不管这个回调是来自ViewModel还是其他逻辑,换个页面用也不用改代码。 - 逻辑更集中,好维护:所有和ViewModel的交互都放在MainScreen里,想看这个页面的业务逻辑(比如退出、添加条目、获取列表),直接看MainScreen就行,不用在多个子组件里找。
- 避免状态不一致风险:虽然
hiltViewModel()在同一个ViewModelStoreOwner下会返回同一个实例,但如果子组件被不小心放到不同的作用域里(比如嵌套在其他导航页面),可能会创建新的ViewModel实例,导致状态乱掉。用参数传递就没这个问题。
如果让子组件各自注入ViewModel,会带来这些麻烦:
- 子组件和ViewModel强绑定,换个场景用不了,比如别的页面需要类似的TopBar,但用的是另一个ViewModel,就没法复用。
- 逻辑分散,比如要改
addItem的参数,得同时改MainScreen和FAB的代码,维护成本高。
优化后的代码示例:
// 子组件只接收必要的参数和回调 @Composable fun TopBar(onSignOutClick: () -> Unit) { TopAppBar(title = { Text("主页") }) { Button(onClick = onSignOutClick) { Text("退出登录") } } } @Composable fun ItemsContent(items: List<String>, modifier: Modifier = Modifier) { LazyColumn(modifier = modifier) { items(items) { item -> Text(item, modifier = Modifier.padding(16.dp)) } } } @Composable fun FAB(onAddClick: () -> Unit) { FloatingActionButton(onClick = onAddClick) { Icon(Icons.Default.Add, contentDescription = "添加条目") } } // MainScreen负责注入ViewModel并传递数据/回调 @Composable fun MainScreen( viewModel: ItemsViewModel = hiltViewModel() ) { Scaffold( topBar = { TopBar(onSignOutClick = viewModel::signOut) }, content = { paddingValues -> ItemsContent( items = viewModel.getItems(), modifier = Modifier.padding(paddingValues) ) }, floatingActionButton = { FAB(onAddClick = { viewModel.addItem("ABC") }) } ) }
内容的提问来源于stack exchange,提问作者Always Learner
相关产品推荐
相关产品推荐

