我有点困惑。我们可以将 viewmodel 传递给另一个可组合函数吗?如果不是,那么将任何视图模型访问另一个函数的好方法是什么?我在这里给出代码片段,以便您可以更好地理解。单击对话框中的“保存”按钮后,我正在传递视图模型以访问房间数据库插入功能之一。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun homeScreen(modifier: Modifier, todoViewModel: todoViewModel = viewModel()) {
val dataList by todoViewModel.getAllTodo.collectAsState()
val scrollBehavior = TopAppBarDefaults
.pinnedScrollBehavior(rememberTopAppBarState())
val openDialog = remember { mutableStateOf(true) }
Scaffold(modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
CenterAlignedTopAppBar(
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Black,
titleContentColor = Color.White
),
title = {
Text(
"TO DO LISTS",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
actions = {
IconButton(onClick = { dialogBox(todoViewModel,openDialog) }){
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "You have to add new todo work",
tint = Color.White
)
}
},
scrollBehavior = scrollBehavior
)
}
){
Column(modifier= modifier.padding(it)) {
LazyColumn {
items(dataList){list ->
todoItems( data = list)
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun dialogBox(todoViewModel: todoViewModel, openDialog: MutableState<Boolean>){
var title by rememberSaveable{mutableStateOf("")}
var description by rememberSaveable{mutableStateOf("")}
Dialog(onDismissRequest = {openDialog.value = false}){
Card(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.padding(16.dp),
shape = RoundedCornerShape(16.dp)
){
Column(modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
OutlinedTextField(value = title,
onValueChange = { title = it },
label = { Text("Title") })
Spacer(modifier = Modifier.padding(vertical = 12.dp))
OutlinedTextField(value = description,
onValueChange = { description = it },
label = { Text("Description") })
Row(modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center) {
TextButton(onClick = {
todoViewModel.insertTodo(todoEntity(title = title, desciription = description))},
modifier = Modifier.padding(8.dp)){
Text("Save")
}
TextButton(onClick = {}, modifier = Modifier.padding(8.dp)){
Text("Dismiss")
}
}
}
}
}
}
我已经搜索了有关它的文章,但没有得到足够的文章可以更好地说明。
通过将整个
ViewModel
对象传递到可重用可组合项中,您可以将可重用可组合项紧密耦合到 ViewModel
对象,这会阻止您在其他地方轻松使用它,或者在不需要存根 ViewModel
的情况下测试它
待用。
“Compose 和其他库”文档的 ViewModel 部分也指出了这一点:
注意:由于其生命周期和作用域,您应该在屏幕级可组合项(即,靠近从导航图的活动、片段或目标调用的根可组合项)访问和调用 ViewModel 实例。 您永远不应该将 ViewModel 实例传递给其他可组合项,只传递它们所需的数据以及执行所需逻辑的函数作为参数。
因此,仅将您需要的内容从 ViewModel 传递到您的
dialogBox
可组合项(您还应该最好使用 PascalCase
作为方法名称,作为一个小挑剔)。据我所知,您只使用 insertTodo
中的 ViewModel
方法 - 这可以简化为 onSaveClick
lambda,然后您可以在使用此 的地方调用
insertTodo
DialogBox
可组合。
当您可以在父可组合项中执行条件渲染时,也无需直接传递状态来处理条件渲染:
@Composable
fun DialogBox(..., openDialog: MutableState<Boolean>) { ... }
// VS
@Composable
fun DialogBox(...) { ... }
@Composable
fun HomeScreen(...) {
if (openDialog.value) {
DialogBox(...)
}
}
无论如何,更新后的代码如下所示:
// "dialogBox" is too vague, I would probably name it something more
// specific like "NewTodoDialog"
@Composable
fun NewTodoDialog(
onDismissRequest: () -> Unit,
// Pass back the data here...
// You could maybe make the data be contained in a data class,
// something like "TodoItem" or similar
onSaveClick: (title: String, description: String) -> Unit
) {
var title by rememberSaveable { mutableStateOf(...) }
var description by rememberSaveable { mutableStateOf(...) }
Dialog(onDismissRequest = onDismissRequest) {
// Dialog content...
// ...
// Dialog actions:
Row(/* ... */) {
TextButton(
// You can pass back the data to be added here:
onClick = { onSaveClick(title, description) },
modifier = Modifier.padding(8.dp)
) {
Text("Save")
}
// I've also updated the onClick of your dismiss button to _actually_
// do something, like say request for the dialog to be dismissed
TextButton(
onClick = onDismissRequest,
modifier = Modifier.padding(8.dp)
) {
Text("Dismiss")
}
}
}
}
// In the parent composable:
@Composable
fun HomeScreen(viewModel: YourViewModel, /* ... */) {
var isDialogShown by rememberSaveable { mutableStateOf(false) }
// Then you can conditionally render the dialog
if (isDialogShown) {
NewTodoDialog(
// This would actually dismiss the dialog
onDismissRequest = { isDialogShown = false },
// Method references are more ideal here if the method + lambda
// definitions are the same.
// Otherwise, you'll have to call it manually in a lambda
onSaveClick = viewModel::insertTodo
)
}
}
来自 ViewModels 最佳实践,简而言之:
不要将 ViewModel 传递给其他类、函数或其他 UI 组件。因为平台管理它们,所以您应该让它们尽可能靠近平台。接近您的 Activity、片段或屏幕级可组合函数。这可以防止较低级别的组件访问超出其需要的数据和逻辑。