NavigationEventDispatcher


public final class NavigationEventDispatcher


A dispatcher for navigation events that can be organized hierarchically.

This class acts as a localized entry point for registering NavigationEventHandler instances and dispatching navigation events within a specific UI scope, such as a composable or a fragment.

Dispatchers can be linked in a parent-child hierarchy. This structure allows for a sophisticated system where nested UI components can handle navigation events independently while still respecting the state of their parent. The core logic is delegated to a single, shared NavigationEventProcessor instance across the entire hierarchy, ensuring consistent event handling.

It is important to call dispose when the owner of this dispatcher is destroyed (e.g., in a DisposableEffect) to unregister handlers and prevent memory leaks.

Summary

Constants

static final int

Default priority level for primary UI content.

static final int

Highest priority level, intended for overlay UI components.

Public constructors

Creates a root NavigationEventDispatcher with no default fallback action.

NavigationEventDispatcher(
    @NonNull OnBackCompletedFallback onBackCompletedFallback
)

Creates a root NavigationEventDispatcher with a fallback action.

Creates a child NavigationEventDispatcher linked to a parent.

Public methods

final void
@MainThread
addHandler(
    @NonNull NavigationEventHandler<@NonNull ?> handler,
    int priority
)

Adds a new NavigationEventHandler to receive navigation events.

final void

Adds an input with an unspecified priority, registering it with the shared processor and binding it to this dispatcher's lifecycle.

final void

Adds an input with a specific priority, registering it with the shared processor and binding it to this dispatcher's lifecycle.

final void

Removes this dispatcher and its entire chain of descendants from the hierarchy.

final @NonNull StateFlow<@NonNull NavigationEventHistory>

The globally observable, read-only state of the navigation history stack.

final @NonNull StateFlow<@NonNull NavigationEventTransitionState>

The globally observable, read-only state of the physical navigation gesture.

final boolean

Controls whether this dispatcher is active and will process navigation events.

final void

Removes and detaches an input from this dispatcher and the shared processor.

final void
setEnabled(boolean isEnabled)

Controls whether this dispatcher is active and will process navigation events.

Constants

PRIORITY_DEFAULT

public static final int PRIORITY_DEFAULT = 1

Default priority level for primary UI content.

Components at this level will receive navigation events after PRIORITY_OVERLAY components have been given a chance to handle them.

PRIORITY_OVERLAY

public static final int PRIORITY_OVERLAY = 0

Highest priority level, intended for overlay UI components.

Components at this level (e.g., dialogs, bottom sheets, navigation drawers) will receive navigation events before components at PRIORITY_DEFAULT.

Public constructors

Added in 1.0.0-alpha09
public NavigationEventDispatcher()

Creates a root NavigationEventDispatcher with no default fallback action.

Establishes the top-level dispatcher for a new navigation hierarchy, typically within an Activity or a top-level composable. It creates its own internal NavigationEventProcessor.

If a navigation event completes without being handled by any registered NavigationEventHandler, nothing further will happen.

Added in 1.0.0-alpha09
public NavigationEventDispatcher(
    @NonNull OnBackCompletedFallback onBackCompletedFallback
)

Creates a root NavigationEventDispatcher with a fallback action.

Establishes the top-level dispatcher for a new navigation hierarchy, typically within an Activity or a top-level composable. It creates its own internal NavigationEventProcessor.

Parameters
@NonNull OnBackCompletedFallback onBackCompletedFallback

A lambda to be invoked if a navigation event completes and no registered NavigationEventHandler handles it. This provides a default "back" action for the entire hierarchy. It will not be invoked if the event is cancelled.

Added in 1.0.0-alpha09
public NavigationEventDispatcher(@NonNull NavigationEventDispatcher parent)

Creates a child NavigationEventDispatcher linked to a parent.

Used to create nested dispatchers within an existing hierarchy. The new dispatcher shares the same underlying NavigationEventProcessor as its parent, allowing it to participate in the same event stream.

Parameters
@NonNull NavigationEventDispatcher parent

The parent NavigationEventDispatcher to which this dispatcher will be attached.

Public methods

addHandler

Added in 1.0.0-alpha09
@MainThread
public final void addHandler(
    @NonNull NavigationEventHandler<@NonNull ?> handler,
    int priority
)

Adds a new NavigationEventHandler to receive navigation events.

Handlers are invoked based on priority, and then by recency. All PRIORITY_OVERLAY handlers are called before any PRIORITY_DEFAULT handlers. Within each priority group, handlers are invoked in a Last-In, First-Out (LIFO) order—the most recently added handler is called first.

All handlers are invoked on the main thread. To stop receiving events, a handler must be removed via NavigationEventHandler.remove.

Parameters
@NonNull NavigationEventHandler<@NonNull ?> handler

The handler instance to be added.

int priority

The priority of the handler, determining its invocation order relative to others. See NavigationEventDispatcher.Priority.

Throws
kotlin.IllegalArgumentException

if the given handler is already registered with a different dispatcher.

kotlin.IllegalArgumentException

if priority is not one of the supported constants.

kotlin.IllegalStateException

if the dispatcher has already been disposed.

addInput

Added in 1.0.0-alpha09
@MainThread
public final void addInput(@NonNull NavigationEventInput input)

Adds an input with an unspecified priority, registering it with the shared processor and binding it to this dispatcher's lifecycle.

The input is registered globally with the sharedProcessor to receive system-wide state updates (e.g., whether any handlers are enabled). It is also tracked locally by this dispatcher for lifecycle management.

The input's NavigationEventInput.onAdded method is invoked immediately upon addition. It will be automatically detached when this dispatcher dispose is called.

