Kotlin Multiplatform lifecycle-aware business logic components (aka BLoCs) with routing functionality and pluggable UI (Jetpack Compose, SwiftUI, JS React, etc.), inspired by Badoos RIBs fork of the Uber RIBs framework

Overview

Maven Central License Twitter URL

badge badge badge badge badge badge badge

Decompose

Please see the project website for documentation and APIs.

Decompose is a Kotlin Multiplatform library for breaking down your code into lifecycle-aware business logic components (aka BLoC), with routing functionality and pluggable UI (Jetpack Compose, Android Views, SwiftUI, JS React, etc.). This project is inspired by Badoos RIBs fork of the Uber RIBs framework.

Setup

Setup Decompose in your Gradle project

Overview

  • Components - every component represents a piece of logic with lifecycle and optional pluggable UI.
  • ComponentContext - provided to every component with the tools for routing, state keeping, instance keeping and lifecycle
  • Routers - responsible for managing components with a backstack and its own Lifecycle
  • StateKeeper - preserve state during configuration changes and/or process death
  • InstanceKeeper - retain instances in your components (similar to AndroidX ViewModel)

Component hierarchy

Pluggable UI hierarchy

Typical component structure

Samples

Check out the project website for a full description of each sample.

Articles

Author

Twitter: @arkann1985

If you like this project you can always Buy Me A Coffee ;-)

