I'm new to Hilt. Trying to convert from Dagger to Hilt. But I'm stuck on injecting NavController to the Activity.
So to create NavController instance I need to have access to the activity and the host fragment Id. In Dagger this was my approach to create that instance.
First, create ActivityModule
@Module
class ActivityModule {
@ActivityScope
@Provides
fun navigation(activity: MainActivity, hosFragment: Int) =
Navigation.findNavController(activity,hosFragment)
}
In the second step ActivitySubComponent has the ability to take activity and host fragment id as parameters.
@ActivityScope
@Subcomponent(modules = [ActivityModule::class])
interface ActivitySubComponent {
@Subcomponent.Factory
interface Factory{
fun create(@BindsInstance activity: MainActivity,
@BindsInstance hostFragment: Int): ActivitySubComponent
}
fun inject(MainActivity: MainActivity)
}
After adding this subcomponent with other subcomponents to the ApplicationComponent from you can get the instance of NavController to the fragment like below.
class MainActivity : AppCompatActivity() {
@Inject
lateinit var navigationController: NavController
lateinit var viewBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
injector.activityComponent().create(this, R.id.hostFragment).inject(this)
}
}
injector coming from the following class.
interface InjectorProvider {
val component: ApplicationComponent
}
val Activity.injector get() = (application as InjectorProvider).component
val Fragment.injector get() = (requireActivity().application as InjectorProvider).component
When I was trying to transition into Hilt, the first issue I faced was how to provide parameters to the module. Not like in Dagger I could not find an alternative for Subcomponent Factory. Is it even possible to provide parameters to a module on Hilt?
Anyways, I decided to provide the FragmentContainerView id to the NavController manually since it's not changing. With that, I came to the below solution in Hilt.
@Module
@InstallIn(ActivityComponent::class)
object NavigationModule {
@Provides
fun navigationController(activity: Activity) =
activity.findNavController(R.id.hostFragment)
}
And tried to inject it into my MainActvity like below.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var navigationController: NavController
lateinit var viewBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
initialization()
}
private fun initialization() {
setSupportActionBar(viewBinding.toolbar)
NavigationUI.setupActionBarWithNavController(this, navigationController)
}
}
Application built successfully but now I'm getting a run time error saying "ID does not reference a View inside this Activity" but my activity layout is constructed like below.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/purple_700"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/hostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/main_navigation" />
</LinearLayout>
Error pointing at super.onCreate(savedInstanceState) line on the MainActivity. Please let me know what am I missing here. Thanks.