-
-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
212 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Navigation with Child Overlay | ||
|
||
## The OverlayNavigator | ||
|
||
All navigation in `Child Overlay` is performed using the [`OverlayNavigator`](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt) interface, which is extended by the `OverlayNavigation` interface. | ||
|
||
`OverlayNavigator` contains the `navigate` method with the following arguments: | ||
|
||
- `transformer` - converts the current configuration (if any) into a new one or `null`. | ||
- `onComplete` - called when navigation is finished. | ||
|
||
There is also `navigate` extension function without the `onComplete` callback, for convenience. | ||
|
||
```kotlin title="Creating the navigation" | ||
val navigation = OverlayNavigation<DialogConfig>() | ||
``` | ||
|
||
### The navigation process | ||
|
||
During the navigation process, `Child Overlay` compares the new configuration with the previous one. If both are the same, then no navigation is performed. Otherwise, the currently active component is destroyed (if any), and a new one is activated (if the new configuration is not `null`). | ||
|
||
`Child Overlay` 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. `dismiss` is called from `onResume` lifecycle callback of a component being activated. All recursive invocations are queued and performed one by one once the current navigation is finished. | ||
|
||
## OverlayNavigator extension functions | ||
|
||
There are `OverlayNavigator` [extension functions](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt) to simplify the navigation. | ||
|
||
### activate | ||
|
||
Activates a component with the provided `Configuration` (if not `null`). Any currently active component is destroyed. | ||
|
||
```kotlin | ||
navigation.activate(DialogConfig(title = "Some title")) | ||
``` | ||
|
||
### dismiss | ||
|
||
Destroys the currently active component, if any. | ||
|
||
```kotlin | ||
navigation.dismiss() | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Child Overlay overview | ||
|
||
## The Child Overlay | ||
|
||
`Child Overlay` is a navigation model with either one active component at a time, or none. In other words, each `Child Overlay` allows to activate a child component, replace with another child component, or dismiss when not needed. It is possible to have more than one `Child Overlay` in a component, nested overlays are also supported. | ||
|
||
The `Child Overlay` navigation consists of two main entities: | ||
|
||
- [ChildOverlay](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt) - a simple data class that holds the currently active child, if any. | ||
- [OverlayNavigation](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt) - an interface that accepts navigation commands and forwards them to all subscribed observers. | ||
|
||
### Component Configurations | ||
|
||
Each component created and managed by the `Child Overlay` has a configuration, please read the documentation about [child configurations](/Decompose/navigation/overview/#component-configurations-and-child-factories). | ||
|
||
### Initializing the Child Overlay | ||
|
||
There are three steps to initialize the `Child Overlay`: | ||
|
||
- Create a new instance of `OverlayNavigation` and assign it to a variable or a property. | ||
- Initialize the `Child Overlay` using the `ComponentContext#childOverlay` extension function and pass `OverlayNavigation` into it along with other arguments. | ||
- The `childOverlay` function returns `Value<ChildOverlay>` that can be observed in the UI. Assign the returned `Value` to another property or a variable. | ||
|
||
## Example | ||
|
||
Here is a very basic example of a child overlay: | ||
|
||
```kotlin title="Dialog component" | ||
interface DialogComponent { | ||
|
||
fun onDismissClicked() | ||
} | ||
|
||
class DefaultDialogComponent( | ||
private val componentContext: ComponentContext, | ||
private val message: String, | ||
private val onDismissed: () -> Unit, | ||
) : DialogComponent, ComponentContext by componentContext { | ||
|
||
override fun onDismissClicked() { | ||
onDismissed() | ||
} | ||
} | ||
``` | ||
|
||
```kotlin title="Root component" | ||
interface RootComponent { | ||
|
||
val dialog: Value<ChildOverlay<*, DialogComponent>> | ||
} | ||
|
||
class DefaultRootComponent( | ||
componentContext: ComponentContext, | ||
) : RootComponent, ComponentContext by componentContext { | ||
|
||
private val dialogNavigation = OverlayNavigation<DialogConfig>() | ||
|
||
private val _dialog = | ||
childOverlay( | ||
source = dialogNavigation, | ||
key = "Dialog", | ||
// persistent = false, // Disable navigation state saving, if needed | ||
handleBackButton = true, // Close the dialog on back button press | ||
) { config, componentContext -> | ||
DefaultDialogComponent( | ||
componentContext = componentContext, | ||
message = config.message, | ||
onDismissed = dialogNavigation::dismiss, | ||
) | ||
} | ||
|
||
override val dialog: Value<ChildOverlay<*, DialogComponent>> = _dialog | ||
|
||
private fun showDialog(message: String) { | ||
dialogNavigation.activate(DialogConfig(message = message)) | ||
} | ||
|
||
@Parcelize | ||
private data class DialogConfig( | ||
val message: String, | ||
) : Parcelable | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Navigation overview | ||
|
||
Decompose provides the ability to create [permanent child components](/Decompose/navigation/stack/overview/) using the `childStack` extension function. But if you need to dynamically switch child components, then navigation comes in handy. | ||
|
||
Currently, Decompose provides two ways to navigate: | ||
|
||
- [Child Stack](/Decompose/navigation/stack/overview/) - prefer this way if you need to organize child components in a stack and navigate between them. | ||
- [Child Overlay](/Decompose/navigation/overlay/overview/) - prefer this way if you need to activate-dismiss a child component. | ||
|
||
## Component configurations and child factories | ||
|
||
The term `Configuration` is widely used in Decompose navigation. A configuration is a persistent class that represents a child component and contains all its arguments (not dependencies). Decompose automatically persists child configurations using [StateKeeper](/Decompose/component/state-preservation/), so child components are automatically recreated on events like Android configuration changes, process death, etc. | ||
|
||
Usually, you initialize a navigation by supplying a child factory function to Decompose. The function accepts a child configuration and `ComponentContext` and returns a new instance of the corresponding child component - `(Config, ComponentContext) -> Child`. When you need to navigate, you call a navigation method and pass a configuration there. Decompose automatically creates and manages a [ComponentContext](/Decompose/component/overview/#componentcontext) for every child component, and calls the provided factory function when a new instance of a child component is required. This is where you should instantiate child components and supply dependencies, the configuration only provides persistent arguments and is used to distinguish which component to create. | ||
|
||
### Configuration requirements | ||
|
||
Configurations must meet the following requirements: | ||
|
||
1. Be immutable | ||
2. [Correctly](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--) implement `equals()` and `hashCode()` methods | ||
3. Implement `Parcelable` interface | ||
|
||
Different kinds of navigation may have additional requirements for configurations. It's recommended to define configurations as `data class`, and use only `val` properties and immutable data structures. | ||
|
||
### Configurations are Parcelable | ||
|
||
`Configurations` can be persisted via Android's [saved state](https://developer.android.com/guide/components/activities/activity-lifecycle#save-simple,-lightweight-ui-state-using-onsaveinstancestate), thus allowing the navigation state to be restored after configuration changes or process death. | ||
|
||
Decompose uses [Essenty](https://github.com/arkivanov/Essenty) library, which provides both `Parcelable` interface and `@Parcelize` annotation in common code using expect/actual, which works well with Kotlin Multiplatform. Please familiarise yourself with Essenty library. | ||
|
||
!!!warning | ||
On Android the amount of data that can be preserved is [limited](https://developer.android.com/guide/components/activities/parcelables-and-bundles). Please mind the size of configurations. |
2 changes: 1 addition & 1 deletion
2
docs/child-stack/browser-history.md → docs/navigation/stack/browser-history.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Oops, something went wrong.