Parameters
@NonNull NavigationEventInput input

The input to add.

Throws
kotlin.IllegalStateException

if the dispatcher has already been disposed.

kotlin.IllegalArgumentException

if input is already added to a dispatcher.

addInput

Added in 1.0.0-alpha09
@MainThread
public final void addInput(@NonNull NavigationEventInput input, int priority)

Adds an input with a specific priority, registering it with the shared processor and binding it to this dispatcher's lifecycle.

The input is registered globally with the sharedProcessor to receive system-wide state updates (e.g., whether any handlers are enabled). It is also tracked locally by this dispatcher for lifecycle management.

The input's NavigationEventInput.onAdded method is invoked immediately upon addition. It will be automatically detached when this dispatcher dispose is called.

Parameters
@NonNull NavigationEventInput input

The input to add.

int priority

The priority to associate with this input. Must be one of the supported constants: PRIORITY_OVERLAY, PRIORITY_DEFAULT.

Throws
kotlin.IllegalStateException

if the dispatcher has already been disposed.

kotlin.IllegalArgumentException

if input is already added to a dispatcher.

kotlin.IllegalArgumentException

if priority is not one of the supported constants.

dispose

Added in 1.0.0-alpha09
@MainThread
public final void dispose()

Removes this dispatcher and its entire chain of descendants from the hierarchy.

This is the primary cleanup method and should be called when the component owning this dispatcher is destroyed (e.g., in DisposableEffect in Compose).

This is a terminal operation; once a dispatcher is disposed, it cannot be reused.

Calling this method triggers a comprehensive, iterative cleanup:

  1. It iteratively processes and disposes of all child dispatchers and their descendants, ensuring a complete top-down cleanup of the entire sub-hierarchy without recursion.

  2. For each dispatcher, it first detaches all registered NavigationEventInput instances by calling NavigationEventInput.onRemoved. This severs their lifecycle link to the dispatcher and allows them to release any tied resources.

  3. It then removes all NavigationEventHandler instances registered with that dispatcher from the shared processor, preventing memory leaks.

  4. Finally, it removes the dispatcher from its parent's list of children, fully dismantling the hierarchy.

Throws
kotlin.IllegalStateException

if the dispatcher has already been disposed.

getHistory

Added in 1.0.0-alpha09
public final @NonNull StateFlow<@NonNull NavigationEventHistorygetHistory()

The globally observable, read-only state of the navigation history stack.

This flow represents only the navigation stack (the NavigationEventHistory.mergedHistory and NavigationEventHistory.currentIndex) and is the counterpart to transition state.

A key contract of this state is that it remains stable during a navigation gesture. It only updates when the navigation stack itself changes (e.g., when a new handler becomes active, or the active handler's info is updated), which typically occurs after a gesture completes or before one begins.

This allows UI components to subscribe only to changes in the history stack without being notified of rapid gesture progress updates from transition state.

getTransitionState

Added in 1.0.0-alpha09
public final @NonNull StateFlow<@NonNull NavigationEventTransitionStategetTransitionState()

The globally observable, read-only state of the physical navigation gesture.

This flow represents only the gesture's progress (e.g., Idle or InProgress) and is separate from the navigation history state.

System-level components or UI animations can subscribe to this flow to react to the start, progress, and end of a gesture without needing to know about the specific, generic NavigationEventInfo types involved in the history.

This state is derived from the NavigationEventTransitionState of the currently active NavigationEventHandler.

isEnabled

Added in 1.0.0-alpha09
public final boolean isEnabled()

Controls whether this dispatcher is active and will process navigation events.

A dispatcher's effective enabled state is hierarchical. It depends on both its own local isEnabled state and the state of its parent.

Getting the value:

  • This will return false if the parentDispatcher exists and its isEnabled state is false, regardless of this dispatcher's own setting. This provides a simple way to disable an entire branch of a navigation hierarchy by disabling its root.

  • If there is no parent or the parent is enabled, it will return the local value of this property (true by default).

Setting the value:

  • This only updates the local enabled state for this specific dispatcher. The getter will always re-evaluate the effective state based on the parent.

For this dispatcher to be truly active, its local isEnabled property must be true, and the isEnabled properties of all its ancestors must also be true.

removeInput

Added in 1.0.0-alpha09
@MainThread
public final void removeInput(@NonNull NavigationEventInput input)

Removes and detaches an input from this dispatcher and the shared processor.

This severs the input's lifecycle link to the dispatcher. Its NavigationEventInput.onRemoved method is invoked, and it will no longer receive lifecycle calls or global state updates from the processor.

Parameters
@NonNull NavigationEventInput input

The input to remove.

Throws
kotlin.IllegalStateException

if the dispatcher has already been disposed.

See also
addInput
onAdded

setEnabled

Added in 1.0.0-alpha09
public final void setEnabled(boolean isEnabled)

Controls whether this dispatcher is active and will process navigation events.

A dispatcher's effective enabled state is hierarchical. It depends on both its own local isEnabled state and the state of its parent.

Getting the value:

  • This will return false if the parentDispatcher exists and its isEnabled state is false, regardless of this dispatcher's own setting. This provides a simple way to disable an entire branch of a navigation hierarchy by disabling its root.

  • If there is no parent or the parent is enabled, it will return the local value of this property (true by default).

Setting the value:

  • This only updates the local enabled state for this specific dispatcher. The getter will always re-evaluate the effective state based on the parent.

For this dispatcher to be truly active, its local isEnabled property must be true, and the isEnabled properties of all its ancestors must also be true.