Dagger Hilt: Scope dependencies for parent-/child-fragments
Asked Answered
M

2

8

I am trying to find a solution in how to define Hilt in a certain fragment related scenario. I have the following setup:

  • Activity
    • Parent Fragment 1
      • Child Fragment 1
      • Child Fragment 2
      • ...
      • Child Fragment n-1
    • Parent Fragment 2
      • Child Fragment 1
      • Child Fragment 2
      • ...
      • Child Fragment n-1

Parent Fragment 1 is using the dependency A. The instance of that dependency is something I want to share only between that parent fragment and all of its child fragments. The parent fragment 2 + its child fragments should use a different instance than the parent fragment 1 + children. Generally his structure should have only two instances of any given dependency - one for the first flow and one for the second.

I can see that a custom scope might work here but I am uncertain about how to use that in regards to Hilt.

Memoried answered 29/4, 2021 at 11:24 Comment(0)
F
0

One way of achieving your goal would be by leveraging Hilt's default bindings (in this case, Fragment) and dagger Qualifier annotation.

In your first parent fragment you have your fragment scoped and qualified binding like

@AndroidEntryPoint
class ParentFragment1 : Fragment {

  @Inject
  @ParentFragment1
  lateinit var binding: Binding
}

and in your ChildFragment1, you have the Binding which is not qualified and we'll provide a way for dagger to inject appropriate binding depending on the fragment argument we'll pass to fragment bundle

@AndroidEntryPoint
class ChildFragment1 : Fragment {

  @Inject
  lateinit var binding: Binding
}

In your dagger module you can specify which qualified bidning to provide for ChildFragment1 depending on the arguments we pass to bundle during ChildFragment1 creation.

@InstallIn(FragmentComponent::class)
@Module
class FragmentModule {
 
  @Provides
  fun provieBinding(
     fragment:Fragment, // default binding in FragmentComponent
     @ParentFragment1 bindingOne: Binding,
     @ParentFragment2 bindingTwo: Binding,
  ): Binding {
    retrun when(fragment.arguments.getParcelable(KEY)){
       is FRAGMENT1 -> bindingOne
       is FRAGMENT2 -> bindingTwo
    }
  }

  @Provides
  @ParentFragment1
  fun providesParentFragment1QualifiedBinding(): Binding {
      return Binding()
  }

  @Provides
  @ParentFragment2
  fun providesParentFragment1QualifiedBinding(): Binding {
      return Binding()
  }

   enum class ContentState {
        FRAGMENT1,
        FRAGMENT2,
   }


   @Qualifier
   @Retention(AnnotationRetention.RUNTIME)
   annotation class ParentFragment1

   @Qualifier
   @Retention(AnnotationRetention.RUNTIME)
   annotation class ParentFragment2
}
Foxed answered 22/2, 2023 at 0:10 Comment(0)
R
-4

You can do this using dagger-hilt by adding @AndroidEntryPoint in each parent Fragment and their child's view. Then your Parent Fragment 1 and Parent Fragment 2 will have different fragment scope with different instances as per the dagger-hilt explanation. See below note from dagger-hilt:

A common misconception is that all fragment instances will share the same instance of a binding scoped with @FragmentScoped. However, this is not true. Each fragment instance gets a new instance of the fragment component, and thus a new instance of all its scoped bindings.

Also, see below dagger graph and the scopes hierarchy:

enter image description here

More details in their documentation.

Also, Don't use custom scopes until it's really necessary since it has performance and overhead issues.

Risinger answered 29/4, 2021 at 16:29 Comment(5)
Ok thanks. But that would only be regarding the parent fragment, right? I want the child fragments to share the same instance of the dependency as the relevant parent fragment.Memoried
As you pointed out, @FragmentScoped create a new instance of the dependency per fragment - that is not what I am looking for. I want to only have different instance of the dependencies between the two different "flows" - parent 1 + children vs parent 2 + children.Memoried
You would need to get the dependencies from the parent fragment directly.Privacy
That could work if I have only a few dependencies but gets "complex" if there are a lot of dependencies. Generally that was my question in how to archive sharing dependencies between fragments without using @Singleton.Memoried
What about @ViewModelScoped and VM, where you are going to have your injected classes. The VM will be shared through "by viewModels(ownerProducer = {parentFragment})" sugar? Or as you suggest, using your custom scope, where it will be working "similar" to the direct solution proposed by EpicPandaForce. First, you will create CustomComponent in Parent and then share it in Children fragments with parentFragment().customCom().inject(this). If the children are strongly bound to the parent, then my personal opinion is to go with the proposed direct solution rather than making custom components.Phylloid

© 2022 - 2024 — McMap. All rights reserved.