
Enables function-like navigation for screens within applications, using a concept of navigation keys as contracts for screen transitions, with support for fragments, activities, and composables.
Note
Please see the CHANGELOG to understand the latest changes in Enro.
Enro is a powerful navigation library for Kotlin Multiplatform, based on a simple idea: screens within an application should behave like functions.
A NavigationKey is the signature of a screen — it declares the screen's inputs,
and optionally a typed result. Calling code never needs to know how the screen is
implemented; it just invokes the contract.
@Serializable
data class ShowProfile(val userId: String) : NavigationKey
@Serializable
data class SelectDate(
val minDate: LocalDate? = null,
val maxDate: LocalDate? = null,
) : NavigationKey.WithResult<LocalDate>If you read those as function signatures:
fun showProfile(userId: String): Unit
fun selectDate(minDate: LocalDate? = null, maxDate: LocalDate? = null): LocalDateEnro targets Android, iOS, Desktop and Web through Compose Multiplatform.
A first-class compatibility layer (enro-compat) keeps Fragments and Activities
working on Android, so you can adopt Enro in an existing app without rewriting it.
Enro is published to Maven Central. Add the
mavenCentral() repository and depend on:
dependencies {
implementation("dev.enro:enro:3.0.0-alpha10")
ksp("dev.enro:enro-processor:3.0.0-alpha10")
testImplementation("dev.enro:enro-test:3.0.0-alpha10")
}Define a key, implement a destination, and navigate. That's the loop.
@Serializable
data object Home : NavigationKey
@Serializable
data class Profile(val userId: String) : NavigationKey
@Composable
@NavigationDestination(Home::class)
fun HomeScreen() {
val navigation = navigationHandle<Home>()
Button(onClick = { navigation.open(Profile("user-123")) }) {
Text("View profile")
}
}
@Composable
@NavigationDestination(Profile::class)
fun ProfileScreen() {
val navigation = navigationHandle<Profile>()
Column {
Text("Profile for ${navigation.key.userId}")
Button(onClick = { navigation.close() }) { Text("Back") }
}
}Wire Enro into your application once:
@NavigationComponent
object MyComponent : NavigationComponentConfiguration(
module = createNavigationModule { }
)
// Android — install in your Application
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
MyComponent.installNavigationController(this)
}
}Then host a backstack anywhere in your UI:
val container = rememberNavigationContainer(
backstack = backstackOf(Home.asInstance()),
)
NavigationDisplay(state = container)That's the whole flow. See enro.dev for the full guide.
recipes/ — every concept (dialogs, bottom sheets, list-detail, tabs, deep links, managed flows, shared view models, custom animations) is a small runnable sample.Enro 3 is a significant rewrite that targets Kotlin Multiplatform and a Compose-first model. The migration guide at enro.dev/docs/migrating-from-v2.html covers the API delta in detail. Highlights:
NavigationKey.SupportsPush / SupportsPresent → flat NavigationKey (+ optional WithResult<T>)@Parcelize → @Serializable (kotlinx)push() / present() → open(key); dialog/overlay behaviour now lives in destination metadatacloseWithResult(r) → complete(r)
NavigationApplication interface is gone; install the component directly from Application.onCreate
enro-compat module"The novices' eyes followed the wriggling path up from the well as it swept a great meandering arc around the hillside. Its stones were green with moss and beset with weeds. Where the path disappeared through the gate they noticed that it joined a second track of bare earth, where the grass appeared to have been trampled so often that it ceased to grow. The dusty track ran straight from the gate to the well, marred only by a fresh set of sandal-prints that went down, and then up, and ended at the feet of the young monk who had fetched their water." — The Garden Path
Note
Please see the CHANGELOG to understand the latest changes in Enro.
Enro is a powerful navigation library for Kotlin Multiplatform, based on a simple idea: screens within an application should behave like functions.
A NavigationKey is the signature of a screen — it declares the screen's inputs,
and optionally a typed result. Calling code never needs to know how the screen is
implemented; it just invokes the contract.
@Serializable
data class ShowProfile(val userId: String) : NavigationKey
@Serializable
data class SelectDate(
val minDate: LocalDate? = null,
val maxDate: LocalDate? = null,
) : NavigationKey.WithResult<LocalDate>If you read those as function signatures:
fun showProfile(userId: String): Unit
fun selectDate(minDate: LocalDate? = null, maxDate: LocalDate? = null): LocalDateEnro targets Android, iOS, Desktop and Web through Compose Multiplatform.
A first-class compatibility layer (enro-compat) keeps Fragments and Activities
working on Android, so you can adopt Enro in an existing app without rewriting it.
Enro is published to Maven Central. Add the
mavenCentral() repository and depend on:
dependencies {
implementation("dev.enro:enro:3.0.0-alpha10")
ksp("dev.enro:enro-processor:3.0.0-alpha10")
testImplementation("dev.enro:enro-test:3.0.0-alpha10")
}Define a key, implement a destination, and navigate. That's the loop.
@Serializable
data object Home : NavigationKey
@Serializable
data class Profile(val userId: String) : NavigationKey
@Composable
@NavigationDestination(Home::class)
fun HomeScreen() {
val navigation = navigationHandle<Home>()
Button(onClick = { navigation.open(Profile("user-123")) }) {
Text("View profile")
}
}
@Composable
@NavigationDestination(Profile::class)
fun ProfileScreen() {
val navigation = navigationHandle<Profile>()
Column {
Text("Profile for ${navigation.key.userId}")
Button(onClick = { navigation.close() }) { Text("Back") }
}
}Wire Enro into your application once:
@NavigationComponent
object MyComponent : NavigationComponentConfiguration(
module = createNavigationModule { }
)
// Android — install in your Application
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
MyComponent.installNavigationController(this)
}
}Then host a backstack anywhere in your UI:
val container = rememberNavigationContainer(
backstack = backstackOf(Home.asInstance()),
)
NavigationDisplay(state = container)That's the whole flow. See enro.dev for the full guide.
recipes/ — every concept (dialogs, bottom sheets, list-detail, tabs, deep links, managed flows, shared view models, custom animations) is a small runnable sample.Enro 3 is a significant rewrite that targets Kotlin Multiplatform and a Compose-first model. The migration guide at enro.dev/docs/migrating-from-v2.html covers the API delta in detail. Highlights:
NavigationKey.SupportsPush / SupportsPresent → flat NavigationKey (+ optional WithResult<T>)@Parcelize → @Serializable (kotlinx)push() / present() → open(key); dialog/overlay behaviour now lives in destination metadatacloseWithResult(r) → complete(r)
NavigationApplication interface is gone; install the component directly from Application.onCreate
enro-compat module"The novices' eyes followed the wriggling path up from the well as it swept a great meandering arc around the hillside. Its stones were green with moss and beset with weeds. Where the path disappeared through the gate they noticed that it joined a second track of bare earth, where the grass appeared to have been trampled so often that it ceased to grow. The dusty track ran straight from the gate to the well, marred only by a fresh set of sandal-prints that went down, and then up, and ended at the feet of the young monk who had fetched their water." — The Garden Path