I've a composable called ParentScreen and a ViewModel named ParentViewModel. Inside the ParentViewModel, I am collecting a value from my 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)
}
Inside the ParentScreen composable, I have a ChildWidget composable and it has its own ViewModel named 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
}
Now, I want to get parentData inside ChildViewModel's constructor.
Questions
- How do I supply
ChildViewModelFactoryto Navigation Compose'shiltViewModelmethod? - If that's not possible, what would be the most suitable method to inject an object from the parent composable to the child composable's
ViewModel? How about creating alateinitproperty andinitmethod like below?
@HiltViewModel
class ChildViewModel @Inject constructor(
) : ViewModel() {
lateinit var parentData: Long
fun init(parentData: Long){
if(this::parentData.isInitialized) return
this.parentData = parentData
}
}
You can do this using
EntryPointAccessors(from Hilt) and aViewModelProvider.Factoryfrom View Model library.In my sample app,
BookFormScreenis usingBookFormViewModeland the view model needs to load a book based on abookIdpassed by the previous screen. This is what I did:Notice that I'm not using
@HiltViewModel. TheprovideFactorywill be use to supply a factory to create this view model.Then, define the
ViewModelFactoryProviderfor the entry point:Now, you need to define a composable function to provide the view model using this factory.
If you're using the navigation library, you can add the
ViewModelStoreOwnerparameter in this function and use it inviewModel()function call. For this parameter, you can pass theNavBackStackEntryobject, with this, the view model will be scoped to that particular back stack entry.Finally, you can use your view model in your composable.