【问题标题】:How to do Assisted Injection with Navigation Compose?如何使用 Navigation Compose 进行辅助注入?
【发布时间】:2022-05-02 22:43:13
【问题描述】:

我有一个名为 ParentScreen 的组合和一个名为 ParentViewModelViewModel。在 ParentViewModel 中,我正在从我的 repo 中收集一个值。

class MyRepo @Inject constructor() {
    fun getParentData() = System.currentTimeMillis().toString() // some dummy value
}

@HiltViewModel
class ParentViewModel @Inject constructor(
    myRepo: MyRepo
) : ViewModel() {
    private val _parentData = MutableStateFlow("")
    val parentData = _parentData.asStateFlow()

    init {
        val realData = myRepo.getParentData()
        _parentData.value = realData
    }
}

@Composable
fun ParentScreen(
    parentViewModel: ParentViewModel = hiltViewModel()
) {
    val parentData by parentViewModel.parentData.collectAsState()
    ChildWidget(parentData = parentData)
}

ParentScreen 可组合对象中,我有一个ChildWidget 可组合对象,它有自己的ViewModel,名为ChildViewModel

@HiltViewModel
class ChildViewModel @AssistedInject constructor(
    @Assisted val parentData: String
) : ViewModel() {

    @AssistedFactory
    interface ChildViewModelFactory {
        fun create(parentData: String): ChildViewModel
    }

    init {
        Timber.d("Child says data is $parentData ")
    }
}

@Composable
fun ChildWidget(
    parentData: String,
    childViewModel: ChildViewModel = hiltViewModel() // How do I supply assisted injection factory here?
) {
    // Code omitted
}

现在,我想在ChildViewModel 的构造函数中获取parentData

问题

  • 如何将 ChildViewModelFactory 提供给 Navigation Compose 的 hiltViewModel 方法?
  • 如果这不可能,那么将对象从父可组合注入到子可组合的ViewModel 的最合适方法是什么?像下面这样创建一个lateinit 属性和init 方法怎么样?
@HiltViewModel
class ChildViewModel @Inject constructor(
) : ViewModel() {
    lateinit var parentData: Long

    fun init(parentData: Long){
        if(this::parentData.isInitialized) return
        this.parentData = parentData
    }
}

【问题讨论】:

  • 我真的很想从未来的答案中学习。就个人而言,我在最近的项目中从 Compose 开始,我发现将一个 ViewModel 保留在父级要容易得多。如果子可组合中有任何数据并希望将其打包传递给父级,我只需使用 Unit 类型参数返回相同的数据。我不确定,如果我的方式是正确的,我很想看到答案。
  • @Abdullah 在这种情况下,您可以对此问题进行投票并关注 以获取更新,评论不会订阅您的答案。
  • 错过了????.

标签: android kotlin android-jetpack-compose dagger-hilt navigation-compose


【解决方案1】:

您可以使用 EntryPointAccessors(来自 Hilt)和来自 View Model 库的 ViewModelProvider.Factory 来执行此操作。

在我的sample app 中,BookFormScreen 正在使用BookFormViewModel,并且视图模型需要根据前一个屏幕传递的bookId 加载一本书。这就是我所做的:

class BookFormViewModel @AssistedInject constructor(
    ...
    @Assisted private val bookId: String?,
) : ViewModel() {

    ...

    @AssistedFactory
    interface Factory {
        fun create(bookId: String?): BookFormViewModel
    }

    companion object {
        @Suppress("UNCHECKED_CAST")
        fun provideFactory(
            assistedFactory: Factory, // this is the Factory interface 
                                      // declared above
            bookId: String?
        ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return assistedFactory.create(bookId) as T
            }
        }
    }
}

请注意,我没有使用@HiltViewModelprovideFactory 将用于提供工厂来创建此视图模型。

现在,您需要定义一个可组合函数来使用该工厂提供视图模型。

@Composable
fun bookFormViewModel(bookId: String?): BookFormViewModel {
    val factory = EntryPointAccessors.fromActivity(
        LocalContext.current as Activity,
        ViewModelFactoryProvider::class.java
    ).bookFormViewModelFactory()

    return viewModel(factory = BookFormViewModel.provideFactory(factory, bookId))
}

如果使用导航库,可以在该函数中添加ViewModelStoreOwner参数,在viewModel()函数调用中使用。对于此参数,您可以传递 NavBackStackEntry 对象,这样,视图模型将被限定为该特定的返回堆栈条目。

最后,您可以在可组合物中使用您的视图模型。

val bookFormViewModel: BookFormViewModel = bookFormViewModel(bookId)

【讨论】:

    猜你喜欢
    • 2020-11-01
    • 2016-09-14
    • 1970-01-01
    • 2014-05-09
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    相关资源
    最近更新 更多