ComponentContextFactory in Decompose: How It Creates Isolated Child Component Contexts
ComponentContextFactory is a functional interface in the Decompose library that constructs isolated component contexts for child components by combining Lifecycle, StateKeeper, InstanceKeeper, and BackHandler services.
When building navigation flows in Decompose (arkivanov/decompose), parent components must spawn child components with their own isolated lifecycles and state management. The ComponentContextFactory centralizes this creation logic, ensuring every child receives properly configured Essenty services without tight coupling to specific context implementations.
What Is ComponentContextFactory?
Located in decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactory.kt, ComponentContextFactory is a functional interface that knows how to build a new component context from the four Essenty services required by every Decompose component.
| Parameter | Purpose |
|---|---|
Lifecycle |
Controls the start/stop lifecycle of the component. |
StateKeeper |
Persists state across process recreation. |
InstanceKeeper |
Holds singleton‑like objects for the component’s whole lifetime. |
BackHandler |
Handles back‑button events (Android) or other “back” actions. |
fun interface ComponentContextFactory<out T : Any> {
operator fun invoke(
lifecycle: Lifecycle,
stateKeeper: StateKeeper,
instanceKeeper: InstanceKeeper,
backHandler: BackHandler,
): T
}
Why ComponentContextFactory Is Required for Child Components
When a navigation model (e.g., a Router, a StackNavigator, or a ChildSlotNavigator) creates a child component, it must provide that child with an isolated context containing its own lifecycle, state‑keeping, instance‑keeping, and back‑handling. The factory encapsulates the creation logic so the navigation model does not need to know the concrete type of the context (plain ComponentContext, JetpackComponentContext, or any custom implementation).
- Decoupling: The navigation infrastructure works with the abstract
ComponentContextFactory<T>rather than a concrete context class. - Customization: Apps can supply their own factory (e.g., to create a
JetpackComponentContextthat also exposes aSavedStateHandle). - Testability: Tests can inject a simple factory returning
DefaultComponentContextto verify navigation behavior.
How ComponentContextFactory Works in Practice
Default Factory Usage in Parent Components
The default implementation of ComponentContext provides its own factory via ComponentContextFactoryOwner. In DefaultComponentContext.kt, the factory is exposed so parent components can create isolated child contexts.
class RootComponent(
componentContext: ComponentContext
) : ComponentContextOwner by componentContext {
// Router that will create child components
private val router = router<Config, Child>(initialStack = listOf(Config.First))
// The factory comes from the parent context (DefaultComponentContext)
private val childFactory = componentContext.componentContextFactory
private fun createChild(config: Config): Child {
// Build a fresh child context using the factory
val childContext = childFactory(
lifecycle = LifecycleRegistry(),
stateKeeper = StateKeeper(),
instanceKeeper = InstanceKeeper(),
backHandler = BackHandler()
)
return Child(childContext, config)
}
}
Custom Factory for Jetpack Integration
For Android Jetpack integration, DefaultJetpackComponentContext.kt supplies a factory that creates JetpackComponentContext objects. This allows child components to access Jetpack‑specific APIs like SavedStateHandle while maintaining the same creation pattern.
class JetpackRootComponent(
componentContext: JetpackComponentContext
) : ComponentContextOwner by componentContext {
// Use the Jetpack‑specific factory
private val childFactory: ComponentContextFactory<JetpackComponentContext> =
componentContext.componentContextFactory
private fun createJetpackChild(): JetpackChild {
val childContext = childFactory(
lifecycle = componentContext.lifecycle,
stateKeeper = componentContext.stateKeeper,
instanceKeeper = componentContext.instanceKeeper,
backHandler = componentContext.backHandler
)
return JetpackChild(childContext)
}
}
Testing with Factory Injection
In unit tests, you can inject a simple factory that returns DefaultComponentContext instances. This is demonstrated in the test utilities where ComponentContextFactory enables verification of router behavior without requiring full Android infrastructure.
class RouterTest {
// Simple factory that creates a DefaultComponentContext
private val testFactory = ComponentContextFactory(::DefaultComponentContext)
@Test
fun `router creates child with proper context`() {
val router = TestRouter(
factory = testFactory,
childFactory = { ctx, config -> TestChild(ctx, config) }
)
// ...assertions about lifecycle, stateKeeper etc.
}
}
Key Implementation Files
Summary
- ComponentContextFactory is a functional interface in
ComponentContextFactory.ktthat constructs component contexts from four Essenty services:Lifecycle,StateKeeper,InstanceKeeper, andBackHandler. - It enables decoupled child creation by allowing navigation models to instantiate child components without knowing the concrete context type.
- The factory pattern supports customization for platform-specific implementations like
JetpackComponentContextand improves testability through dependency injection. - Routers use
ComponentContextFactoryviaDefaultChildItemFactory.ktto ensure each child receives an isolated context with properly wired lifecycle and state management.
Frequently Asked Questions
What parameters does ComponentContextFactory require?
ComponentContextFactory accepts four parameters: a Lifecycle for controlling component start/stop states, a StateKeeper for persisting state across process recreation, an InstanceKeeper for holding singleton-like objects during the component's lifetime, and a BackHandler for managing back-button events. These parameters are defined in the invoke operator function in ComponentContextFactory.kt.
How does ComponentContextFactory differ from ComponentContext itself?
While ComponentContext is the interface that components implement to access lifecycle and state services, ComponentContextFactory is the functional interface responsible for creating those context instances. Think of ComponentContext as the container of services, and ComponentContextFactory as the builder that assembles new containers for each child component.
Can I create a custom ComponentContextFactory for my own context type?
Yes, you can implement a custom ComponentContextFactory to create specialized context types like JetpackComponentContext. By implementing the functional interface and providing your own logic in the invoke operator, you can expose additional platform-specific APIs (such as Android's SavedStateHandle) while maintaining compatibility with Decompose's navigation infrastructure.
Where is ComponentContextFactory used in the Decompose router?
The router uses ComponentContextFactory through DefaultChildItemFactory.kt to instantiate fresh contexts for each child in a navigation stack. When the router pushes a new configuration, it invokes the factory to create a child-specific Lifecycle, StateKeeper, InstanceKeeper, and BackHandler, ensuring proper isolation and lifecycle propagation between parent and child components.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →