I have SetupActivity and there in it’s viewModel I fetch data and then that data I need to pass it to MainActivity and it’s Fragment’s ViewModels , I am wondering instead of passing it is it possible to inject dynamically that settingsData in ViewModel's constructor's
settingsViewModel.settingsMutableData.observe(this, Observer {settingsData->
startActivity(MainActivity.getStartInent(this,settingsData))
})
#IntoMap
#ViewModelKey(PageViewModel::class)
abstract fun bindHomeViewModel(pageViewModel: PageViewModel): ViewModel```
Here is one of my ViewModel
```class PageViewModel #Inject constructor( homeRepository: HomeRepository) : ViewModel(), Injectable {
}```
Related
I have a word quiz app where there are different word games (multiple choice, spelling, match the words, etc). I have created a ParentViewModel to keep the common things, such as getting words from Room database, current question variable, etc. But, on the other hand, as each game has different aspects. They have their own viewmodels. I want to be able to use functions and variables from ParentViewModel in child view models. My question is if I am correctly injecting the ParentViewModel below or if there is another way to do this?
ParentViewModel:
#HiltViewModel
class ParentViewModel #Inject constructor(
private val wordRepository: WordRepository
): ViewModel(){
// Common things such as getting words from database.
}
Child view model (Different view model for each type of game):
#HiltViewModel
class SpellingViewModel #Inject constructor(
practiceViewModel: PracticeViewModel
): ViewModel(){
// Functions and varibles spesific to the current game.
}
Appmodule:
#Provides
#Singleton
fun provideParentViewModel(wordRepository: WordRepository): ParentViewModel {
return ParentViewModel(wordRepository)
}
I think a baseViewModel should be created and inherit child classes from the parent class, You can create open functions that are in the parent class and use them in the child classes.
Let me give you an example :
abstract class BaseViewModel : ViewModel(){open fun wordGameOne(){
// do something
}
open fun wordGameTwo(){
// do something
}
}
And in the child class, you can override
#HiltViewModel class GameOneViewModel #Inject constructor(
private val wordRepository: WordRepository) : BaseViewModel() {
override fun wordGameOne(){
// do something
}
}
I'm trying to inject a singleton class that was defined in a hiltmodule inside a composable.
I know how to inject viewmodels but what about singleton classes ?
#Inject
lateinit var mysingleton: MySingletonClass
This code works fine in an activity but carrying it around from the activity to the composable that uses it is a long way ...
Any better solution ?
You cannot inject dependencies into a function, which is what a #Composable is. #Composable functions don't have dependencies, but can get values returned by Hilt functions, like hiltViewModel().
If you need access to a ViewModel-scoped (or Application-scoped) singleton inside a #Composable, you can have that singleton injected into the ViewModel, and then access the ViewModel from the #Composable.
You can inject that singleton into the ViewModel by annotating the provider function for that object in the ViewModel hilt module as #ViewScoped.
You could install the provider into the SingletonComponent::class and annotate it as #Singleton, if you want a singleton for the whole app, instead of a singleton per ViewModel instance. More info here.
Hilt module file
#Module
#InstallIn(ViewModelComponent::class)
object ViewModelModule {
#ViewScoped
#Provides
fun provideMySingleton(): MySingletonClass = MySingletonClass()
}
Your ViewModel class:
#HiltViewModel
class MyViewModel
#Inject constructor(
val mySingleton: MySingletonClass
): ViewModel() {
...
}
Your #Composable function:
#Composable fun DisplayPrettyScreen() {
...
val viewModel: MyViewModel = hiltViewModel()
val singleton = viewModel.mySingleton //no need to assign it to a local variable, just for explanation purposes
}
I also thought that is not possible but then found way... tried it and seems it works.
define you entry point interface:
private lateinit var dataStoreEntryPoint: DataStoreEntryPoint
#Composable
fun requireDataStoreEntryPoint(): DataStoreEntryPoint {
if (!::dataStoreEntryPoint.isInitialized) {
dataStoreEntryPoint =
EntryPoints.get(
LocalContext.current.applicationContext,
DataStoreEntryPoint::class.java,
)
}
return dataStoreEntryPoint
}
#EntryPoint
#InstallIn(SingletonComponent::class)
interface DataStoreEntryPoint {
val dataStoreRepo: DataStoreRepo
}
DataStoreRepo is singleton defined in Hilt
#Singleton
#Provides
fun provideDataStoreRepository(dataStore: DataStore<Preferences>): DataStoreRepo =
DataStoreRepo(dataStore)
and then use in composable:
#Composable
fun ComposableFuncionName(dataStoreRepo: DataStoreRepo = requireDataStoreEntryPoint().dataStoreRepo){
...
}
I have three class needing to share a dependency. The latter is initialisated by one of them.The SettingsViewModel contains the data to initialize the dependency and it need to be deleted at the end of the activity. NetworkViewModel and TimeViewModel use it as an interface since the dependancy is an interface with the logic to handle Bluetooth.
SettingsViewModel -->(initialize) SingletonDependency.
NetworkViewModel --> (use).
TimeViewModel --> (use).
How can I make Hilt (or manual) injection to use the same interface? If I understand well I can't use singleton here since I need to iniatilize the dependency when the activity start.
If we consider that your class name is SomeClass you can provide a live data of this class like this:
#Module
#InstallIn(SingletonComponent::class)
object SingeltonModule {
#Provides
#Singleton
fun provideSomeClassLiveData(): MutableLiveData<SomeClass> {
return MutableLiveData<SomeClass>()
}
}
in your SettingsViewModel do this:
#HiltViewModel
class SettingsViewModel #Inject constructor(
val SomeClassLiveData: MutableLiveData<SomeClass>
) : ViewModel() {
init{
someClassLiveData.value = SomeClass()
}
}
and in other view models you can inject this to contractors and observe it:
#HiltViewModel
class NetworkViewModel #Inject constructor(
val SomeClassLiveData: MutableLiveData<SomeClass>
) : ViewModel() {
init{
someClassLiveData.observeForEver{
//do what you want with value
}
}
}
I have a Fragment flow scoped with a navigation graph and want to scope each Fragment's ViewModel accordingly. However, I don't want each of the Fragments to have access to all methods and variables in the ViewModel, therefore each Fragment's ViewModel is an interface implemented in the base ViewModel.
I am using by navGraphViewModels() delegation in each of the Fragments but it seems to be unable to cast the interface to the base class.
The trace error is:
java.lang.ClassCastException: java.lang.Object cannot be cast to
androidx.lifecycle.ViewModel
Any advice on how to approach this problem??
In my Fragment it is defined as follows:
#AndroidEntryPoint
class ExampleFragment : Fragment() {
private val viewModel: ExampleViewModelController by
navGraphViewModels(R.id.nav_graph_example){defaultViewModelProviderFactory}
///
And the ViewModel is defined by:
#HiltViewModel
class ExampleViewModel #Inject constructor(
private val handle: SavedStateHandle,
private val useCases: ExampleUseCases,
) : ViewModel(), ExampleViewModelController {
override fun validateExampleInputs() {
// TODO("Not yet implemented")
}
}
And lastly, the interface:
interface ExampleViewModelController {
fun validateExampleInputs()
}
The ClassCastException happens because there's no type parameter passed to the delegate like by navGraphViewModels<ExampleViewModel>(). Thus, the delegate is wrongly trying to create a new instance of the interface ExampleViewModelController instead of ExampleViewModel.
I am using Dagger 2 with Kotlin. I`ve provided a viewmodel(CarViewModel) injection on the following way. Everywhere is written that it has to be done via ViewModelFactory injection. I want to ask if my way to directly inject viewmodel is correct? Here is my AppModule:
#Module
class AppModule {
#Provides
#Singleton
fun getContext(application: Application): Context = application.applicationContext
#Provides
#Singleton
fun getDb(context: Context): MyDatabase = MyDatabase.getInstance(context)
#Provides
fun injectViewModel(application: Application): CarViewModel=
ViewModelProvider.AndroidViewModelFactory.getInstance(application).create(CarViewModel::class.java)
}
Than injected in activity:
#Inject
lateinit var carViewModel: CarViewModel
No, your way of injecting a view model is not quite correct. The main reason is that the view model isn't associated with an activity, so it won't be reused when the activity is recreated, instead it will be recreated as well. The proper way of creating a view model is using ViewModelProvider, not the ViewModelProvider.Factory directly:
// `this` refers to the activity or fragment
viewModel = ViewModelProviders.of(this)[MyViewModel::class.java]
or:
viewModel = ViewModelProvider(this)[MyViewModel::class.java]
if you use the newest alpha version (ViewModelProviders.of() is deprecated).
If you want to inject a view model and also be able to inject into view model (using constructor injection), you have to create your implementation of the ViewModelProvider.Factory interface and use it to create view models with non-empty constructors.
you only have to inject viewmodel factory & bind viewmodel and then you can obtain view model via viewmodel factory.
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
val carViewModel: CarViewModel by viewModels{viewModelFactory}