0

I want to provide viewmodel through dagger, So I was using a ViewModelProviderFactory which has the map multibinding. Here is the code for ViewModelProviderFactory.kt:

@Singleton
class ViewModelProviderFactory @Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T = viewModels[modelClass]?.get() as T
}

And this is my ViewModelFactoryModule.kt :

@Module
abstract class ViewModelFactoryModule {

    @Binds
    abstract fun bindViewModelFactory(providerFactory: ViewModelProviderFactory) : ViewModelProvider.Factory

}

This is my ViewModelKey.kt :

@Documented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value : KClass<out ViewModel>) {

}

I want to add AuthViewModel to my activity named Login, here is my AuthViewModelModule.kt :

@Module
abstract class AuthViewModelModule {
    @Binds
    @IntoMap
    @ViewModelKey(AuthViewModel::class)
    abstract fun bindAuthViewModel(viewModel : AuthViewModel) : ViewModel
}

and this is Login.kt :

class Login : AppCompatActivity() {
    @Inject
    lateinit var providerFactory : ViewModelProviderFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val api = Api.instance

        val repository = UserRepository(api)

        val factory = AuthViewModelFactory(repository)

        val binding: ActivityLoginBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_login)

        val viewModel = ViewModelProvider(this, providerFactory).get(AuthViewModel::class.java)

        binding.viewmodel = viewModel

    }

}

This is my AppComponent.kt

@Singleton
@Component(
    modules = [
        AndroidSupportInjectionModule::class,
        ActivityBuildersModule::class,
        AppModule::class,
        ViewModelFactoryModule::class
    ]
)
interface AppComponent : AndroidInjector<BaseApplication> {

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application) : Builder

        fun build() : AppComponent
    }
}

And my ActivityBuildersModule.kt :

@Module
abstract class ActivityBuildersModule {

    @ContributesAndroidInjector(
        modules = [AuthViewModelModule::class]
    )
    abstract fun contributeLogin() : Login
}

Here is my AppModule.kt :

@Module
class AppModule {
    @Module
    companion object
    {
        @Provides
        @JvmStatic
        fun providesAppNull(application: Application) : Boolean = application == null
        }
    }

}

This is my AuthViewModel :

class AuthViewModel @Inject constructor(
) : ViewModel() {
    var name: String? = null
    var email: String? = null
    var password: String? = null
    var password_confirm: String? = null

    var authListener: AuthListener? = null


    fun onSignUpButtonClicked(view: View) {
        authListener?.onStarted()
        val mContext = view.context

        if (name.isNullOrEmpty() || email.isNullOrEmpty() || password.isNullOrEmpty() || password_confirm.isNullOrEmpty()) {
            authListener?.onFailure("Invalid email or password!")
            return
        } else if (!password.equals(password_confirm)) {
            authListener?.onFailure("Passwords do not match!!!")
            return
        } else {
            Log.d("AuthViewModel", "doing something")
        }
    }
}

Instead injecting all of these I'm getting error for not providing, this is the error I'm getting right now:

java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.kreeti.gogal.viewmodels.ViewModelProviderFactory(viewModels)
com.kreeti.gogal.viewmodels.ViewModelProviderFactory is injected at
com.kreeti.gogal.di.ViewModelFactoryModule.bindViewModelFactory(providerFactory)
androidx.lifecycle.ViewModelProvider.Factory is injected at
com.kreeti.gogal.ui.auth.Login.providerFactory
com.kreeti.gogal.ui.auth.Login is injected at
SuvodipMondal
  • 87
  • 1
  • 8

2 Answers2

0

You should inject the constructer of the viewModel class Example: @Inject public AuthViewModel(){} It should work fine.

-1

Just convert this code to kotlin then replace the original one

import java.util.Map; import javax.inject.Inject;import javax.inject.Provider;import javax.inject.Singleton; import androidx.lifecycle.ViewModel;import androidx.lifecycle.ViewModelProvider; public class ViewModelProviderFactory implements ViewModelProvider.Factory { private static final String TAG = "ViewModelProviderFactor"; private final Map, Provider> creators; @Inject public ViewModelProviderFactory(Map, Provider> creators) { this.creators = creators; } @Override public T create(Class modelClass) { Provider creator = creators.get(modelClass); if (creator == null) { // if the viewmodel has not been created // loop through the allowable keys (aka allowed classes with the @ViewModelKey) for (Map.Entry, Provider> entry : creators.entrySet()) { // if it's allowed, set the Provider if (modelClass.isAssignableFrom(entry.getKey())) { creator = entry.getValue(); break; } } } // if this is not one of the allowed keys, throw exception if (creator == null) { throw new IllegalArgumentException("unknown model class " + modelClass); } // return the Provider try { return (T) creator.get(); } catch (Exception e) { throw new RuntimeException(e); } }}

  • This code is not formatted and unreadable. Please take some time to format your code when posting in an answer. Also, the question asks for Kotlin, not Java. – Kevin Feb 02 '20 at 14:51