提问人:King 提问时间:11/12/2023 更新时间:11/12/2023 访问量:49
我们可以将 viewmodel 作为参数传递给另一个 compose 函数吗?
Can we pass viewmodel as parameter to another compose function?
问:
我有点困惑。我们可以将 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 实例的生命周期和范围,您应该在屏幕级可组合项(即接近从 Activity 、Fragment 或 Navigation 图的目标调用的根可组合项)访问和调用 ViewModel 实例。切勿将 ViewModel 实例传递给其他可组合项,而应仅传递它们所需的数据和执行所需逻辑的函数作为参数。
因此,只需将 ViewModel 中需要的内容传递给可组合项(最好还应该用于方法名称,作为次要的吹毛求疵)。据我所知,你只使用你的方法 - 这可以简化为一个 lambda,然后你可以在你使用这个可组合物的地方调用它。dialogBox
PascalCase
insertTodo
ViewModel
onSaveClick
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、片段或屏幕级可组合函数。这样可以防止较低级别的组件访问超出其需要的数据和逻辑。
评论