### Install Decompose Claude Plugin Source: https://arkivanov.github.io/Decompose/community Commands to install the Decompose Claude Plugin from the marketplace. ```bash /plugin marketplace add Litun/DecomposeClaudePlugin /plugin install decompose@Litun-DecomposeClaudePlugin ``` -------------------------------- ### Compose Navigation with Decompose Components Source: https://arkivanov.github.io/Decompose/tips-tricks/navigation-compose-component This example shows how to set up a NavHost in Jetpack Compose to navigate between screens, each potentially hosting a Decompose component. Use `rememberRetainedComponent` to manage component instances across recompositions. ```kotlin import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.arkivanov.decompose.ComponentContext @Composable fun App() { val nav = rememberNavController() NavHost(navController = nav, startDestination = "home") { composable("home") { HomeScreen(onShowDetails = { nav.navigate("details") }) } composable("details") { val detailsComponent = rememberRetainedComponent(factory = ::DetailsComponent) DetailsComponent(component = detailsComponent, onBack = nav::popBackStack) } } } @Composable fun HomeScreen(onShowDetails: () -> Unit) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Button(onClick = onShowDetails) { Text("Go to details") } } } class DetailsComponent( componentContext: ComponentContext, ) : ComponentContext by componentContext { // Some code here } @Composable fun DetailsComponent(component: DetailsComponent, onBack: () -> Unit) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Button(onClick = onBack) { Text("Go back") } } } ``` -------------------------------- ### Example Usage of ChildItemsLifecycleController Source: https://arkivanov.github.io/Decompose/extensions/compose Demonstrates integrating ChildItemsLifecycleController with a LazyColumn to manage child components. It observes child items and uses the controller to optimize their lifecycle based on scroll position. ```kotlin import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import com.arkivanov.decompose.extensions.compose.lazyitems.ChildItemsLifecycleController import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.foo.ItemComponent import com.arkivanov.sample.shared.foo.ItemsComponent @Composable fun ItemsContent(component: ItemsComponent) { val childItems by component.items.subscribeAsState() val lazyListState = rememberLazyListState() LazyColumn(state = lazyListState) { items(items = childItems.items) { config -> ItemContent(component = component.items[config]) } } ChildItemsLifecycleController( items = component.items, lazyListState = lazyListState, forwardPreloadCount = 3, // Preload 3 components forward backwardPreloadCount = 3, // Preload 3 components backward itemIndexConverter = { it }, ) } @Composable fun ItemContent(component: ItemComponent) { // Omitted code } ``` -------------------------------- ### Create Root Component in Other Platforms Source: https://arkivanov.github.io/Decompose/component/overview Prefer creating the root ComponentContext and component before starting Compose, typically in the main function, ensuring it happens on the UI thread. ```kotlin import androidx.compose.ui.window.application import com.arkivanov.decompose.DefaultComponentContext fun main() { // Create the root component before starting Compose. // Make sure that this happens on the UI thread. val root = DefaultRootComponent(componentContext = DefaultComponentContext(...)) // Start Compose application { // The rest of the code } } ``` -------------------------------- ### Implement StackRouterView for Navigation Source: https://arkivanov.github.io/Decompose/extensions/android Example of using `StackRouterView` to manage child views in a stack-like navigation structure within an Android layout. ```kotlin import android.view.View import com.arkivanov.decompose.extensions.android.ViewContext import com.arkivanov.decompose.extensions.android.child import com.arkivanov.decompose.extensions.android.layoutInflater import com.arkivanov.decompose.extensions.android.stack.StackRouterView fun ViewContext.RootView(component: RootComponent): View { val layout = layoutInflater.inflate(R.layout.counter_root, parent, false) val nextButton: View = layout.findViewById(R.id.button_next) val routerView: StackRouterView = layout.findViewById(R.id.router) nextButton.setOnClickListener { counterRoot.onNextChild() } // Create a child `ViewContext` for the permanent `CounterView` child(layout.findViewById(R.id.container_counter)) { // Reuse the `CounterView` CounterView(component.counter) } // Subscribe the `StackRouterView` to the `ChildStack` changes routerView.children(component.childStack, lifecycle) { parent, newStack, _ -> // Remove all existing views parent.removeAllViews() // Add the child view for the currently active child component parent.addView(CounterView(newStack.active.instance)) } return layout } ``` -------------------------------- ### Initialize Decompose Root Component with LifecycleRegistry Source: https://arkivanov.github.io/Decompose/getting-started/quick-start Initialize a LifecycleRegistry, pass it to the root ComponentContext, and attach it to the document. This setup is crucial for managing component lifecycles and integrating with browser history. ```kotlin import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.router.stack.webhistory.DefaultWebHistoryController import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume import com.arkivanov.essenty.lifecycle.stop import kotlinx.browser.window import react.create import react.dom.client.createRoot import web.dom.DocumentVisibilityState import web.dom.document import web.events.EventType @OptIn(ExperimentalDecomposeApi::class) fun main() { val lifecycle = LifecycleRegistry() val root = DefaultRootComponent( // Pass the LifecycleRegistry to the context componentContext = DefaultComponentContext(lifecycle = lifecycle), ... // Other dependencies here ) // Attach the LifecycleRegistry to document lifecycle.attachToDocument() // Render the UI createRoot(document.getElementById("app")!!).render( RootContent.create { component = root } ) } // Attaches the LifecycleRegistry to the document private fun LifecycleRegistry.attachToDocument() { fun onVisibilityChanged() { if (document.visibilityState == DocumentVisibilityState.visible) { resume() } else { stop() } } onVisibilityChanged() document.addEventListener(type = EventType("visibilitychange"), callback = { onVisibilityChanged() }) } ``` -------------------------------- ### Create Root Component in JVM/Desktop Application Source: https://arkivanov.github.io/Decompose/component/overview Ensure the root component is created on the UI thread before starting Compose for JVM/Desktop applications. Use runOnUiThread or equivalent for thread safety. ```kotlin import androidx.compose.ui.window.application import com.arkivanov.decompose.DefaultComponentContext fun main() { // Create the root component on the UI thread before starting Compose val root = runOnUiThread { DefaultRootComponent(componentContext = DefaultComponentContext(...)) } // Start Compose application { // The rest of the code } } ``` -------------------------------- ### Create Root Component in Android Activity Source: https://arkivanov.github.io/Decompose/component/overview Create the root ComponentContext and component on the UI thread before starting Compose in an Android Activity. ```kotlin import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import com.arkivanov.decompose.defaultComponentContext class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Create the root component before starting Compose val root = DefaultRootComponent(componentContext = defaultComponentContext()) // Start Compose setContent { // The rest of the code } } } ``` -------------------------------- ### Navigate with Main, Details, and Extra Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Sets the provided Main, Details, and Extra configurations for navigation. Use this to update all three panel types simultaneously. ```kotlin navigation.navigate(main = Main2, details = Details2, extra = Extra2) ``` ```kotlin navigation.navigate(main = Main2, details = null, extra = null) ``` -------------------------------- ### Navigate with Details and Extra Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Sets the provided Details and Extra configurations for navigation. The Main configuration remains unchanged. ```kotlin navigation.navigate(details = Details2, extra = Extra2) ``` ```kotlin navigation.navigate(details = null, extra = null) ``` -------------------------------- ### Initialize Root Component in Activity Source: https://arkivanov.github.io/Decompose/extensions/android Demonstrates how to initialize the root Decompose component and set up the view context within an Android Activity. ```kotlin import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.arkivanov.decompose.defaultComponentContext import com.arkivanov.decompose.extensions.android.DefaultViewContext import com.arkivanov.essenty.lifecycle.essentyLifecycle class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) val root = DefaultCounterComponent(defaultComponentContext()) val viewContext = DefaultViewContext( parent = findViewById(R.id.content), lifecycle = essentyLifecycle() ) viewContext.apply { parent.addView(CounterView(root)) } } } ``` -------------------------------- ### PanelsNavigator.navigate(main, details, extra) Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Sets the provided Main, Details, and Extra configurations for navigation. ```APIDOC ## navigate(main, details, extra) ### Description Sets the provided Main, Details, and Extra configurations for navigation. ### Method POST (Implied by state change) ### Endpoint /navigation/panels ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - **main** (Configuration, Optional) - The configuration for the Main panel. - **details** (Configuration, Optional) - The configuration for the Details panel. - **extra** (Configuration, Optional) - The configuration for the Extra panel. ### Request Example ```kotlin // Example 1: Setting all configurations navigation.navigate(main = Main2, details = Details2, extra = Extra2) // Example 2: Setting only main, details and extra to null navigation.navigate(main = Main2, details = null, extra = null) ``` ### Response #### Success Response (200) State updated with new configurations. #### Response Example ```json { "main": "Main2", "details": "Details2", "extra": "Extra2", "stack": "SINGLE" } ``` ``` -------------------------------- ### Counter Component State Management Source: https://arkivanov.github.io/Decompose/component/overview Example of a simple Counter component using MutableValue for state. The state is updated using the 'update' function. ```kotlin import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value import com.arkivanov.decompose.value.update class Counter { private val _state = MutableValue(State()) val state: Value = _state fun increment() { _state.update { it.copy(count = it.count + 1) } } data class State(val count: Int = 0) } ``` -------------------------------- ### Details Screen Composable with ViewModel Source: https://arkivanov.github.io/Decompose/tips-tricks/composable-viewmodel A Composable function for the Details screen that uses an AndroidX ViewModel. This is an example of a screen that might be difficult to migrate directly to Decompose. ```kotlin import androidx.compose.runtime.Composable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun DetailsContent(viewModel: DetailsViewModel = viewModel()) { // Omitted code } class DetailsViewModel : ViewModel() { // Omitted code } ``` -------------------------------- ### Configure Root Component for Deep Links Source: https://arkivanov.github.io/Decompose/navigation/stack/deeplinking Set up the root component to handle initial deep link data. If `initialItemId` is provided, the `ItemDetails` component will be the initial screen, with `ItemList` on the back stack. ```kotlin import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack import kotlinx.serialization.Serializable class DefaultRootComponent( componentContext: ComponentContext, initialItemId: Long? = null, // It can be any other type, e.g. a sealed class with all possible destinations ) : RootComponent, ComponentContext by componentContext { private val navigation = StackNavigation() private val stack = childStack( source = navigation, initialStack = { listOfNotNull( Config.List, if (initialItemId != null) Config.Details(itemId = initialItemId) else null, ) }, handleBackButton = true, childFactory = ::createChild, ) // Omitted code @Serializable private sealed class Config { @Serializable data object List : Config() @Serializable data class Details(val itemId: Long) : Config() } } ``` -------------------------------- ### Implement Reactive Input Handling Source: https://arkivanov.github.io/Decompose/navigation/stack/overview Implement child components to subscribe to input streams and process events asynchronously. This example uses RxJava's `Observable`. ```kotlin class DefaultItemListComponent( componentContext: ComponentContext, input: Observable, private val onItemSelected: (id: Long) -> Unit, ) : ItemListComponent, ComponentContext by componentContext, DisposableScope by componentContext.disposableScope() { init { // Subscribe to input input.subscribeScoped { when (it) { is ItemList.Input.ItemDeleted -> TODO("Handle item deleted") } } } override fun onItemClicked(id: Long) { onItemSelected(id) } } ``` -------------------------------- ### Create RootViewController for Compose (with ApplicationLifecycle) Source: https://arkivanov.github.io/Decompose/getting-started/quick-start Create a Kotlin file for RootViewController to be used with Compose. This function returns a UIViewController that hosts the Compose UI, rendering the root component. ```kotlin import androidx.compose.ui.window.ComposeUIViewController import platform.UIKit.UIViewController fun rootViewController(root: RootComponent): UIViewController = ComposeUIViewController { // Render the UI here } ``` -------------------------------- ### Push Configuration to Front Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation The `pushToFront` function moves a configuration to the top of the stack, removing it from the back stack if present. It compares configurations by equality. ```kotlin navigation.pushToFront(Configuration.A(2)) ``` ```kotlin navigation.pushToFront(Configuration.A(1)) ``` ```kotlin navigation.pushToFront(Configuration.A(2)) ``` -------------------------------- ### bringToFront Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation 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. ```APIDOC ## bringToFront(configuration) ### Description 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. The operation is performed as one transaction. If there is already a component with the same configuration, it will not be recreated. ### Method Signature `bringToFront(configuration: Configuration)` ### Parameters * `configuration` (Configuration) - The configuration to bring to the front of the stack. ### Example ```kotlin navigation.bringToFront(Configuration.B) ``` ``` -------------------------------- ### Back Animation Setup for iOS-like Gesture Source: https://arkivanov.github.io/Decompose/extensions/compose Configures the Decompose stack animation to use the iOS-like slide animator for predictive back gestures. Requires `BackHandler` and an `onBack` callback. ```kotlin actual fun backAnimation( backHandler: BackHandler, onBack: () -> Unit, ): StackAnimation = stackAnimation( animator = iosLikeSlide(), predictiveBackParams = { PredictiveBackParams( backHandler = backHandler, onBack = onBack, ) }, ) ``` ```kotlin private fun iosLikeSlide(animationSpec: FiniteAnimationSpec = tween()): StackAnimator = stackAnimator(animationSpec = animationSpec) { factor, direction -> Modifier .then(if (direction.isFront) Modifier else Modifier.fade(factor + 1F)) .offsetXFactor(factor = if (direction.isFront) factor else factor * 0.5F) } ``` ```kotlin private fun Modifier.fade(factor: Float) = drawWithContent { drawContent() drawRect(color = Color(red = 0F, green = 0F, blue = 0F, alpha = (1F - factor) / 4F)) } ``` ```kotlin private fun Modifier.offsetXFactor(factor: Float): Modifier = layout { val placeable = measurable.measure(constraints) layout(placeable.width, placeable.height) { placeable.placeRelative(x = (placeable.width.toFloat() * factor).toInt(), y = 0) } } ``` -------------------------------- ### Initialize Root Component for Desktop Compose Source: https://arkivanov.github.io/Decompose/getting-started/quick-start Initializes the root component for a Desktop Compose application using `LifecycleController` to bind the root lifecycle with the main window state. Ensure `runOnUiThread` is used for component creation on the UI thread. ```kotlin import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.ui.Modifier import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle.LifecycleController import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.sample.shared.DefaultRootComponent import com.arkivanov.sample.shared.RootContent fun main() { val lifecycle = LifecycleRegistry() // Always create the root component outside Compose on the UI thread val root = runOnUiThread { DefaultRootComponent( componentContext = DefaultComponentContext(lifecycle = lifecycle), ) } application { val windowState = rememberWindowState() LifecycleController(lifecycle, windowState) Window( onCloseRequest = ::exitApplication, state = windowState, title = "My Application" ) { MaterialTheme { Surface { RootContent(component = root, modifier = Modifier.fillMaxSize()) } } } } } ``` -------------------------------- ### RootContent with Experimental backAnimation Source: https://arkivanov.github.io/Decompose/extensions/compose Use this in your commonMain source set for the experimental Decompose Compose stack integration with predictive back gestures. ```kotlin import androidx.compose.runtime.Composable import com.arkivanov.decompose.extensions.compose.experimental.stack.ChildStack import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.StackAnimation import com.arkivanov.essenty.backhandler.BackHandler @Composable fun RootContent(component: RootComponent) { ChildStack( stack = component.childStack, animation = backAnimation( backHandler = component.backHandler, onBack = component::onBackClicked, ), ) { // Omitted code } } expect fun backAnimation( backHandler: BackHandler, onBack: () -> Unit, ): StackAnimation ``` -------------------------------- ### Selecting First Component Source: https://arkivanov.github.io/Decompose/navigation/pages/navigation Selects the first component in the navigation. ```kotlin navigation.selectFirst() ``` -------------------------------- ### Component Lifecycle Subscription - Kotlin Source: https://arkivanov.github.io/Decompose/component/lifecycle Demonstrates how to subscribe to component lifecycle events using callbacks or lambda functions. Use this to perform actions when a component is created, started, resumed, paused, stopped, or destroyed. ```kotlin import com.arkivanov.decompose.ComponentContext import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.doOnCreate import com.arkivanov.essenty.lifecycle.subscribe class SomeComponent( componentContext: ComponentContext ) : ComponentContext by componentContext { init { lifecycle.subscribe( object : Lifecycle.Callbacks { override fun onCreate() { /* Component created */ } // onStart, onResume, onPause, onStop, onDestroy } ) lifecycle.subscribe( onCreate = { /* Component created */ }, // onStart, onResume, onPause, onStop, onDestroy ) lifecycle.doOnCreate { /* Component created */ } // doOnStart, doOnResume, doOnPause, doOnStop, doOnDestroy } } ``` -------------------------------- ### Run Material MkDocs Locally with Docker Source: https://arkivanov.github.io/Decompose/getting-started/contributing Use these Docker commands to pull the Material MkDocs image and run a local server to preview documentation changes. ```bash # download the image docker pull squidfunk/mkdocs-material # run the server locally docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material ``` -------------------------------- ### Initialize StackNavigation Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation Instantiate the StackNavigation component to manage your navigation stack. Ensure the generic type parameter matches your navigation configuration type. ```kotlin val navigation = StackNavigation() ``` -------------------------------- ### Multiple Child Slots in a Component Source: https://arkivanov.github.io/Decompose/navigation/slot/overview Illustrates how to configure multiple Child Slots within a single component, each requiring a unique key for identification. This setup is for managing distinct UI elements like top and bottom navigation areas. ```kotlin 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() private val topSlot = childSlot( source = topNavigation, key = "TopSlot", // Omitted code ) private val bottomNavigation = SlotNavigation() private val bottomSlot = childSlot( source = bottomNavigation, key = "BottomSlot", // Omitted code ) } ``` -------------------------------- ### pushToFront(configuration) Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation Pushes the provided configuration to the top of the stack, removing it from the back stack if present. ```APIDOC ## pushToFront(configuration) ### Description 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. ### Method `pushToFront(configuration: Configuration)` ### Endpoint None (SDK method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **configuration** (Configuration) - Required - The configuration to push to the front of the stack. ### Request Example ```kotlin navigation.pushToFront(Configuration.A(2)) ``` ### Response #### Success Response (200) Stack updated with the configuration moved to the top and removed from the back stack if it existed there. #### Response Example None ``` -------------------------------- ### Navigate with Extra Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Sets the provided Extra configuration for navigation. The Main and Details configurations remain unchanged. ```kotlin navigation.navigate(extra = Extra2) ``` ```kotlin navigation.navigate(extra = null) ``` -------------------------------- ### PanelsNavigator.navigate(details, extra) Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Sets the provided Details and Extra configurations for navigation, keeping the Main configuration. ```APIDOC ## navigate(details, extra) ### Description Sets the provided Details and Extra configurations for navigation, keeping the Main configuration. ### Method POST (Implied by state change) ### Endpoint /navigation/panels ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - **details** (Configuration, Optional) - The configuration for the Details panel. - **extra** (Configuration, Optional) - The configuration for the Extra panel. ### Request Example ```kotlin // Example 1: Setting details and extra navigation.navigate(details = Details2, extra = Extra2) // Example 2: Setting details and extra to null navigation.navigate(details = null, extra = null) ``` ### Response #### Success Response (200) State updated with new configurations. #### Response Example ```json { "main": "Main1", "details": "Details2", "extra": "Extra2", "stack": "SINGLE" } ``` ``` -------------------------------- ### Create a Simple Child View Source: https://arkivanov.github.io/Decompose/extensions/android Shows how to inflate a layout and observe component models to update a simple child view in Android. ```kotlin import android.view.View import android.widget.TextView import com.arkivanov.decompose.extensions.android.ViewContext import com.arkivanov.decompose.extensions.android.layoutInflater import com.arkivanov.decompose.value.observe fun ViewContext.CounterView(component: CounterComponent): View { // Inflate the layout without adding it to the parent val layout = layoutInflater.inflate(R.layout.counter, parent, false) // Find required views val counterText: TextView = layout.findViewById(R.id.text_count) // Observe Counter models and update the view component.model.observe(lifecycle) { data -> counterText.text = data.text } return layout // Return the root of the inflated sub-tree } ``` -------------------------------- ### Push Configuration to Stack Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation Use the `push` function to add a new configuration to the top of the navigation stack. An exception is thrown if the configuration already exists in the stack. ```kotlin navigation.push(Configuration.C) ``` -------------------------------- ### Create PanelsNavigation Instance Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Instantiate PanelsNavigation in your component class to manage child panel configurations. ```kotlin val navigation = PanelsNavigation() ``` -------------------------------- ### pushNew(configuration) Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation Pushes the provided Configuration at the top of the stack. Does nothing if the provided Configuration is already on top of the stack. Throws an exception if the Configuration is present in the back stack. ```APIDOC ## pushNew(configuration) ### Description 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. ### Method `pushNew(configuration: Configuration)` ### Endpoint None (SDK method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **configuration** (Configuration) - Required - The configuration to push onto the stack. ### Request Example ```kotlin navigation.pushNew(Configuration.C) ``` ### Response #### Success Response (200) Stack updated with the new configuration at the top, or remains unchanged if the configuration was already on top. #### Response Example None ``` -------------------------------- ### DefaultMyPanelsComponent Implementation Source: https://arkivanov.github.io/Decompose/navigation/web-navigation Implements the MyPanelsComponent interface, setting up child panels navigation and web navigation integration. Requires configuration for deep links and factories. ```kotlin import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.panels.ChildPanels import com.arkivanov.decompose.router.panels.PanelsNavigation import com.arkivanov.decompose.router.panels.childPanels import com.arkivanov.decompose.router.panels.childPanelsWebNavigation import com.arkivanov.decompose.router.webhistory.WebNavigation import com.arkivanov.decompose.value.Value import kotlinx.serialization.Serializable class DefaultMyPanelsComponent( componentContext: ComponentContext, deepLinkUrl: String?, // Or your favourite data structure, like Uri, etc. ) : MyPanelsComponent, ComponentContext by componentContext { private val nav = PanelsNavigation() private val _panels: Value> = childPanels( source = nav, serializers = MainConfig.serializer() to DetailsConfig.serializer(), initialPanels = { TODO("Use the deepLinkUrl parameter to initialize the navigation") }, mainFactory = { ... }, detailsFactory = { ... }, ) override val panels: Value> = _panels override val webNavigation: WebNavigation<*> = childPanelsWebNavigation( navigator = nav, panels = _panels, serializers = MainConfig.serializer() to DetailsConfig.serializer(), pathMapper = { panels -> TODO("Return a path for the navigation state") }, // Optional parametersMapper = { panels -> TODO("Return a Map with parameters for the navigation state") }, // Optional childSelector = { panels -> TODO("Return a WebNavigationOwner for the navigation state") }, // Optional ) @Serializable private data class MainConfig(...) @Serializable private data class DetailsConfig(...) } ``` -------------------------------- ### Configure Predictive Back Animation in RootContent (Experimental API) Source: https://arkivanov.github.io/Decompose/extensions/compose The experimental API uses `PredictiveBackParams` within `stackAnimation` for predictive back gesture configuration. This allows for a more integrated approach with existing animations. ```kotlin import androidx.compose.runtime.Composable import com.arkivanov.decompose.extensions.compose.experimental.stack.ChildStack import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.PredictiveBackParams import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.fade import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.plus import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.scale import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.stackAnimation @Composable fun RootContent(component: RootComponent) { ChildStack( stack = component.childStack, animation = stackAnimation( animator = fade() + scale(), predictiveBackParams = { PredictiveBackParams( backHandler = component.backHandler, onBack = component::onBackClicked, ) }, ), ) { // Omitted code } } ``` -------------------------------- ### Pop to First Configuration Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation The `popToFirst` function removes all configurations except the first one, making the initial configuration the active top of the stack. ```kotlin navigation.popToFirst() ``` -------------------------------- ### Creating PagesNavigation Instance Source: https://arkivanov.github.io/Decompose/navigation/pages/navigation Instantiate PagesNavigation with a specific configuration type. ```kotlin val navigation = PagesNavigation() ``` -------------------------------- ### Activate Main Component Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Activates the Main component with the specified configuration and dismisses any currently active Main component. ```kotlin navigation.activateMain(main = Main2) ``` -------------------------------- ### popToFirst() Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation Pops configurations at the top of the stack so that the first configuration becomes active (the new top of the stack). ```APIDOC ## popToFirst() ### Description Pops configurations at the top of the stack so that the first configuration becomes active (the new top of the stack). ### Method `popToFirst()` ### Endpoint None (SDK method) ### Parameters None ### Request Example ```kotlin navigation.popToFirst() ``` ### Response #### Success Response (200) All configurations except the first one are popped from the stack. #### Response Example None ``` -------------------------------- ### push(configuration) Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation Pushes the provided Configuration at the top of the stack. Throws an exception if the Configuration is already present in the stack. ```APIDOC ## push(configuration) ### Description 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. ### Method `push(configuration: Configuration)` ### Endpoint None (SDK method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **configuration** (Configuration) - Required - The configuration to push onto the stack. ### Request Example ```kotlin navigation.push(Configuration.C) ``` ### Response #### Success Response (200) Stack updated with the new configuration at the top. #### Response Example None ``` -------------------------------- ### PanelsNavigator.activateMain(main) Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Activates the Main component and dismisses any currently active Main component. ```APIDOC ## activateMain(main) ### Description Activates the Main component represented by the specified `main` configuration, and dismisses (destroys) any currently active Main component. ### Method POST (Implied by state change) ### Endpoint /navigation/panels/main ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - **main** (Configuration) - The configuration for the Main panel to activate. ### Request Example ```kotlin navigation.activateMain(main = Main2) ``` ### Response #### Success Response (200) Main component activated and previous one dismissed. #### Response Example ```json { "main": "Main2", "details": "Details1", "extra": "Extra1", "stack": "SINGLE" } ``` ``` -------------------------------- ### Implement StackAnimation (Experimental API) Source: https://arkivanov.github.io/Decompose/extensions/compose The experimental StackAnimation API provides similar functionality to the default API but may have different underlying implementations or features. ```kotlin import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.extensions.compose.experimental.stack.ChildStack import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.StackAnimation import com.arkivanov.decompose.router.stack.ChildStack @Composable fun RootContent(component: RootComponent) { ChildStack( stack = component.childStack, animation = someAnimation(), ) { // Omitted code } } fun someAnimation(): StackAnimation = StackAnimation { stack: ChildStack, modifier: Modifier, content: @Composable AnimatedVisibilityScope.(Child.Created) -> Unit -> // Render each frame here } ``` -------------------------------- ### Control Lifecycle on Desktop with LifecycleController Source: https://arkivanov.github.io/Decompose/extensions/compose Utilize the `LifecycleController()` composable to make the `LifecycleRegistry` react to window state changes on Jetbrains Compose Desktop. This ensures proper lifecycle event triggering for window minimization, restoration, or closure. ```kotlin import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle.LifecycleController import com.arkivanov.essenty.lifecycle.LifecycleRegistry fun main() { val lifecycle = LifecycleRegistry() val root = runOnUiThread { DefaultRootComponent(DefaultComponentContext(lifecycle)) } application { val windowState = rememberWindowState() LifecycleController(lifecycle, windowState) Window(onCloseRequest = ::exitApplication, state = windowState) { RootContent(root) } } } ``` -------------------------------- ### Create ItemsNavigation Instance Source: https://arkivanov.github.io/Decompose/navigation/items/navigation Instantiate ItemsNavigation in your component class. Specify the Configuration type for the navigation state. ```kotlin val navigation = ItemsNavigation() ``` -------------------------------- ### Add Experimental Compose Extensions (Groovy) Source: https://arkivanov.github.io/Decompose/getting-started/installation Add the experimental Decompose Compose extensions to your build.gradle file using Groovy syntax. ```groovy implementation "com.arkivanov.decompose:extensions-compose-experimental:" ``` -------------------------------- ### Use AppDelegate in App Entrypoint (with ApplicationLifecycle) Source: https://arkivanov.github.io/Decompose/getting-started/quick-start Integrate the AppDelegate into your application's main entry point using the @main attribute. This sets up the RootView with the root component from the AppDelegate. ```swift @main struct iOSApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate: AppDelegate var body: some Scene { WindowGroup { RootView(root: appDelegate.root) } } } ``` -------------------------------- ### Using ViewModelStoreComponent in Root Component Source: https://arkivanov.github.io/Decompose/tips-tricks/composable-viewmodel Demonstrates how to use the ViewModelStoreComponent within a Decompose Root component to manage child components and navigation, including passing arguments to child configurations. ```kotlin import androidx.core.os.bundleOf import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.pushNew import com.arkivanov.decompose.value.Value import com.arkivanov.sample.app.RootComponent.Child import com.arkivanov.sample.app.RootComponent.Child.ListChild import com.arkivanov.sample.app.RootComponent.Child.MainChild import kotlinx.serialization.Serializable class DefaultRootComponent( componentContext: ComponentContext, ) : RootComponent, ComponentContext by componentContext { private val nav = StackNavigation() override val childStack: Value> = childStack( source = nav, serializer = Config.serializer(), initialConfiguration = Config.Main, ) { config, ctx -> when (config) { is Config.Main -> ListChild( component = DefaultMainComponent( componentContext = ctx, onShowDetails = { text -> nav.pushNew(Config.Details(text = text)) // Pass text to the Details screen }, ), ) is Config.Details -> MainChild( ``` -------------------------------- ### Combining Fade and Scale Animations (Experimental API) Source: https://arkivanov.github.io/Decompose/extensions/compose Combine fade and scale animations using the experimental API. This approach allows for sequential application of animations by using the `plus` operator. ```kotlin import androidx.compose.runtime.Composable import com.arkivanov.decompose.extensions.compose.experimental.stack.ChildStack import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.fade import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.plus import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.scale import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.stackAnimation @Composable fun RootContent(component: RootComponent) { ChildStack( stack = component.childStack, animation = stackAnimation(fade() + scale()) ) { // Omitted code } } ``` -------------------------------- ### Bring Configuration to Front of Stack Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation Removes all components with the same configuration class and places the specified configuration at the top of the stack. This is useful for bottom navigation patterns. The operation is atomic. ```kotlin navigation.bringToFront(Configuration.B) ``` -------------------------------- ### Add Experimental Compose Extensions (Kotlin) Source: https://arkivanov.github.io/Decompose/getting-started/installation Add the experimental Decompose Compose extensions to your build.gradle.kts file using Kotlin syntax. ```kotlin implementation("com.arkivanov.decompose:extensions-compose-experimental:") ``` -------------------------------- ### Provide ViewModelStoreOwner via LocalViewModelStoreOwner Source: https://arkivanov.github.io/Decompose/tips-tricks/composable-viewmodel Provides the child ViewModelStoreOwner via the LocalViewModelStoreOwner provider in Jetpack Compose, making the ViewModelStore available to the Details screen. ```kotlin import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import com.arkivanov.decompose.extensions.compose.stack.Children import com.arkivanov.sample.app.RootComponent.Child.MainChild import com.arkivanov.sample.app.RootComponent.Child.ListChild @Composable fun RootContent(component: RootComponent) { Children(stack = component.childStack) { (_, child) -> when (child) { is ListChild -> MainContent(child.component) is MainChild -> CompositionLocalProvider(LocalViewModelStoreOwner provides child.component) { DetailsContent() // The Details screen now has its own ViewModelStore } } } } ``` -------------------------------- ### replaceAll Source: https://arkivanov.github.io/Decompose/navigation/stack/navigation 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. ```APIDOC ## replaceAll(vararg configurations) ### Description 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. ### Method Signature `replaceAll(configurations: Configuration...)` ### Parameters * `configurations` (vararg Configuration) - The new configurations to replace the existing stack with. ### Example ```kotlin navigation.replaceAll(Configuration.B, Configuration.C, Configuration.D) ``` ``` -------------------------------- ### Implement Pages Component with Navigation Source: https://arkivanov.github.io/Decompose/navigation/pages/overview Implements the main pages component, managing multiple child pages and navigation. State is preserved by default; set `serializer = null` to disable. ```kotlin import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.pages.ChildPages import com.arkivanov.decompose.router.pages.Pages import com.arkivanov.decompose.router.pages.PagesNavigation import com.arkivanov.decompose.router.pages.childPages import com.arkivanov.decompose.router.pages.select import com.arkivanov.decompose.value.Value import kotlinx.serialization.Serializable interface PagesComponent { val pages: Value> fun selectPage(index: Int) } class DefaultPagesComponent( componentContext: ComponentContext, ) : PagesComponent, ComponentContext by componentContext { private val navigation = PagesNavigation() override val pages: Value> = childPages( source = navigation, serializer = Config.serializer(), // Or null to disable navigation state saving initialPages = { Pages( items = List(10) { index -> Config(data = "Item $index") }, selectedIndex = 0, ) }, ) { config, childComponentContext -> DefaultPageComponent( componentContext = childComponentContext, data = config.data, ) } override fun selectPage(index: Int) { navigation.select(index = index) } @Serializable // kotlinx-serialization plugin must be applied private data class Config(val data: String) } ``` -------------------------------- ### Create SlotNavigation Instance Source: https://arkivanov.github.io/Decompose/navigation/slot/navigation Instantiate SlotNavigation with a specific configuration type. This is typically done within your component class. ```kotlin val navigation = SlotNavigation() ``` -------------------------------- ### PanelsNavigator.navigate(extra) Source: https://arkivanov.github.io/Decompose/navigation/panels/navigation Sets the provided Extra configuration for navigation, keeping Main and Details configurations. ```APIDOC ## navigate(extra) ### Description Sets the provided Extra configuration for navigation, keeping Main and Details configurations. ### Method POST (Implied by state change) ### Endpoint /navigation/panels ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - **extra** (Configuration, Optional) - The configuration for the Extra panel. ### Request Example ```kotlin // Example 1: Setting extra navigation.navigate(extra = Extra2) // Example 2: Setting extra to null navigation.navigate(extra = null) ``` ### Response #### Success Response (200) State updated with new configuration. #### Response Example ```json { "main": "Main1", "details": "Details1", "extra": "Extra2", "stack": "SINGLE" } ``` ``` -------------------------------- ### Web Navigation for Child Pages Implementation Source: https://arkivanov.github.io/Decompose/navigation/web-navigation Implements a component that manages child pages and integrates with web history. Uses `childPagesWebNavigation` to link the navigator and pages to the web history. ```kotlin import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.pages.ChildPages import com.arkivanov.decompose.router.pages.PagesNavigation import com.arkivanov.decompose.router.pages.childPages import com.arkivanov.decompose.router.pages.childPagesWebNavigation import com.arkivanov.decompose.router.webhistory.WebNavigation import com.arkivanov.decompose.value.Value import kotlinx.serialization.Serializable class DefaultMyPagesComponent( componentContext: ComponentContext, deepLinkUrl: String?, // Or your favourite data structure, like Uri, etc. ) : MyPagesComponent, ComponentContext by componentContext { private val nav = PagesNavigation() private val _pages: Value> = childPages( source = nav, serializer = Config.serializer(), initialPages = { TODO("Use the deepLinkUrl parameter to initialize the navigation") }, childFactory = { ... }, ) override val pages: Value> = _pages override val webNavigation: WebNavigation<*> = childPagesWebNavigation( navigator = nav, pages = _pages, serializer = Config.serializer(), pathMapper = { pages -> TODO("Return a path for the navigation state") }, // Optional parametersMapper = { pages -> TODO("Return a Map with parameters for the navigation state") }, // Optional childSelector = { child -> TODO("Return a WebNavigationOwner for the child") }, // Optional ) @Serializable private data class Config(...) } ``` -------------------------------- ### Fade Animation (Experimental API) Source: https://arkivanov.github.io/Decompose/extensions/compose Utilize the experimental API for fade animations. This version requires importing from `com.arkivanov.decompose.extensions.compose.experimental.stack`. ```kotlin import androidx.compose.runtime.Composable import com.arkivanov.decompose.extensions.compose.experimental.stack.ChildStack import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.fade import com.arkivanov.decompose.extensions.compose.experimental.stack.animation.stackAnimation @Composable fun RootContent(component: RootComponent) { ChildStack( stack = component.childStack, animation = stackAnimation(fade()), ) { // Omitted code } } ``` -------------------------------- ### Standard Android-like System Animation with Predictive Back Source: https://arkivanov.github.io/Decompose/extensions/compose Implements a standard Android-like back gesture animation using `predictiveBackAnimation`. It includes a fallback animation for cases where predictive back is not available. ```kotlin import androidx.compose.runtime.Composable import com.arkivanov.decompose.extensions.compose.stack.Children import com.arkivanov.decompose.extensions.compose.stack.animation.fade import com.arkivanov.decompose.extensions.compose.stack.animation.plus import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.androidPredictiveBackAnimatable import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.predictiveBackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.scale import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation @Composable fun RootContent(component: RootComponent) { Children( stack = component.childStack, animation = predictiveBackAnimation( backHandler = component.backHandler, fallbackAnimation = stackAnimation(fade() + scale()), selector = { backEvent, _, _ -> androidPredictiveBackAnimatable(backEvent) }, onBack = component::onBackClicked, ), ) { // Omitted code } } ```