Child Slot overview¶
The Child Slot¶
Child Slot
is a navigation model that allows only one child component at a time, or none. In other words, each Child Slot
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 Slot
in a component, nested slots are also supported.
The most common use cases include but not limited to displaying dialogs, drawers, bottom sheets, and just changing the visibility of some views. It's not necessarily something that overlays the parent component.
The Child Slot
navigation consists of two main entities:
- ChildSlot - a simple data class that holds the currently active child, if any.
- SlotNavigation - an interface that accepts navigation commands and forwards them to all subscribed observers.
Component Configurations¶
Each component created and managed by the Child Slot
has a configuration, please read the documentation about child configurations.
Initializing the Child Slot¶
There are three steps to initialize the Child Slot
:
- Create a new instance of
SlotNavigation
and assign it to a variable or a property. - Initialize the
Child Slot
using theComponentContext#childSlot
extension function and passSlotNavigation
into it along with other arguments. - The
childSlot
function returnsValue<ChildSlot>
that can be observed in the UI. Assign the returnedValue
to another property or a variable.
Example¶
Here is a very basic example of a child slot:
import com.arkivanov.decompose.ComponentContext
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()
}
}
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.slot.ChildSlot
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.childSlot
import com.arkivanov.decompose.router.slot.dismiss
import com.arkivanov.decompose.value.Value
import kotlinx.serialization.Serializable
interface RootComponent {
val dialog: Value<ChildSlot<*, DialogComponent>>
}
class DefaultRootComponent(
componentContext: ComponentContext,
) : RootComponent, ComponentContext by componentContext {
private val dialogNavigation = SlotNavigation<DialogConfig>()
override val dialog: Value<ChildSlot<*, DialogComponent>> =
childSlot(
source = dialogNavigation,
serializer = DialogConfig.serializer(), // Or null to disable navigation state saving
handleBackButton = true, // Close the dialog on back button press
) { config, childComponentContext ->
DefaultDialogComponent(
componentContext = childComponentContext,
message = config.message,
onDismissed = dialogNavigation::dismiss,
)
}
private fun showDialog(message: String) {
dialogNavigation.activate(DialogConfig(message = message))
}
@Serializable // kotlinx-serialization plugin must be applied
private data class DialogConfig(
val message: String,
)
}
Multiple Child Slots in a component¶
When multiple Child Slots
are used in one component, each such Child Slot
must have a unique key associated. The keys are required to be unique only within the parent (hosting) component, so it is ok for different components to have Child Slots
with same keys. An exception will be thrown if multiple Child Slots
with the same key are detected in a component.
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.childSlot
class Root(
componentContext: ComponentContext
) : ComponentContext by componentContext {
private val topNavigation = SlotNavigation<TopConfig>()
private val topSlot =
childSlot<TopConfig, TopChild>(
source = topNavigation,
key = "TopSlot",
// Omitted code
)
private val bottomNavigation = SlotNavigation<BottomConfig>()
private val bottomSlot =
childSlot<BottomConfig, BottomChild>(
source = bottomNavigation,
key = "BottomSlot",
// Omitted code
)
}