Skip to content

Navigation with Child Stack

The StackNavigator

All navigation in Child Stack is performed using the StackNavigator interface, which is extended by the StackNavigation interface.

StackNavigator contains the navigate method with the following arguments:

  • transformer - converts the current stack of configurations to a new one. The stack is represented as List, where the last element is the top of the stack, and the first element is the bottom of the stack.
  • onComplete - called when navigation is finished.

There is also navigate extension function without the onComplete callback, for convenience.

Warning

The configuration stack returned by the transformer function must not be empty.

Creating the navigation
// In your component class
val navigation = StackNavigation<Configuration>()

The navigation process

During the navigation process, the Child Stack navigation model compares the new stack of configurations with the previous one. It ensures that all removed components are destroyed, and that there is only one component resumed at a time - the top one. All components in the back stack are always either stopped or destroyed.

The Child Stack navigation model usually performs the navigation synchronously, which means that by the time the navigate method returns, the navigation is finished and all component lifecycles are moved into required states. However, the navigation is performed asynchronously in case of recursive invocations - e.g. pop is called from onResume lifecycle callback of a component being pushed. All recursive invocations are queued and performed one by one once the current navigation is finished.

StackNavigator extension functions

There are StackNavigator extension functions to simplify the navigation. Some of which were already used in the Child Stack Overview example.

push(configuration)

Pushes the provided Configuration at the top of the stack. Decompose will throw an exception if the provided Configuration is already present in the stack. This usually happens when a component is pushed on user interaction (e.g. a button click). Consider using pushNew instead.

Illustration

Before
[A, B*]
navigation.push(Configuration.C)
After
[A, B, C*]

pushNew(configuration)

Pushes the provided Configuration at the top of the stack. Does nothing if the provided Configuration is already on top of the stack. Decompose will throw an exception if the provided Configuration is already present in the back stack (not at the top of the stack).

This can be useful when pushing a component on button click, to avoid pushing the same component if the user clicks the same button quickly multiple times.

Illustration 1

Before
[A, B*]
navigation.pushNew(Configuration.C)
After
[A, B, C*]

Illustration 2

Before
[A, B, C*]
navigation.pushNew(Configuration.C)
After
[A, B, C*]

pushToFront(configuration)

Pushes the provided configuration to the top of the stack, removing the configuration from the back stack, if any.

This API works similar to bringToFront, except it compares configurations by equality rather than by configuration class.

Illustration 1

Before
[A(1), B(1)*]
navigation.pushToFront(Configuration.A(2))
After
[A(1), B(1), A(2)*]

Illustration 2

Before
[A(1), B(1), A(2)]
navigation.pushToFront(Configuration.A(1))
After
[B(1), A(2), A(1)*]

Illustration 3

Before
[A(1), B(1), A(2)]
navigation.pushToFront(Configuration.A(2))
After
[A(1), B(1), A(2)*]

pop

Pops the latest configuration at the top of the stack.

Illustration

Before
[A, B, C*]
navigation.pop()

// Or

navigation.pop { isSuccess ->
    // Called when the navigation is finished.
    // isSuccess - `true` if the stack size was greater than 1 and a component was popped, `false` otherwise.
}
After
[A, B*]

popWhile(predicate)

Pops configurations at the top of the stack while the provided predicate returns true.

Illustration

Before
[A, B, C, D*]
navigation.popWhile { topOfStack: Configuration -> topOfStack !is B }
After
[A, B*]

popTo(index)

Pops configurations at the top of the stack so that the provided index becomes active (the new top of the stack).

Illustration

Before
[A, B, C, D*]
navigation.popTo(index = 1)
After
[A, B*]

popToFirst

Pops configurations at the top of the stack so that the first configuration becomes active (the new top of the stack).

Illustration

Before
[A, B, C, D*]
navigation.popToFirst()
After
[A*]

replaceCurrent(configuration)

Replaces the current configuration at the top of the stack with the provided Configuration.

Illustration

Before
[A, B, C*]
navigation.replaceCurrent(Configuration.D)
After
[A, B, D*]

replaceAll(vararg configurations)

Replaces all configurations currently in the stack with the provided configurations. Components that remain in the stack are not recreated, components that are no longer in the stack are destroyed.

Illustration

Before
[A, B, C*]
navigation.replaceAll(Configuration.B, Configuration.C, Configuration.D)
After
[B, C, D*]

bringToFront(configuration)

Removes all components with configurations of the provided Configuration's class, and adds the provided Configuration to the top of the stack. This is primarily helpful when implementing a Decompose app with Bottom Navigation. See the related discussion in the old repository.

Note

The operation is performed as one transaction. If there is already a component with the same configuration, it will not be recreated.

Illustration

Before
[A, B, C*]
navigation.bringToFront(Configuration.B)
After
[A, C, B*]