NavDestination

@Target(allowedTargets = [AnnotationTarget.CLASS])
annotation class NavDestination(val route: KClass<*>, val parentScope: KClass<*>, val kind: DestinationKind)(source)

Marks a presenter class as a navigation destination so the codegen processor can wire it into the consumer's navigation graph at compile time.

Without this annotation a presenter is just a class. The consumer would have to manually write a Metro @GraphExtension for it, manually register a NavDestination factory, manually register a NavRouteBinding for polymorphic save and restore, and repeat that boilerplate for every screen, overlay, and tab in the app. With this annotation the processor emits all of that for you in a file that lives next to your presenter and that your IDE can navigate to.

The kind parameter picks the destination role (a regular stack screen, a modal overlay, or a top level tab anchor) and the processor emits the matching pair of files.

Example

@Inject
@NavDestination(
route = ShowsRoute::class,
parentScope = ActivityScope::class,
kind = DestinationKind.SCREEN,
)
class ShowsPresenter(
componentContext: ComponentContext,
) : ComponentContext by componentContext

The processor emits two files into the presenter's <package>.di package: a Metro graph extension named ShowsScreenGraph and a binding named ShowsNavDestinationBinding. The binding contributes the presenter to the consumer's Set<NavDestination<*>> and Set<NavRouteBinding<*>> multibindings.

Generated artifacts by kind

  • DestinationKind.SCREEN emits <Presenter>ScreenGraph (a @GraphExtension(route) interface) and <Presenter>NavDestinationBinding contributing @IntoSet NavDestination.Screen plus @IntoSet NavRouteBinding<*>.

  • DestinationKind.OVERLAY emits the same files as DestinationKind.SCREEN except the destination is NavDestination.Overlay. The consumer's navigator decides at runtime whether to push the destination onto the back stack or present it as an overlay, based on the destination subclass and the route's type.

  • DestinationKind.TAB_ROOT emits <Presenter>TabGraph (a @GraphExtension(route) interface) and <Presenter>TabDestinationBinding contributing @IntoSet NavDestination.TabRoot plus @IntoSet NavRootBinding<*>. The route is a NavRoot (typically a data object) and doubles as the graph scope.

Parameterized presenters

If the presenter uses Metro's @AssistedInject with a nested @AssistedFactory, the processor detects this and emits a parameterized binding that extracts the runtime parameter from the route. The presenter's single @Assisted constructor parameter must have the same type as a property on the route class. The processor reads the route property at navigation time and passes it through the assisted factory.

@AssistedInject
@NavDestination(
route = ShowDetailsRoute::class,
parentScope = ActivityScope::class,
kind = DestinationKind.SCREEN,
)
class ShowDetailsPresenter(
@Assisted val showId: Long,
componentContext: ComponentContext,
) : ComponentContext by componentContext {
@AssistedFactory
interface Factory {
fun create(showId: Long): ShowDetailsPresenter
}
}

@Serializable
data class ShowDetailsRoute(val showId: Long) : NavRoute

Validation

The processor reports a compile error if any of the following hold:

  • The annotated symbol is not a class.

  • kind is not SCREEN, OVERLAY, or TAB_ROOT.

  • The presenter is parameterized but does not have exactly one @Assisted constructor parameter.

  • kind is TAB_ROOT and the presenter declares a nested @AssistedFactory. Tab roots must use plain @Inject because their route is a singleton data object and carries no runtime payload.

Properties

Link copied to clipboard

The destination role. See DestinationKind.

Link copied to clipboard

The parent dependency injection scope hosting the generated binding. Typically ActivityScope::class in the consumer project.

Link copied to clipboard
val route: KClass<*>

The feature's route class. For DestinationKind.SCREEN or DestinationKind.OVERLAY it implements NavRoute. For DestinationKind.TAB_ROOT it implements NavRoot. The route class doubles as the generated graph's scope marker.