0

In short: How can I inject a ViewModelProvider.Factory into my Fragment to get access to the appropriate ViewModel?

I have an activity with tabs managed by a BottomNavigationBar. Each tab screen is represented by a Fragment. Switching between tabs is done by replacing Fragments.

Each TabFragment has an associated ViewModel class. These ViewModel classes take arguments in the constructor, and for this reason I have to create a ViewModelProvider.Factory to use ViewModelProvider.

Naturally, my first instinct would be to inject this Factory into the fragment upon construction, and then retrieve a ViewModel instance via the ViewModelProvider:

class TabA(private val viewModelFactory: ViewModelProvider.Factory) : Fragment() {
    ...
    tabAViewModel = ViewModelProvider(this, viewModelFactory)
            .get(TabAViewmodel::class.java)
    ...
}

Fragments, however, require no-arguments constructors. Instead instantiation can be done through a static factory method and simple arguments can be supplied through a Bundle, but it seems that this Bundle cannot contain complex types such as a ViewModelFactory.

How can I then inject my ViewModelFactory into the Fragment? Is dependency injection of custom types even possible for fragments?

Magnus
  • 25
  • 6

1 Answers1

1

If you want to use a Fragment with constructor arguments you can use FragmentFactory - it is no longer a requirement to have no-arg constructors on fragments.

  1. Create the injectable factory :

     class MyFragmentFactory @Inject constructor(private val viewModelFactoryTabA: ViewModelProvider.Factory) : FragmentFactory() {
    
     override fun instantiate(classLoader: ClassLoader, className: String): Fragment =
         when (loadFragmentClass(classLoader, className)) {
             TabA::class.java     -> TabA(viewModelFactoryTabA)
             else                 -> super.instantiate(classLoader, className)
    
         }
    

    }

  2. Set the factory in parent Activity :

     class MyActivity : AppCompatActivity() {
    
     @Inject lateinit var fragmentFactory: MyFragmentFactory
    
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         supportFragmentManager.fragmentFactory = fragmentFactory
     }
    

    }

More info in the documentation : https://developer.android.com/reference/androidx/fragment/app/FragmentFactory

Mark
  • 9,604
  • 5
  • 36
  • 64