如何在Jetpack Compose中将ID和应用程序传递给viewModel/viewModelFactory?

9

我使用 Jetpack Compose 和 Navigation Compose,我想将 id 传递给这个 viewmodel

class RecallViewModel(private val id:Long,application: Application):AndroidViewModel(application) {
  ............................
}

可组合函数:

我不知道如何在可组合函数中获取 application

@Composable
fun RecallScreen(
    id:Long,
    onEnd:() -> Unit
){
       val recallViewModel = viewModel(factory = RecallViewModelFactory(
id = id,application = "i don't know how to get application"))

}

和工厂

class RecallViewModelFactory(private val id:Long,val application: Application):ViewModelProvider.AndroidViewModelFactory(application) {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return RecallViewModel(id,application) as T
    }
}


        composable(
            "${Routes.recall}/{id}",
            arguments = listOf(navArgument("id") { type = NavType.LongType })
        ) {
            RecallScreen(
                id = it.arguments!!.getLong("id"),
                onEnd = {navController.navigateUp()}
            )
        }


1
你是否将你的 id 作为 navArgument 传递到你的屏幕中,作为你的路由的一部分?如果是这样,它已经通过默认支持的 SavedStateHandle 可用于你的 ViewModel。你尝试手动构建工厂的特定原因是什么? - ianhanniballake
我该如何做到这一点,这是一个好的实践方法吗? - Omar Khaled
你能否包含你在RecallScreen中使用的composable目标吗? - ianhanniballake
1个回答

42
回答您的问题:您可以从LocalContext对象中检索Application
val context = LocalContext.current
val application = context.applicationContext as Application

然而,使用 Navigation Compose 时,您无需手动向 ViewModel 传递任何参数。相反,您可以利用内置支持的 ViewModel 中的 SavedState 并将 SavedStateHandle 参数添加到您的 ViewModel中。 SavedStateHandle 是一个键值映射,其中包含自动填充目标的参数。

这意味着您的 ViewModel 变为:

class RecallViewModel(
    application: Application,
    savedStateHandle: SavedStateHandle
):AndroidViewModel(application) {

  // Get your argument from the SavedStateHandle
  private val id: Long = savedStateHandle.get("id")

  ............................
}

现在您不再需要手动解析参数中的ID或将其传递给ViewModel:

composable(
    "${Routes.recall}/{id}",
    arguments = listOf(navArgument("id") { type = NavType.LongType })
) {
    RecallScreen(
        onEnd = {navController.navigateUp()}
    )
}
@Composable
fun RecallScreen(
    onEnd:() -> Unit
) {
    val recallViewModel: RecallViewModel = viewModel()
}

笔误 savedStateHandle, SavedStateHandle - Raw Hasan
@RawHasan - 很好的发现,是的,我已将其更正为savedStateHandle: SavedStateHandle(变量名为savedStateHandle,类型为SavedStateHandle)。 - ianhanniballake
@ianhanniballake 把视图模型的初始化放在可组合函数中是最佳实践吗?(就像你所做的那样)还是将实例作为参数传递?(从活动中)或者将视图模型作为参数,但在参数中进行初始化/注入?(@Composable fun RecallScreen(viewModel: RecallViewModel = viewModel()/hiltViewModel()) - Dr.jacky
1
@Dr.jacky - 将其作为参数传递使得在预览和测试中更容易使用该组件,因此这似乎是一个好主意。此答案中使用的样式只是与问题中的样式匹配,以提供解决此特定问题所需的最小更改集。 - ianhanniballake
@ianhanniballake 我明白了。我在某个地方读到过建议不要让你的可组合依赖于ViewModel,而是将最终结果传递给它,但正如你所说,Google也是这样做的(https://developer.android.com/jetpack/compose/state#viewmodel-state)。但是这样做,难道不会在每次重新组合(帧刷新,即使方向没有改变或输入没有改变,可组合仍可能被调用多次)时初始化ViewModel并调用相关方法(例如调用网络或db)吗? - Dr.jacky
1
如果我们在 savedStateHandle[] 中导航到前面并返回,数据是否持续存在? - Cyph3rCod3r

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接