NavigationEventDispatcher



A dispatcher for navigation events that can be organized hierarchically.

This class acts as a localized entry point for registering NavigationEventCallback 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 callbacks and prevent memory leaks.

Summary

Public constructors

Creates a child NavigationEventDispatcher linked to a parent.

Cmn
NavigationEventDispatcher(
    fallbackOnBackPressed: (() -> Unit)?,
    onHasEnabledCallbacksChanged: ((Boolean) -> Unit)?
)

Creates a root NavigationEventDispatcher.

Cmn

Public functions

Unit
@MainThread
addCallback(
    callback: NavigationEventCallback<*>,
    priority: NavigationEventPriority
)

Adds a new NavigationEventCallback to receive navigation events.

Cmn
Unit

Adds an input handler and binds it to this dispatcher's lifecycle.

Cmn
Unit

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

Cmn
inline StateFlow<NavigationEventState<T>>
<T : NavigationEventInfo> getState(scope: CoroutineScope, initialInfo: T)

Creates a StateFlow that only emits states for a specific NavigationEventInfo type.

Cmn
Boolean

Returns true if there is at least one NavigationEventCallback.isEnabled callback registered with this dispatcher.

Cmn
Unit

Removes and detaches an input handler from this dispatcher.

Cmn

Public properties

Boolean

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

Cmn
StateFlow<NavigationEventState<NavigationEventInfo>>

The StateFlow from the highest-priority, enabled navigation callback.

Cmn

Public constructors

NavigationEventDispatcher(parentDispatcher: NavigationEventDispatcher)

Creates a child NavigationEventDispatcher linked to a parent.

This constructor is used to create nested dispatchers within an existing hierarchy. The new dispatcher will share the same underlying NavigationEventProcessor as its parent, allowing it to participate in the same event stream.

Parameters
parentDispatcher: NavigationEventDispatcher

The parent NavigationEventDispatcher to which this new dispatcher will be attached.

NavigationEventDispatcher(
    fallbackOnBackPressed: (() -> Unit)? = null,
    onHasEnabledCallbacksChanged: ((Boolean) -> Unit)? = null
)

Creates a root NavigationEventDispatcher.

This constructor is used to establish the top-level dispatcher for a new navigation hierarchy, typically within a scope like an Activity or a top-level composable. It creates its own internal NavigationEventProcessor.

Parameters
fallbackOnBackPressed: (() -> Unit)? = null

An optional lambda to be invoked if a navigation event completes and no registered NavigationEventCallback handles it. This provides a default "back" action for the entire hierarchy.

onHasEnabledCallbacksChanged: ((Boolean) -> Unit)? = null

An optional lambda that will be called whenever the global state of whether there are any enabled callbacks changes.

Public functions

addCallback

@MainThread
fun addCallback(
    callback: NavigationEventCallback<*>,
    priority: NavigationEventPriority = Default
): Unit

Adds a new NavigationEventCallback to receive navigation events.

Callbacks are invoked based on priority, and then by recency. All Overlay callbacks are called before any Default callbacks. Within each priority group, callbacks are invoked in a Last-In, First-Out (LIFO) order—the most recently added callback is called first.

All callbacks are invoked on the main thread. To stop receiving events, a callback must be removed via NavigationEventCallback.remove.

Parameters
callback: NavigationEventCallback<*>

The callback instance to be added.

priority: NavigationEventPriority = Default

The priority of the callback, determining its invocation order relative to others. See NavigationEventPriority.

Throws
kotlin.IllegalArgumentException

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

kotlin.IllegalStateException

if the dispatcher has already been disposed.

addInputHandler

fun addInputHandler(inputHandler: NavigationEventInputHandler): Unit

Adds an input handler and binds it to this dispatcher's lifecycle.

The handler is immediately attached via its onAttach() method. The dispatcher will then invoke onEnabled(), onDisabled(), and onDispose() on the handler to mirror its own state changes.

Parameters
inputHandler: NavigationEventInputHandler

The handler to add.

Throws
kotlin.IllegalStateException

if the dispatcher has already been disposed.

dispose

@MainThread
fun dispose(): Unit

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 unregisters this dispatcher's onHasEnabledCallbacksChanged listener from the shared processor.

  2. 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.

  3. It removes all NavigationEventCallback instances directly registered with this specific dispatcher from the shared NavigationEventProcessor, preventing memory leaks and ensuring callbacks are no longer active.

  4. Finally, it removes itself from its parent's list of children, if a parent exists.

Throws
kotlin.IllegalStateException

if the dispatcher has already been disposed.

getState

inline fun <T : NavigationEventInfo> getState(scope: CoroutineScope, initialInfo: T): StateFlow<NavigationEventState<T>>

Creates a StateFlow that only emits states for a specific NavigationEventInfo type.

Parameters
<T : NavigationEventInfo>

The NavigationEventInfo type to filter for.

scope: CoroutineScope

The CoroutineScope in which the new StateFlow is created.

initialInfo: T

The initial NavigationEventInfo of type T to be used when the StateFlow starts.

Returns
StateFlow<NavigationEventState<T>>

A StateFlow that emits values only when the state's destination is of type T.

hasEnabledCallbacks

fun hasEnabledCallbacks(): Boolean

Returns true if there is at least one NavigationEventCallback.isEnabled callback registered with this dispatcher.

Returns
Boolean

True if there is at least one enabled callback.

removeInputHandler

fun removeInputHandler(inputHandler: NavigationEventInputHandler): Unit

Removes and detaches an input handler from this dispatcher.

This severs the lifecycle link. The handler's onDetached() method is invoked, and it will no longer receive events or lifecycle calls from this dispatcher.

Parameters
inputHandler: NavigationEventInputHandler

The handler to remove.

Throws
kotlin.IllegalStateException

if the dispatcher has already been disposed.

See also
addInputHandler

Public properties

isEnabled

var isEnabledBoolean

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.

state

val stateStateFlow<NavigationEventState<NavigationEventInfo>>

The StateFlow from the highest-priority, enabled navigation callback.

This represents the navigation state of the currently active component.