Comments
  • Navigation does not work on chrome ios

    Navigation does not work on chrome ios

    Only occurs on chrome for mobile ios devices. After debugging, it appears the issue is that routerState.activeChild.instance does not update after child is created.

    //This code does not get executed.

    Crossfade(
                target = routerState.activeChild.instance, 
                attrs = {
                    style {
                        width(100.percent)
                        height(100.percent)
                        position(Position.Relative)
                        left(0.px)
                        top(0.px)
                    }
                }
            ) { child ->
                when (child) {
                    is Nav.Child.Splash -> Splash(component)
                    is Nav.Child.Inbox -> ChatUI(component)
                    is Nav.Child.Home -> Home(component)
                    is Nav.Child.HomePage -> HomePage(component)
            }
        }
    `
    bug 
    opened by amuna 32
  • Logo wanted!

    Logo wanted!

    Decompose needs its own logo. The logo should express the main purpose of the library:

    Decompose breaks the code down into small and independent components and organizes them into trees.

    Navigation.

    Something like a few coloured nodes connected into a tree, some of the nodes are grayed out (inactive). That's just my imagination. :-)

    help wanted 
    opened by arkivanov 11
  • Child gets recomposed more than once at startup in Compose Desktop

    Child gets recomposed more than once at startup in Compose Desktop

    I have 2 problems at the moment.

    1. When you launch the app on Compose Desktop the initial configuration destination gets called more than once which causes the Children block to be called more than once causing multiple recompositions which can lead to a crash.

    You can see here on the first screenshot that at the launch there are multiple calls to same thing (related to the Home Screen): Screenshot 2022-03-23 090508

    But after you navigate to something like a Settings Screen. You only see one call for each. Even when you pop back to the Home screen the home screen composable will be called only once this time: Screenshot 2022-03-23 090709

    I am using Decompose version: 0.5.2. Github link to the navigation and ui part of the app in question is this: https://github.com/Thinkrchive/Thinkrchive-Multiplatform/tree/main/desktopApp/src/jvmMain/kotlin/work/racka/thinkrchive/v2/desktop/ui

    The navigation folder contains RootComponent and it's implementations. The screen folder contains each screen composable with it component Interface and Implementation.

    I followed the official guide for using compose-extensions.

    1. Navigating to my About screen fails. When I click the About button the event is not propagated from the comoposable to the component creation inside my NavHostComponent

    What's weird is that every other call like for settingsScreen or donateScreen work as expected just the aboutScreen call that has issues.

    Also using crossfade() or crossfadeScale() crashes the app or refuses any navigation calls.

    documentation question 
    opened by racka98 10
  • Child is not created on forward press. Composable does not render.

    Child is not created on forward press. Composable does not render.

    This is the flow when website is launched.

    1. Starts on Homepage.
    2. Click on Button to show next screen. (router.push(Config.Splash) )
    3. user press back button (returns to homepage)
    4. user pressed forward button
    5. Blank white screen is seen. How to create child when forward is pressed?
    private val router: Router<Config, Any> =
            router(
                initialStack = { getInitialStack(deepLink) },
                handleBackButton = true,
                childFactory = ::createChild
            )
    
    private fun createChild(config: Config, componentContext: ComponentContext): Any =
            when (config) {
                is Config.Splash -> Nav.Child.Splash()
                is Config.HomePage -> Nav.Child.HomePage()
                else -> {}
            }
    
    
    question 
    opened by amuna 10
  • Add ModalBottomSheet with Child Overlay example

    Add ModalBottomSheet with Child Overlay example

    Hello, it would be great to add an example how to work with ModalBottomSheet in Compose UI with Child Overlay and also make a convenient Composable function like a Children but for ModalBottomSheet. I've implemented this one and it's work, but may be it's not perfect solution:

    @Composable
    fun <C : Any, T : Any> ModalBottomSheetOverlay(
        childOverlay: ChildOverlay<C, T>,
        onDismiss: () -> Unit,
        sheetContent: @Composable (child: Child.Created<C, T>?) -> Unit,
        modifier: Modifier = Modifier,
        skipHalfExpanded: Boolean = false,
        sheetShape: Shape = MaterialTheme.shapes.large,
        sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
        sheetBackgroundColor: Color = MaterialTheme.colors.surface,
        sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
        scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
        content: @Composable () -> Unit
    ) {
        val overlay = childOverlay.overlay
        val bottomSheetState = rememberModalBottomSheetState(
            initialValue = ModalBottomSheetValue.Hidden,
            skipHalfExpanded = skipHalfExpanded,
            confirmStateChange = { value ->
                if (value == ModalBottomSheetValue.Hidden) {
                    onDismiss()
                    return@rememberModalBottomSheetState false
                }
                true
            }
        )
        var lastOverlay by remember { mutableStateOf(overlay) }
    
        SideEffect {
            if (overlay != null) {
                lastOverlay = overlay
            }
        }
    
        LaunchedEffect(overlay) {
            if (overlay != null) {
                bottomSheetState.show()
            } else {
                bottomSheetState.hide()
                lastOverlay = null
            }
        }
    
        ModalBottomSheetLayout(
            sheetContent = {
                sheetContent.invoke(overlay ?: lastOverlay)
            },
            sheetState = bottomSheetState,
            modifier = modifier,
            sheetShape = sheetShape,
            sheetElevation = sheetElevation,
            sheetBackgroundColor = sheetBackgroundColor,
            sheetContentColor = sheetContentColor,
            scrimColor = scrimColor,
            content = content
        )
    }
    
    opened by AJIEKCX 8
  • (Android) Using coroutines when creating a child crashes the app if the component context is created using `defaultComponentContext()`

    (Android) Using coroutines when creating a child crashes the app if the component context is created using `defaultComponentContext()`

    Hi. I just found out that in order to create a Decompose component context using the defaultComponentContext function, which uses lifecycle.EssentyLifecycleInterop.subscribe underneath, breaks the app (most likely because of its interaction with androidx.lifecycle), and the workaround is to either not use coroutines when creating a child or create the child in Dispatchers.Main. Otherwise the app crashes immediately with error logs similar to this (Replaced app package with com.company.app):

    E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
        Process: com.company.app, PID: 25237
        java.lang.IllegalStateException: Method addObserver must be called on the main thread
            at androidx.lifecycle.LifecycleRegistry.enforceMainThreadIfNeeded(LifecycleRegistry.java:323)
            at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:178)
            at com.arkivanov.essenty.lifecycle.EssentyLifecycleInterop.subscribe(AndroidExt.kt:32)
            at com.arkivanov.decompose.lifecycle.MergedLifecycle.<init>(MergedLifecycle.kt:27)
            at com.arkivanov.decompose.lifecycle.MergedLifecycle.<init>(MergedLifecycle.kt:15)
            at com.arkivanov.decompose.router.stack.RouterEntryFactoryImpl.invoke(RouterEntryFactoryImpl.kt:26)
            at com.arkivanov.decompose.router.stack.RouterEntryFactory$DefaultImpls.invoke$default(RouterEntryFactory.kt:8)
            at com.arkivanov.decompose.router.stack.StackControllerImpl.navigate(StackControllerImpl.kt:39)
            at com.arkivanov.decompose.router.stack.ChildStackController.navigateActual(ChildStackController.kt:45)
            at com.arkivanov.decompose.router.stack.ChildStackController.access$navigateActual(ChildStackController.kt:14)
            at com.arkivanov.decompose.router.stack.ChildStackController$queue$1.invoke(ChildStackController.kt:26)
            at com.arkivanov.decompose.router.stack.ChildStackController$queue$1.invoke(ChildStackController.kt:26)
            at com.arkivanov.decompose.SerializedQueue.drain(SerializedQueue.kt:23)
            at com.arkivanov.decompose.SerializedQueue.offer(SerializedQueue.kt:17)
            at com.arkivanov.decompose.router.stack.ChildStackController$eventObserver$1.invoke(ChildStackController.kt:38)
            at com.arkivanov.decompose.router.stack.ChildStackController$eventObserver$1.invoke(ChildStackController.kt:38)
            at com.arkivanov.decompose.Relay$queue$1.invoke(Relay.kt:13)
            at com.arkivanov.decompose.Relay$queue$1.invoke(Relay.kt:12)
            at com.arkivanov.decompose.SerializedQueue.drain(SerializedQueue.kt:23)
            at com.arkivanov.decompose.SerializedQueue.offer(SerializedQueue.kt:17)
            at com.arkivanov.decompose.Relay.accept(Relay.kt:25)
            at com.arkivanov.decompose.router.stack.StackNavigationImpl.navigate(StackNavigationFactory.kt:18)
            at com.arkivanov.decompose.router.stack.StackNavigatorExtKt.push(StackNavigatorExt.kt:16)
            at com.arkivanov.decompose.router.stack.StackNavigatorExtKt.push$default(StackNavigatorExt.kt:15)
            at com.company.app.root.RootComponentImpl$createChild$createAuthScreen$1.invoke-bFv73x8(RootComponentImpl.kt:42)
            at com.company.app.root.RootComponentImpl$createChild$createAuthScreen$1.invoke(RootComponentImpl.kt:42)
            at com.company.app.authentication.AuthenticationScreenImpl$1.invokeSuspend(AuthenticationScreenImpl.kt:51)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
            at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
            at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
        	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@1089d7d, Dispatchers.Default]
    

    MainActivity.kt:

    import androidx.activity.compose.setContent
    import androidx.appcompat.app.AppCompatActivity
    import com.arkivanov.decompose.defaultComponentContext
    // ...
    import android.os.Bundle
    
    internal val app = MyCustomApplicationStarterLogic()
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            app.init(AppInitData(this, defaultComponentContext()))
            setContent { ApplicationScreen(app.root) }
        }
    }
    

    Emulator: Pixel 4 XL API 25 (Android 7.1.1 Google APIs | x86)

    Just wanted to report this (edge?) case.

    question 
    opened by YektaDev 7
  • Accessing non-final property lifecycle in constructor

    Accessing non-final property lifecycle in constructor

    I was attempting to do something similar to this example, which led to the following warning:

    class Foo(context: ComponentContext) : ComponentContext by context {
        private val screenLifecycle = object : Lifecycle.Callbacks {
            override fun onCreate() { /*...*/ }
            override fun onStart() { /*...*/ }
            override fun onResume() { /*...*/ }
            override fun onPause() { /*...*/ }
            override fun onStop() { /*...*/ }
            override fun onDestroy() { /*...*/ }
        }
    
        init {
            lifecycle.subscribe(screenLifecycle)
            //^^^^^^^
            //Accessing non-final property lifecycle in constructor 
        }
        // ...
    }
    

    Since I tried a simple logging inside onCreate of screenLifecycle and nothing was shown, I wanted to see whether this warning is the reason or it's somehow related to another issue.

    question 
    opened by YektaDev 7
  • Problem with configurations containing 3rd-party non-parcelable classes.

    Problem with configurations containing 3rd-party non-parcelable classes.

    Is it possible to use arrow.core.Either (or other classes from 3rd-party multiplatform libraries which isn't inherit Parcelable / Serializable) as part of configuration class?

    Context: I want to store (inside some of configurations) something like Either<TemporaryView, String>, where the .right() is for an id of DbView (I don't want to store it inside configuration) and .left() is for a temporary view (which can be saved).

    Right now my configuration looks like this:

    // main module
    sealed class Screen : Parcelable {
        @Parcelize
        data class Notes(val filter: NotesFilterInfo) : Screen()
        ...
    }
    
    // shared module without access to parcelable
    data class NotesFilterInfo(
        @Serializable(with = EitherSerializer::class)
        tempOrId: Either<NotesFilter, String>
    ) : java.io.Serializable
    
    @Serializable
    data class NotesFilter(val id: String, val tags: List<String>) : java.io.Serializable
    

    And it fails when activity paused:

     java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = NotesFilterInfo)
            at android.os.Parcel.writeSerializable(Parcel.java:1833)
            at NotesFilterInfo.writeToParcel(Unknown Source:16)
            at android.os.Parcel.writeParcelable(Parcel.java:1801)
            at android.os.Parcel.writeValue(Parcel.java:1707)
            at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
            at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
            at android.os.Bundle.writeToParcel(Bundle.java:1253)
            at android.os.Parcel.writeBundle(Parcel.java:997)
            at com.arkivanov.essenty.parcelable.AndroidParcelableContainer.writeToParcel(AndroidParcelableContainer.kt:32)
            ...
         Caused by: java.io.NotSerializableException: arrow.core.Either$Right
            at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
            at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
            at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
            at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
    E/AndroidRuntime:     at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
            at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
            at android.os.Parcel.writeSerializable(Parcel.java:1828)
            	... 51 more
    

    Don't sure how to solve this problem, so I have some sub-questions:

    1. Is it actully enough to implement Serializable to have Parcelable? (NotesFilterInfo is contained inside shared module without access to Parcelable)
    2. Is it possible to define custom router with serializable configuration? (my configurations isn't big and Parcelable implementation probably hasn't perfomance gain comparing to Serializable implementation)
    question 
    opened by evjava 7
  • Can't start app only with Navigation <= No interface method startRestartGroup on ChildrenKt.Children(Children.kt:22)

    Can't start app only with Navigation <= No interface method startRestartGroup on ChildrenKt.Children(Children.kt:22)

    I followed this tutorial. Of course, I checked this on medium.com

    I almost only copied codes from the tutorial and changed the package name.

    When starting desktop on InteliiJ IDEA:

    Exception in thread "main" java.lang.NoSuchMethodError: androidx.compose.runtime.Composer.startRestartGroup(ILjava/lang/String;)Landroidx/compose/runtime/Composer;
    	at com.arkivanov.decompose.extensions.compose.jetbrains.ChildrenKt.Children(Children.kt:22)
    	at net.liplum.common.ScreenKt.Root(Screen.kt:90)
    	at net.liplum.common.ComposableSingletons$AppKt$lambda-1$1.invoke(App.kt:10)
    	at net.liplum.common.ComposableSingletons$AppKt$lambda-1$1.invoke(App.kt:9)
    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    	at androidx.compose.material.MaterialTheme_desktopKt.PlatformMaterialTheme(MaterialTheme.desktop.kt:26)
    	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:82)
    	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:81)
    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    	at androidx.compose.material.TextKt.ProvideTextStyle(Text.kt:265)
    	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
    	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:80)
    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    	at androidx.compose.material.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:72)
    	at net.liplum.common.AppKt.App(App.kt:9)
    	at net.liplum.desktop.ComposableSingletons$MainKt$lambda-1$1.invoke(Main.kt:8)
    	at net.liplum.desktop.ComposableSingletons$MainKt$lambda-1$1.invoke(Main.kt:7)
    

    When starting android on Android Studio.

    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.myapplication, PID: 24932
        java.lang.NoSuchMethodError: No interface method startRestartGroup(ILjava/lang/String;)Landroidx/compose/runtime/Composer; in class Landroidx/compose/runtime/Composer; or its super classes (declaration of 'androidx.compose.runtime.Composer' appears in /data/app/~~cveqtxczmcjgqqWLQeHzrA==/com.myapplication-ztE8pb2vmNxy0BTJ1BWwxg==/base.apk)
            at com.arkivanov.decompose.extensions.compose.jetbrains.ChildrenKt.Children(Children.kt:22)
            at net.liplum.common.ScreenKt.Root(Screen.kt:90)
            at net.liplum.common.ComposableSingletons$AppKt$lambda-1$1.invoke(App.kt:10)
            at net.liplum.common.ComposableSingletons$AppKt$lambda-1$1.invoke(App.kt:9)
            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
            at androidx.compose.material.MaterialTheme_androidKt.PlatformMaterialTheme(MaterialTheme.android.kt:23)
            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:82)
            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:81)
            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
            at androidx.compose.material.TextKt.ProvideTextStyle(Text.kt:265)
            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:80)
            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
            at androidx.compose.material.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:72)
            at net.liplum.common.AppKt.App(App.kt:9)
            at net.liplum.android.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:13)
            at net.liplum.android.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:12)
    
    opened by liplum 7
  • Gradle fails in Idea Community desktop compose template

    Gradle fails in Idea Community desktop compose template

    Tried on macbook m1 Idea Community version jdk: azul-16.0.2

    Create a default desktop compose template project

    Steps:

    • Add
    implementation("com.arkivanov.decompose:decompose:0.6.0")
    implementation("com.arkivanov.decompose:extensions-compose-jetbrains:0.6.0")
    

    to gradle dependencies

    • try to run gradle run task

    Result: Gradle failed to run

    Caused by: org.gradle.process.internal.ExecException: Process 'command '...Library/Java/JavaVirtualMachines/azul-16.0.2/Contents/Home/bin/java'' finished with non-zero exit value 1
    	at org.gradle.process.internal.DefaultExecHandle$ExecResultImpl.assertNormalExitValue(DefaultExecHandle.java:414)
    	at org.gradle.process.internal.DefaultJavaExecAction.execute(DefaultJavaExecAction.java:52)
    	at org.gradle.api.tasks.JavaExec.exec(JavaExec.java:156)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
    	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
    	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
    	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:506)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:74)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:74)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:491)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:474)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$300(ExecuteActionsTaskExecuter.java:106)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.executeWithPreviousOutputFiles(ExecuteActionsTaskExecuter.java:271)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:249)
    	at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:83)
    	at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:37)
    	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
    	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:47)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:47)
    	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:37)
    	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
    	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
    	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:50)
    	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
    	at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
    	at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
    	at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
    	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
    	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
    	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:54)
    	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:35)
    	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:60)
    	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:27)
    	at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:174)
    	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:74)
    	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:45)
    	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:40)
    	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:29)
    	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
    	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:99)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:92)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:52)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:36)
    	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:85)
    	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:42)
    	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
    	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
    	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:91)
    	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:49)
    	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:106)
    	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:51)
    	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:72)
    	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:46)
    	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:86)
    	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:86)
    	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:32)
    	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
    	at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:43)
    	at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:31)
    	at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution$2.withWorkspace(ExecuteActionsTaskExecuter.java:284)
    	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
    	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
    	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
    	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
    	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
    	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:33)
    	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:185)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:174)
    	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:109)
    	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:408)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:395)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:388)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:374)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
    	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    
    documentation 
    opened by magom001 7
  • ```describeContents(): Int``` is not implemented on Android

    ```describeContents(): Int``` is not implemented on Android

    I'm working on a multiplatform project (Desktop [JVM] and Android)

    Running it on desktop works without any issue, however on Android the build fails with this error:

    Object 'Login' is not abstract and does not implement abstract base class member public abstract fun describeContents(): Int defined in dev.datlag.burningseries.ui.navigation.NavHostComponent.ScreenConfig
    

    You can take a look at the source code at https://github.com/DATL4G/BurningSeries-Android/tree/compose it's in early development, so easy to understand

    documentation 
    opened by DATL4G 5
  • "Used by" list

    It would be good to have a list of companies/users/projects where Decompose is used. I suggest to stick to the following format, but feel free to omit/add/modify any details. ❤️

    Project name: <project name> Project type: production/pet/sample Company name: personal/<company name> Supported platforms: Android, JVM, Web, iOS, macOS, etc. Link (an open source repository or a web site): <link>

    opened by arkivanov 7
  • v1.0.0 roadmap

    v1.0.0 roadmap

    Required tasks:

    • [ ] Stabilize the Compose Animations API (see #110)
    • [x] Improve the "Check failed" error message (see #77)
    • [x] Comply with the new predictive back gesture in Android 13 (see #156)
    • [x] Move Router to stack sub-package and rename it to StackRouter (see #134)
    • [x] Remove all deprecated code
    • [x] Add extensions for Value (see #200)
    • [x] Change naming in samples from Some + SomeComponent to SomeComponent + DefaultSomeComponent

    Desired tasks:

    • [ ] Stabilize Web Routing API (see #111)
    • [ ] Stabilize extensions-android module
    • [ ] Support Compose for iOS and macOS (see #74)
    • [x] Allow safe Router access from childFactory function
    • [ ] Remove extensions-compose-jetpack module (see #11)
    • [x] Add child overlay navigation
    opened by arkivanov 4
  • Stabilise Web Routing API

    Stabilise Web Routing API

    Docs: link. Collection feedback and use cases here.

    Points to improve:

    • [ ] Consider moving WebHistoryController from commonMain to jsMain
    • [ ] Consider nested WebHistoryController
    opened by arkivanov 5
  • Stabilise the Compose Animations API

    Stabilise the Compose Animations API

    Docs: link. Collecting feedback and use cases here.

    Points to improve:

    • [x] Get rid of Direction.IDLE using movableContentOf (blocked by Compose 1.2.0 release)
    • [x] Make click interception configurable? Or replace with another solution? See b/214231672
    • [x] Nullable StackAnimator in StackAnimation selector
    • [x] Shared element transitions (perhaps LookaheadLayout + movableContentOf should work out of the box when they land in Compose)
    • [x] Add otherChild argument to stackAnimation selector
    • [x] Add minAlpha argument to fade animation
    opened by arkivanov 9
  • Compose native iOS and macOS support

    Compose native iOS and macOS support

    Latest builds of JetBrains Compose support native iOS and macOS targets. Let's try to add it to extensions-compose-jetbrains module. Since it's far from stable, a separate branch seems reasonable.

    Status update: experimental -native-compose versions of Decompose with Compose for Darwin are published from the compose-darwin branch.

    enhancement investigate 
    opened by arkivanov 2
Releases(1.0.0-beta-03)
  • 1.0.0-beta-03(Jan 4, 2023)

    • Added backStackCreateDepth argument to Child Stack (#293, #295)
    • Added persistent argument to Child Stack (#294)
    • Updated Essenty to 0.8.0 (#296)

    The new backStackCreateDepth argument

    The ComponentContext.children extension function got the new argument - backStackCreateDepth. It allows you to specify the amount of components on top of the back stack that are always created. This is useful e.g. for swipe-back navigation on iOS - when you drag the active screen, the previous screen is visible behind. Please see the updated docs for more information.

    The new persistent argument

    The ComponentContext.children extension function got the new argument - persistent. It allows you to disable the persistence of the navigation state - e.g. the initial stack will be used every time the screen is recreated. Please see the updated docs for more information.

    Essenty update to 0.8.0

    The new Essenty release 0.8.0 brings state preservation on JVM/desktop. It's now possible to save the navigation and app's state via StateKeeper on JVM/desktop. Please refer to the update sample app for more information, or check out the related PR #297.

    Versions and dependencies

    Kotlin: 1.7.20 Essenty: 0.8.0 parcelize-darwin: 0.1.2

    extensions-compose-jetpack

    Jetpack Compose: 1.2.1 Jetpack Compose Compiler: 1.3.2

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta-03-native-compose(Jan 4, 2023)

  • 1.0.0-beta-02(Dec 16, 2022)

    • Added minAlpha argument to fade animator (#270)
    • Added a convenience overload function ComponentContext#children with Parcelable navigation state (#279)
    • Applied parcelize-darwin plugin, updated Essenty to 0.7.0 (#286)

    The new parcelize-darwin plugin

    This release adds an experimental support of Parcelize on all Darwin (Apple) targets! Which means it's now possible to preserve the navigation state and any other Parcelable data not only on Android. Please read the updated docs.

    This change shouldn't affect your project's runtime. If you have any problems or questions, please file an issue or a discussion.

    Versions and dependencies

    Kotlin: 1.7.20 Essenty: 0.7.0 parcelize-darwin: 0.1.2

    extensions-compose-jetpack

    Jetpack Compose: 1.2.1 Jetpack Compose Compiler: 1.3.2

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta-02-native-compose(Dec 16, 2022)

  • 1.0.0-beta-01(Nov 19, 2022)

    • Added SimpleNavigation and exposed SimpleChildNavState for custom navigation implementations (#249)
    • Added main thread checks to MutableValue (#259)
    • Provide old and new child stacks in StackRouterView#children function (#260)
    • Added disableInputDuringAnimation argument to stackAnimation function (#264)
    • Converted StackAnimation and StackAnimator interfaces to fun interfaces (#265)
    • Moved StackNavigation and OverlayNavigation factory functions next to the corresponding interfaces (#267)

    The first Beta release

    This is the first Beta release! Which means that all API that is not explicitly marked with ExperimentalDecomposeApi is now considered stable.

    Main thread checks in MutableValue

    MutableValue now checks for the main thread when its value is changed. This is to prevent race conditions and other unexpected bugs. Please test your projects to make sure you access only from the main thread.

    The new disableInputDuringAnimation argument

    The Children function disables the input and touch events while the animation is in progress. This has been the default behaviour for some time. Now the new disableInputDuringAnimation argument provides the ability to disable this behaviour. The default value is true so there is no behaviour change.

    Breaking changes

    Main decompose module

    The main decompose module is source compatible with the previous release 1.0.0-alpha-07, but is not binary compatible. This is because StackNavigation and OverlayNavigation factory functions were moved to another file in the same package, next to corresponding interfaces.

    Compose extension modules

    Both extensions-compose-jetpack and extensions-compose-jetbrains modules are source and binary compatible with 1.0.0-alpha-07.

    extensions-android module

    The extensions-android is not source and binary compatible. This is because the StackRouterView#children function's signature has changed. Now both the new stack and the old stack are provided, so better animations are possible. See #260 for more information.

    Versions and dependencies

    Kotlin: 1.7.20 Essenty: 0.6.0

    extensions-compose-jetpack

    Jetpack Compose: 1.2.1 Jetpack Compose Compiler: 1.3.2

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta-01-native-compose(Nov 19, 2022)

  • 1.0.0-alpha-07(Oct 24, 2022)

    • Added children Generic Navigation model (#241, #242, #243, #245)
    • Added replaceAll extension function for StackNavigator (#229 by @Tommyten)
    • Updated Kotlin to 1.7.20, JetBrains Compose to 1.2.0, Jetpack Compose to 1.3.2 (#233, #236)

    The new Generic Navigation model

    This release introduces the new Generic Navigation model - children. It can be used to create almost any kind of custom navigation models. Both Child Stack and Child Overlay now use the Generic Navigation. See the docs for more information.

    Despite the fact that there are lots of tests, there is a possibility that something is broken. Please file an issue if you believe that something is wrong!

    Versions and dependencies

    Kotlin: 1.7.20 Essenty: 0.6.0

    extensions-compose-jetpack

    Jetpack Compose: 1.2.1 Jetpack Compose Compiler: 1.3.2

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha-07-native-compose(Oct 24, 2022)

    This is the same release as 1.0.0-alpha-07, but with Compose for iOS support.

    Also added support for the simulatorArm64 target (#246).

    Versions and dependencies

    Kotlin: 1.7.20 Essenty: 0.6.0 JetBrains Compose: 1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha-06(Oct 3, 2022)

  • 1.0.0-alpha-06-native-compose(Oct 3, 2022)

  • 1.0.0-alpha-05(Oct 2, 2022)

    • Added new navigation model - Child Overlay (#217, #218, #220)
    • Disallow destroying child ComponentContext (#215)
    • ~~Derive default values of stack and overlay keys from configuration class simpleName (#224)~~
    • Added extensions for Value<ChildStack> (#203)
    • Updated Kotlin to 1.7.10, MPP Compose to 1.2.0-beta02, Jetpack Compose to 1.2.1 (#211, #223)
    • Updated Essenty to 0.6.0 (#219)

    New navigation model - Child Overlay

    In addition to Child Stack, this release adds a new navigation model - Child Overlay. This kind of navigation is designed to activate and dismiss just one child component, e.g. a dialog. See the updated documentation.

    Disallow destroying child ComponentContext

    Decompose now prevents a child ComponentContext created using childContext extension function from being destroyed. This is required to prevent possible memory leaks. See the updated documentation about child ComponentContext. If you need to destroy child components manually, then consider using the new Child Overlay.

    Backward compatibility

    Decompose now uses Essenty v0.6.0 which is not binary compatible with the previously used version v0.5.2. Make sure there are no transitive dependencies using the old version of Essenty.

    Versions and dependencies

    Kotlin: 1.7.10 Essenty: 0.6.0

    extensions-compose-jetpack

    Jetpack Compose: 1.2.1 Jetpack Compose Compiler: 1.3.1

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0-beta02

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha-05-native-compose(Oct 2, 2022)

  • 1.0.0-alpha-04(Aug 22, 2022)

    • Fixed a bug when a parent Child Stack takes precedence in back button handling (#185)

    Versions and dependencies

    Kotlin: 1.7.0 Essenty: 0.5.2

    extensions-compose-jetpack

    Jetpack Compose: 1.2.0 Jetpack Compose Compiler: 1.2.0

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0-alpha01-dev753

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha-03(Aug 22, 2022)

    • Compare configurations by equality in Child Stack (#182)

    Bug fix

    This release fixes a bug that existed for a long time. Now Child Stack compares configurations by equality and not by references. This avoids unnecessary component re-creations in some conditions.

    Here is an example of affected use case:

    class Root(componentContext: ComponentContext): ComponentContext by componentContext {
        private val navigation = StackNavigation<Config>()
        private val stack = childStack(source = navigation, initialConfiguration = Config.Tab1("1")) { ... }
        
        fun foo() {
            navigation.bringToFront(Config.Tab2("2"))
    
            // At this point the stack is: [1, 2].
            // Before this release, the call below caused re-creation of Tab1 component.
            // This is because configurations were compared by reference, instead of equality.
            // Re-creation happens only if the configuration is a class, singleton objects are not affected.
            navigation.bringToFront(Config.Tab1("1"))
        }
        
        private sealed interface Config : Parcelable {
            @Parcelize
            data class Tab1(val payload: String) : Config
    
            @Parcelize
            data class Tab2(val payload: String) : Config
        } 
    }
    

    Versions and dependencies

    Kotlin: 1.7.0 Essenty: 0.5.2

    extensions-compose-jetpack

    Jetpack Compose: 1.2.0 Jetpack Compose Compiler: 1.2.0

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0-alpha01-dev753

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha-04-native-compose(Aug 22, 2022)

  • 1.0.0-alpha-03-native-compose(Aug 22, 2022)

  • 1.0.0-alpha-02(Aug 12, 2022)

    • Fixed incorrect initialValue in extensions-compose-jetpack DefaultStackAnimator (#176)

    Versions and dependencies

    Kotlin: 1.7.0 Essenty: 0.5.2

    extensions-compose-jetpack

    Jetpack Compose: 1.2.0 Jetpack Compose Compiler: 1.2.0

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0-alpha01-dev753

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha-01(Aug 5, 2022)

    • Version updated: Kotlin 1.7.0, Gradle 7.4.2, JB Compose 1.2.0-alpha01-dev753, Jetpack Compose 1.2.0 (#158)
    • Replaced BackPressedHandler with BackHandler (#164)
    • Added otherChild argument to stackAnimation selector function (#162)
    • Removed IDLE entry from Compose animation Direction enum (#161)
    • Nullable result in StackAnimation selector function (#163)
    • Nullable Children animation argument (#165)
    • Better error messages when creating child components with same keys (#87)
    • Removed previously deprecated Router (#159)
    • Removed previously deprecated ValueObserver type alias (#160)

    Breaking changes

    This release brings Decompose closer to v1.0.0 release. The main focus was to support latest Compose versions, further improve stack animation API, and support the new Predictive Back Gesture on Android 13. Please read about breaking changes in this release.

    This release is binary and source incompatible with v0.8.0.

    Predictive Back Gesture on Android 13

    Android 13 introduces the new Predictive Back Gesture. It is essential for Decompose to support it, so BackPressedHandler was replaced with the new BackHandler from Essenty library. The new BackHandler has different API (closer to AndroidX OnBackPressedDispatcher) compatible with Predictive Back Gesture. Please read corresponding section in Essenty readme.

    If you are not using BackPressedHandler or BackPressedDispatcher, then most likely you won't notice any changes and the source code should compile just fine. However, if you do use either of them, then you will need to update your code.

    Migration tips

    // Before
    
    class SomeComponent(componentContext: ComponentContext) : ComponentContext by componentContext {
        init {
            backPressedHandler.register {
                // Process back pressed event
                true | false // return true to consume the event, false to allow other handlers
            }
        }
    }
    
    // After
    
    class SomeComponent(componentContext: ComponentContext) : ComponentContext by componentContext {
        private val backCallback = BackCallback(isEnabled = false) { /* Process back pressed event */ }
    
        init {
            backHandler.register(backCallback)
        }
    
        private fun updateBackCallback() {
            backCallback.isEnabled = true | false // return true to consume all back button events, false to allow other handlers
        }
    }
    
    // Before
    val backPressedDispatcher = BackPressedDispatcher()
    val componentContext = DefaultComponentContext(..., backPressedHandler = backPressedDispatcher)
    // ...
    
    val backHandled = backPressedDispatcher.onBackPressed()
    
    // After
    val backDispatcher = BackDispatcher()
    val componentContext = DefaultComponentContext(..., backHandler = backDispatcher)
    // ...
    
    val backHandled = backPressedDispatcher.back()
    

    Changes in stack animation API

    The stackAnimation {} function's selector got an additional argument - otherChild. Now it is possible to choose StackAnimator for a child based on both - the child itself, and the second child (the other one) in the scene. For example - having different animations for a child X when it is being opened from child A or B. It is also now possible to return null from the selector function, if no animation is required for a child

    The Direction.IDLE enum entry is removed, animators are not called when there is no ongoing animation, thanks to the new movableContentOf Compose API.

    Removed Router and ValueObserver

    The Router API was deprecated in v0.8.0 and is now completely removed. Please read v0.8.0 release notes for migration guides if you didn't migrate yet.

    The previously deprecated ValueObserver type alias is also removed. Please use normal function types instead - (T) -> Unit.

    Versions and dependencies

    Kotlin: 1.7.0 Essenty: 0.5.2

    extensions-compose-jetpack

    Jetpack Compose: 1.2.0 Jetpack Compose Compiler: 1.2.0

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0-alpha01-dev753

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha-01-native-compose(Aug 5, 2022)

  • 0.8.0(Jul 16, 2022)

    • Deprecated Router and introduced Child Stack (#135, #136, #137, #140, #142, #143)
    • Deprecated ValueObserver typealias (#139)

    The new Child Stack

    The Router and all its surroundings are now deprecated. There is new concept introduced - Child Stack. It is located in a separate package and doesn't conflict with Router. The main purpose of introducing Child Stack is to bring more flexibility and compile time safety. And also to allow the room for another kinds of routing in the future.

    Please refer to the updated docs and #134 for more information.

    Migration tips

    // Before
    
    interface Root {
        val routerState: Value<RouterState<*, Child>>
    }
    
    class RootComponent(componentContext: ComponentContext): ComponentContext by componentContext {
        private val router = router<Config, Child>(..., childFactory = ::child)
        override val routerState = router.state
    
        private fun foo() {
            router.push()
            router.pop()
        }
    }
    
    @Composable
    fun RootContent(component: Root) {
        val routerState by component.routerState.observeAsState()
        Children(routerState = routerState, ...) {
            // ...
        }
    }
    
    // After
    
    interface Root {
        val childStack: Value<ChildStack<*, Child>>
    }
    
    class RootComponent(componentContext: ComponentContext): ComponentContext by componentContext {
        private val navigation = StackNavigation<Config>()
        private val stack = childStack(source = navigation, ..., childFactory = ::child)
        override val childStack = stack
    
        private fun foo() {
            navigation.push()
            navigation.pop()
        }
    }
    
    @Composable
    fun RootContent(component: Root) {
        val stack by component.childStack.observeAsState()
        Children(stack = stack, ...) {
            // ...
        }
    }
    

    Breaking changes

    There are no breaking changes in this release, it should be both binary and source compatible.

    Versions and dependencies

    Kotlin: 1.6.10 Essenty: 0.2.2

    extensions-compose-jetpack

    Jetpack Compose: 1.1.1 Jetpack Compose Compiler: 1.1.1

    extensions-compose-jetbrains

    JetBrains Compose: 1.1.0

    Source code(tar.gz)
    Source code(zip)
  • 0.8.0-native-compose-02(Jul 16, 2022)

    This is the same release as 0.8.0, but with Compose for iOS support:

    Versions and dependencies

    Kotlin: 1.7.0 Essenty: 0.4.2 JetBrains Compose: 1.2.0-alpha01-dev745

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0-native-compose-02(Jul 14, 2022)

    • Updated Kotlin to 1.7.0, Compose to 1.2.0-alpha01-dev741, Essenty to 0.4.2, AGP to 7.2.0, Gradle to 7.4.2 (#141)

    Versions and dependencies

    Kotlin: 1.7.0 Essenty: 0.4.2 JetBrains Compose: 1.2.0-alpha01-dev745

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0-native-compose-01(Jun 26, 2022)

    This is the same release as 0.7.0, but with the following changes:

    • extensions-compose-jetbrains supports additional targets: iosX64, iosArm64, macosX64 and macosArm64 (iosSimulatorArm64 is not yet supported, because Compose doesn't support it)
    • extensions-compose-jetpack module is removed

    Versions and dependencies

    Kotlin: 1.6.21 Essenty: 0.2.2 JetBrains Compose: 1.2.0-alpha01-dev716

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Jun 26, 2022)

    • Added onComplete callback to Router.popWhile(...) function (#104 by @Tommyten)
    • Added onComplete callback to Router.push(...), Router. replaceCurrent(...) and Router. bringToFront(...) functions (#128)
    • Added ensureNeverFrozen to MutableValue (#116)
    • Interfaces DefaultWebHistoryController.Window and DefaultWebHistoryController.History are marked internal (#127)
    • Fixed DefaultWebHistoryController not working on Chrome for iOS (#127)

    Versions and dependencies

    Kotlin: 1.6.10 Essenty: 0.2.2

    extensions-compose-jetpack

    Jetpack Compose: 1.1.1 Jetpack Compose Compiler: 1.1.1

    extensions-compose-jetbrains

    JetBrains Compose: 1.1.0

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0-native-compose-01(May 2, 2022)

    • Updated Compose to 1.2.0-alpha01-dev675 (#86)
    • Updated Kotlin to 1.6.21 (#89)

    Versions and dependencies

    Kotlin: 1.6.21 Essenty: 0.2.2

    extensions-compose-jetbrains

    JetBrains Compose: 1.2.0-alpha01-dev675

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0-native-compose(Apr 24, 2022)

    This is the same release as 0.6.0, but with the following changes:

    • extensions-compose-jetbrains supports additional targets: iosX64, iosArm64, macosX64 and macosArm64 (iosSimulatorArm64 is not yet supported, because Compose doesn't support it)
    • extensions-compose-jetpack module is removed

    The sample was updated with Compose for iOS - sample/app-darwin-compose.

    Versions and dependencies

    Kotlin: 1.6.20 Essenty: 0.2.2

    extensions-compose-jetbrains

    JetBrains Compose: 0.0.0-on-rebase-12-apr-2022-dev668

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Apr 8, 2022)

    Changes

    • Updated Compose animations API (different animations for different children, combining animators) (#53, #67)
    • Added onComplete callback to Router.navigate and Router.pop functions (#66)
    • Disabled interactions while animating Compose children (#58)
    • Tidied deprecated code (#64)

    Versions and dependencies

    Kotlin: 1.6.10 Essenty: 0.2.2

    extensions-compose-jetpack

    Jetpack Compose: 1.1.1 Jetpack Compose Compiler: 1.1.1

    extensions-compose-jetbrains

    JetBrains Compose: 1.1.0

    Breaking changes

    The Compose animations API is significantly changed. The main idea is to allow different animations for different children. Also it is now possible to combine animators using the + operator. This is both source and binary incompatible change. Please refer to the updated documentation.

    The Router.navigate function got a second argument - the onComplete callback. The old Router.navigate function with one argument is now an extension function, for convenience. The Router.pop extension function got the onComplete callback as well. It doesn't throw an exception anymore in case when the back stack is empty. Instead, the onComplete callback is called with a result. This is both source and binary incompatible change. Please refer to the updated documentation.

    All deprecated code with Level.ERROR is removed. All deprecations with Level.WARNING are promoted to Level.ERROR.

    Source code(tar.gz)
    Source code(zip)
  • 0.5.2(Mar 11, 2022)

  • 0.5.1(Feb 9, 2022)

  • 0.5.0(Jan 16, 2022)

    • Added Router builder functions with initialStack argument, deprecated old builders with initialConfiguration and initialBackStack arguments (#14)
    • Added WebHistoryController for controlling the browser history (#15, #16)
    • Updated Kotlin to 1.6.10, Jetpack Compose to 1.0.5, Jetpack Compose compiler to 1.1.0-rc02, and JetBrains Compose to 1.0.1
    • Simplified Compose animation API (https://github.com/badoo/Decompose/pull/261)
    • Added Modifier argument to the Children function (https://github.com/badoo/Decompose/pull/262)
    • Fixed a possible memory leak in Compose PageAnimation (https://github.com/badoo/Decompose/pull/259)

    New API

    This release introduces a new experimental API for controlling the web browser history - WebHistoryController. Please refer to the updated documentation for more information.

    Breaking changes

    The animation API for Compose is updated and made simpler. This change doesn't affect if you are using the provided animations like slide() or crossfadeScale. But if you have custom animations (e.g. using the childAnimation function), it is recommended to familiarise yourself with the changes. Please refer to the updated documentation and the corresponding pull request (https://github.com/badoo/Decompose/pull/261).

    The Children function got a new optional argument - Modifier. It can be used to better control positioning and other parameters of child Composables.

    Source code(tar.gz)
    Source code(zip)
Owner
Arkadii Ivanov
Android developer. Passionate about Kotlin Multiplatform, MVI and reactive programming.
Arkadii Ivanov
Kotlin Multiplatform is an SDK for cross-platform mobile development, which enables teams to use the same business logic in both Android and iOS client applications.

Kotlin Multiplatform is an SDK for cross-platform mobile development, which enables teams to use the same business logic in both Android and iOS client applications.

Chris Russell 1 Feb 11, 2022
Kotlin Multiplatform Mobile + Mobile Declarative UI Framework (Jetpack Compose and SwiftUI)

Kotlin Multiplatform Mobile + Mobile Declarative UI Framework (Jetpack Compose and SwiftUI)

Kotchaphan Muangsan 3 Nov 15, 2022
Funstuff - Minimal Kotlin Multiplatform project with SwiftUI, Jetpack Compose, Compose for Wear OS, Compose for Desktop

PeopleInSpace Minimal Kotlin Multiplatform project with SwiftUI, Jetpack Compose

Shivam Dhuria 2 Feb 15, 2022
Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in Kotlin with Jetpack Compose and a backed in Kotlin hosted on AppEngine.

Conferences4Hall Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in K

Gérard Paligot 98 Dec 15, 2022
GraphQL based Jetpack Compose and SwiftUI Kotlin Multiplatform sample

GraphQL based Jetpack Compose and SwiftUI Kotlin Multiplatform sample

John O'Reilly 151 Jan 3, 2023
MangaKu App Powered by Kotlin Multiplatform Mobile, Jetpack Compose, and SwiftUI

MangaKu ?? Introduction MangaKu App Powered by Kotlin Multiplatform Mobile, Jetpack Compose, and SwiftUI Module core: data and domain layer iosApp: io

Uwais Alqadri 132 Jan 8, 2023
🍭 GithubSearchKMM - Github Repos Search - Android - iOS - Kotlin Multiplatform Mobile using Jetpack Compose, SwiftUI, FlowRedux, Coroutines Flow, Dagger Hilt, Koin Dependency Injection, shared KMP ViewModel, Clean Architecture

GithubSearchKMM Github Repos Search - Kotlin Multiplatform Mobile using Jetpack Compose, SwiftUI, FlowRedux, Coroutines Flow, Dagger Hilt, Koin Depend

Petrus Nguyễn Thái Học 50 Jan 7, 2023
KaMP Kit is to facilitate your evaluation of Kotlin Multiplatform (aka KMP)

KaMP Kit Welcome to the KaMP Kit! About Goal The goal of the KaMP Kit is to facilitate your evaluation of Kotlin Multiplatform (aka KMP). It is a coll

bas 0 Oct 25, 2021
Netflix inspired OTT Home Screen, Contains implementation in Reactjs, Kotlin React Wrapper, Jetpack Compose Web

Netflix-Clone-React Practising React by building Netflix Clone Requirements TMDB api key : Add TMDB API key to AppApi.kt Learning Resourcce Build Netf

Chetan Gupta 61 Nov 13, 2022
A minimal notes application in Jetpack Compose with MVVM architecture. Built with components like DataStore, Coroutines, ViewModel, LiveData, Room, Navigation-Compose, Coil, koin etc.

Paper - A Minimal Notes App A minimal notes application in Jetpack Compose with MVVM architecture. Built with components like DataStore, Coroutines, V

Akshay Sharma 139 Jan 2, 2023
A pluggable sealed API result type for modeling Retrofit responses.

A pluggable sealed API result type for modeling Retrofit responses.

Slack 645 Dec 19, 2022
Android app with minimal UI to run snowflake pluggable transports proxy, based on library IPtProxy

Simple Kotlin app for testing IPtProxy's snowflake proxy on Android Essentially a button for starting and stopping a Snowflake Proxy with the default

null 2 Jun 26, 2022
A fork of our clean architecture boilerplate, this time using the Android Architecture Components

Android Clean Architecture Components Boilerplate Note: This is a fork of our original Clean Architecture Boilerplate, except in this repo we have swi

Buffer 1.3k Jan 3, 2023
Minimal UI library for Android inspired by React

Anvil - reactive views for Android Anvil is a small Java library for creating reactive user interfaces. Originally inspired by React, it suits well as

null 1.4k Dec 23, 2022
Easiest routing for compose-jb

Easiest routing for compose-jb Supported targets: Jvm Js Installation repositories { mavenCentral() } dependencies { implementation("io.githu

null 31 Nov 18, 2022
D-KMP Architecture official sample: it uses a shared KMP ViewModel and Navigation for Compose and SwiftUI apps.

D-KMP architecture - official sample This is the official sample of the D-KMP architecture, presenting a simple master/detail app, for Android, iOS an

null 594 Jan 3, 2023
A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

Nek.12 4 Oct 31, 2022
Built with Jetpack compose, multi modules MVVM clean architecture, coroutines + flow, dependency injection, jetpack navigation and other jetpack components

RickAndMortyCompose - Work in progress A simple app using Jetpack compose, clean architecture, multi modules, coroutines + flows, dependency injection

Daniel Waiguru 9 Jul 13, 2022
A lightweight, simple, smart and powerful Android routing library.

RxRouter Read this in other languages: 中文, English A lightweight, simple, smart and powerful Android routing library. Getting started Setting up the d

Season 323 Nov 10, 2022