Basic Concepts
This page is the short vocabulary tour. Each concept here gets its own page under Core Concepts β read this one first so the rest of the docs make sense.
The central idea is that screens behave like functions. A screen has a contract (its inputs and an optional return value); calling code invokes the contract without knowing how the screen is implemented.
Navigation Keys
A NavigationKey is the contract for a screen β its function signature. The properties of the key are the inputs to the screen. If the screen produces a value, the key implements NavigationKey.WithResult<T>.
@Serializable
data class ShowProfile(val userId: String) : NavigationKey
@Serializable
data class SelectDate(
val minDate: LocalDate? = null,
val maxDate: LocalDate? = null,
) : NavigationKey.WithResult<LocalDate>
Keys are @Serializable (kotlinx.serialization) so Enro can persist the backstack across process death and across platforms.
A key on its own doesnβt do anything β itβs a value. The system that invokes the contract is the navigation handle (below).
Navigation Destinations
A NavigationDestination is the implementation of a contract β the screen itself. Itβs bound to a key with the @NavigationDestination(KeyClass::class) annotation. Two styles are supported:
A regular Composable function:
@Composable
@NavigationDestination(ShowProfile::class)
fun ProfileScreen() {
val navigation = navigationHandle<ShowProfile>()
Text("Profile for ${navigation.key.userId}")
}
Or a destination provider, when the destination needs metadata (for example, to behave like a dialog or an overlay):
@NavigationDestination(ShowProfile::class)
val profileDestination = navigationDestination<ShowProfile> {
Text("Profile for ${navigation.key.userId}")
}
The provider form is also what you use to declare a dialog, bottom sheet, or custom overlay β through the metadata = { dialog() } or metadata = { directOverlay() } builders. See Navigation Destinations.
Navigation Handle
A NavigationHandle is the control surface inside a screen β the variable you call open, close, or complete on.
val navigation = navigationHandle<ShowProfile>()
navigation.open(SelectDate(maxDate = LocalDate.now())) // open another screen
navigation.close() // close this screen
navigation.complete(result) // close with a result
The typed parameter (<ShowProfile> above) gives you access to the key the screen was opened with via navigation.key.
Navigation Container
A NavigationContainer is a location in your UI that hosts a backstack of destinations. You create one inside a Composable with rememberNavigationContainer, give it an initial backstack, and render it with NavigationDisplay.
val container = rememberNavigationContainer(
backstack = backstackOf(Home.asInstance()),
)
NavigationDisplay(state = container)
A typical app has one root container; nested containers are supported and are how features like tabs, list-detail layouts, and multiple back stacks are built.
NavigationKey.Instance
When a key is added to a backstack, Enro wraps it in a NavigationKey.Instance β the same key may appear in the backstack more than once, so each appearance gets a unique id. Youβll mostly see Instance when building a backstack:
val initial = backstackOf(
Home.asInstance(),
ShowProfile("user-123").asInstance(),
)
You can also attach Metadata to an instance to influence how that particular appearance behaves (animations, scene treatment, etc.).
Results
A NavigationKey.WithResult<T> screen returns a value to its caller. Callers register a result channel and call open on it.
val getDate = registerForNavigationResult<LocalDate>(
onCompleted = { date -> /* use date */ },
)
Button(onClick = { getDate.open(SelectDate(maxDate = LocalDate.now())) }) {
Text("Pick a date")
}
The screen returns its value with navigation.complete(date). See Results.
NavigationComponent
A NavigationComponent is the configuration object for Enro in your application. Itβs declared once, annotated with @NavigationComponent, and installed at app startup.
@NavigationComponent
object MyComponent : NavigationComponentConfiguration(
module = createNavigationModule { /* optional config */ }
)
See Installation.
Where everything fits
Application starts
βββ MyComponent.installNavigationController(this) β happens once
Compose tree
βββ rememberNavigationContainer(backstack = ...) β NavigationContainer
βββ NavigationDisplay(state = container)
βββ Destination for the current key on the stack
βββ navigationHandle<MyKey>() β NavigationHandle
βββ .open(...) / .close() / .complete(...)
Next steps
- Walk through Your First Screen for a complete end-to-end example.
- Then read the Core Concepts pages in order β they go into each of the above in depth.