Fragments
Tangle performs Fragment injection using constructor injection, just like the rest of a typical Dagger/Anvil graph. There are several steps to configuration, with two different paths at the end.
1. Set up Gradle
- Anvil Plugin
- Kotlin dependencies block
- Groovy dependencies block
// any Android module's build.gradle.kts
plugins {
id("android-library") // or application, etc.
kotlin("android")
id("com.squareup.anvil")
id("com.rickbusarow.tangle")
}
tangle {
fragmentsEnabled.set(true) // default is true
}
// any Android module's build.gradle.kts
plugins {
id("android-library") // or application, etc.
kotlin("android")
id("com.squareup.anvil")
}
dependencies {
api("com.rickbusarow.tangle:tangle-fragment-api:0.12.0")
anvil("com.rickbusarow.tangle:tangle-fragment-compiler:0.12.0")
}
// any Android module's build.gradle
plugins {
id 'android-library' // or application, etc.
kotlin("android")
id 'com.squareup.anvil'
}
dependencies {
api 'com.rickbusarow.tangle:tangle-fragment-api:0.12.0'
anvil 'com.rickbusarow.tangle:tangle-fragment-compiler:0.12.0'
}
2. Use Anvil for the app-scoped Component
Tangle uses the MergeComponent annotation from Anvil to identify the application's Component and add its own dependencies to the Dagger graph.
For anyone already using Anvil, there's probably nothing to be done here.
Anvil uses KClass<T>
references to define scopes. A common pattern is to define an AppScope
class specifically for this purpose in a low-level shared (Gradle) module:
package myApp.core.anvil
abstract class AppScope private constructor()
Then at your application Component, use MergeComponent
with this scope:
package myApp.app
import com.squareup.anvil.annotations.MergeComponent
import myApp.core.anvil.AppScope
@MergeComponent(AppScope::class)
interface MyAppComponent
3. Set the custom FragmentFactory
New Fragment instances are provided by TangleFragmentFactory. This custom factory
is automatically added to any Dagger graph for any @MergeComponent
-annotated Component.
If a requested Fragment is not contained within Tangle's bindings, TangleFragmentFactory
will
fall back to using the default initialization with an empty constructor. This means that large
projects can be migrated gradually.
If a project was already doing Fragment constructor injection using vanilla Dagger, they were
probably already binding into a
Map<Class<out Fragment>, Provider<@JvmSuppressWildcards Fragment>>
. That is what Tangle uses,
so existing multi-bound graphs will often support gradual migrations as well.
Any FragmentManager used within the application will need to have its fragmentFactory
property set to a TangleFragmentFactory
instance. This means the
AppCompatActivity.supportFragmentManager
, and possibly Fragment.childFragmentManager
as well.
This is easiest if your application uses an abstract base class.
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
supportFragmentManager.fragmentFactory = Components.get<BaseActivityComponent>()
.tangleFragmentFactory
super.onCreate(savedInstanceState)
}
}
Click to see how Components
works
4. Contribute Fragments to the graph
Finally, add the Fragments themselves. For basic injection, the only difference from any other constructor-injected class is that you must add the ContributesFragment annotation. This will ensure that the Fragment is included in the TangleFragmentFactory.
import tangle.fragment.ContributesFragment
@ContributesFragment(AppScope::class)
class MyFragment @Inject constructor(
val myRepository: MyRepository
) : Fragment() {
// ...
}
5. Create Fragments with the FragmentManager
All the pieces are now in place, and your FragmentManagers are able to create Fragments with Dagger dependencies.
class MyActivity: BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportFragmentManager.beginTransaction()
.add<MyFragment>(R.id.fragmentContainer)
.commit()
}
}
Next step -- "Assisted" Bundle injection
Tangle is able to generate type-safe factories for Bundle arguments, similar to AssistedInject. Read about this more in bundle injection.
@ContributesFragment(AppScope::class)
class MyFragment @FragmentInject constructor() : Fragment() {
val name by arg<String>("name")
@FragmentInjectFactory
interface Factory {
fun create(@TangleParam("name") name: String): MyFragment
}
}