OhHiMark
OhHiMark

Reputation: 55

How to inject viewmodel factory by dagger 2

Trying to create a view model in a fragment by providing the factory with a dagger but app crashes with error. Move injection to another lifecycle doesn't work

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bigproject.planyourlife, PID: 13934
kotlin.UninitializedPropertyAccessException: lateinit property factory has not been initialized
    at com.bigproject.planyourlife.view.SignupFragment.getFactory(SignupFragment.kt:27)

ViewModel

class SignupViewModel(private val service: AuthService) : ViewModel() {
private var email: String = ""
private var password: String = ""
private val _uiState = MutableStateFlow<SignupState>(SignupState.Success(null))
val uiState: StateFlow<SignupState> = _uiState

@Suppress("UNCHECKED_CAST")
class Factory @Inject constructor(
    private val service: AuthService
) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        require(modelClass == SignupViewModel::class)
        return SignupViewModel(service) as T
    }
}

Fragment

class SignupFragment : Fragment(R.layout.signup_page) {

    private val binding by viewBinding(SignupPageBinding::bind)
    private val signupViewModel: SignupViewModel by viewModels {
        factory
    }

    @Inject
    lateinit var factory: SignupViewModel.Factory

    override fun onAttach(context: Context) {
        context.appComponent.inject(this)
        super.onAttach(context)
    }
}

Upvotes: 1

Views: 5229

Answers (1)

Dương Minh
Dương Minh

Reputation: 2421

First, create a module for your ViewModelFactory. This is important for you to provide ViewModelFactory for dagger.

ViewModelModule.kt

@Module
abstract class ViewModelModule {

    @Binds
    abstract fun provideViewModelFactory(factory: Factory): ViewModelProvider.Factory

    // Binds your viewmodel in here.
}

Next, you need to include that module in your component. Specifically here is AppComponent.

AppComponent.kt

@Singleton
@Component(modules = [ViewModelModule::class, YourFragmentModule::class, YourActivityModule::class, ...])
interface AppComponent {

    @Component.Factory
    abstract class Factory : AndroidInjector.Factory<YourApplication>
}

And now, viewModelFactory is ready for you to inject into your Fragment, Activity, ....

YourFragment.kt

class YourFragment: Fragment() {

    @Inject
    lateinit var viewModelFactory: Factory
}

Upvotes: 1

Related Questions