```@Composable internal fun RootContent(component:...
# decompose
s
Copy code
@Composable
internal fun RootContent(component: RootComponent) {
    Children(
        stack = component.childStack,
        animation = stackAnimation(fade()),
    ) {
        println("child stack =====  ${component.childStack.value}")
        when (val child = it.instance) {
            is RootComponent.Child.EnterMobileNumber -> LoginComponent(child.component)
            is RootComponent.Child.EnterOtp -> OtpComponent(child.component)
            is RootComponent.Child.ProjectListing -> ProjectListingMainScreen(child.component)
            is RootComponent.Child.ProjectDetails -> ProjectDetailsScreen(child.component)
            is RootComponent.Child.Notifications -> NotificationScreen(child.component)
            is RootComponent.Child.ImagePreviewFullScreen -> ImagePreviewFullScreen(child.component)
        }
    }
}
THis is my root content . Now inside ProjectDetails Component i have a BootomNaviagtion containing 5 options "A,B,C,D,E" all of which are different and clicking on "A" loads Component A , Component A can have its own childers which can be one or many , now please help me how should i define ProjectDetails Componet and what should be the structure of this component i.e when inside ProjectDetails, i want this component to act as root component for its 5 childen (A,B,C,D,E), am i thinking in right approach or how should i proceed from here!! @Arkadii Ivanov Please help!!
a
If I understand it correctly, in ProjectDetails component you should define a configuration class for each child component. Then you can use Child Stack and activate each child component using
navigation.bringToFront(...)
.
s
Yes @Arkadii Ivanov this is what i was asking, thanks! I will try to implement if stuck in implementation will ask for your help again.
@Arkadii Ivanov need example code for help.
Copy code
class RootComponent internal constructor(
    componentContext: ComponentContext,
    private val enterMobileNumber: (ComponentContext, (EnterMobileNumberComponent.Output) -> Unit) -> EnterMobileNumberComponent,
    private val enterOtp: (ComponentContext, mobileNumber: String, verificationId: String, (EnterOtpComponent.Output) -> Unit) -> EnterOtpComponent,
    private val projectListing: (ComponentContext, (ProjectListingComponent.Output) -> Unit) -> ProjectListingComponent,
    private val projectDetails: (ComponentContext, projectId: String, (ProjectDetailsComponent.Output) -> Unit) -> ProjectDetailsComponent,
    private val imagePreviewFullScreen: (ComponentContext, imagesList: List<PreviewFile>, initialImageIndex: Int, (ImagePreviewFullScreenComponent.Output) -> Unit) -> ImagePreviewFullScreenComponent,
    private val notifications: (ComponentContext, (NotificationsComponent.Output) -> Unit) -> NotificationsComponent,
) : ComponentContext by componentContext {


    constructor(
        componentContext: ComponentContext,
        storeFactory: StoreFactory,
    ) : this(componentContext = componentContext, enterMobileNumber = { childContext, output ->
        EnterMobileNumberComponent(
            componentContext = childContext, storeFactory = storeFactory, output = output
        )
    }, enterOtp = { childContext, mobileNum, verificationId, output ->
        EnterOtpComponent(
            componentContext = childContext,
            mobileNumber = mobileNum,
            verificationId = verificationId,
            storeFactory = storeFactory,
            output = output
        )
    }, projectListing = { childContext, output ->
        ProjectListingComponent(
            componentContext = childContext, storeFactory = storeFactory, output = output
        )
    }, projectDetails = { childContext, projectId, output ->
        ProjectDetailsComponent(
            componentContext = childContext,
            projectId = projectId,
            storeFactory = storeFactory,
            output = output
        )
    }, notifications = { childContext, output ->
        NotificationsComponent(
            componentContext = childContext, storeFactory = storeFactory, output = output
        )
    }, imagePreviewFullScreen = { childContext, imagesList, initialImageIndex, output ->
        ImagePreviewFullScreenComponent(
            componentContext = childContext,
            imagesList = imagesList,
            initialImageIndex = initialImageIndex,
            output = output
        )
    })

    private val navigation = StackNavigation<Configuration>()

    private val stack = childStack(
        source = navigation,
        initialConfiguration = getInitConfig(),
        handleBackButton = false,
        childFactory = ::createChild
    )

    private fun getInitConfig() = if (pref.getString(RdashConstants.AUTH_TOKEN).isNullOrEmpty()
            .not()
    ) Configuration.ProjectListing
    else Configuration.EnterMobileNumber


    val childStack: Value<ChildStack<*, Child>> = stack

    private fun createChild(
        configuration: Configuration, componentContext: ComponentContext
    ): Child = when (configuration) {
        is Configuration.EnterMobileNumber -> Child.EnterMobileNumber(
            enterMobileNumber(
                componentContext, ::onEnterMobileNumberOutput
            )
        )

        is Configuration.EnterOtp -> Child.EnterOtp(
            enterOtp(
                componentContext,
                configuration.mobile,
                configuration.verificationId,
                ::onEnterOtpOutput
            )
        )

        is Configuration.ProjectListing -> Child.ProjectListing(
            projectListing(componentContext, ::onProjectListingOutput)
        )

        is Configuration.ProjectDetails -> Child.ProjectDetails(
            projectDetails(
                componentContext, configuration.projectId, ::onProjectDetailsOutput
            )
        )

        is Configuration.ImagePreviewFullScreen -> Child.ImagePreviewFullScreen(
            imagePreviewFullScreen(
                componentContext,
                configuration.imagesList,
                configuration.initialImageIndex,
                ::onImagePreviewOutput
            )
        )

        is Configuration.Notification -> Child.Notifications(
            notifications(
                componentContext, ::onNotificationsOutput
            )
        )
    }


    private fun onEnterMobileNumberOutput(output: EnterMobileNumberComponent.Output): Unit =
        when (output) {
            is EnterMobileNumberComponent.Output.NavigateBack -> navigation.pop()
            is EnterMobileNumberComponent.Output.NavigateToEnterOTP -> navigation.push(
                Configuration.EnterOtp(
                    output.mobileNum, output.verificationId
                )
            )
        }

    private fun onEnterOtpOutput(output: EnterOtpComponent.Output): Unit = when (output) {
        is EnterOtpComponent.Output.NavigateBack -> navigation.pop()
        is EnterOtpComponent.Output.NavigateToProjectList -> {
            println("NavigateToProjectList")
            navigation.replaceAll(Configuration.ProjectListing)
        }
    }

    private fun onProjectListingOutput(output: ProjectListingComponent.Output): Unit =
        when (output) {
            is ProjectListingComponent.Output.NavigateBack -> navigation.pop()
            is ProjectListingComponent.Output.NavigateToProjectDetails -> {
                navigation.push(Configuration.ProjectDetails(projectId = output.projectId))
            }

            is ProjectListingComponent.Output.Logout -> {
                navigation.replaceAll(Configuration.EnterMobileNumber)
            }

            is ProjectListingComponent.Output.NavigateToNotification -> {
                navigation.push(Configuration.Notification)
            }
        }

    private fun onProjectDetailsOutput(output: ProjectDetailsComponent.Output): Unit =
        when (output) {
            is ProjectDetailsComponent.Output.NavigateBack -> navigation.pop()
            is ProjectDetailsComponent.Output.OpenFullScreenComponent -> navigation.push(
                Configuration.ImagePreviewFullScreen(
                    imagesList = output.imagesList, initialImageIndex = output.initialImageIndex
                )
            )

            else -> {}
        }

    private fun onNotificationsOutput(output: NotificationsComponent.Output): Unit = when (output) {
        is NotificationsComponent.Output.NavigateBack -> navigation.pop()
    }

    private fun onImagePreviewOutput(output: ImagePreviewFullScreenComponent.Output): Unit =
        when (output) {
            is ImagePreviewFullScreenComponent.Output.NavigateBack -> navigation.pop()/*is ImagePreviewFullScreenComponent.Output.NavigateToImagePreviewFullScreen -> navigation.push(
                Configuration.ImagePreviewFullScreen(
                    imagesList = output.imagesList,
                    initialImageIndex = output.initialImageIndex
                )
            )*/
            else -> {}
        }


    private sealed class Configuration : Parcelable {
        @Parcelize
        object EnterMobileNumber : Configuration()

        @Parcelize
        data class EnterOtp(var mobile: String, val verificationId: String) : Configuration()

        @Parcelize
        object ProjectListing : Configuration()

        @Parcelize
        data class ProjectDetails(val projectId: String) : Configuration()

        @Parcelize
        data class ImagePreviewFullScreen(
            val imagesList: List<PreviewFile>, val initialImageIndex: Int = 0
        ) : Configuration()

        @Parcelize
        object Notification : Configuration()
    }

    sealed class Child {
        data class EnterMobileNumber(val component: EnterMobileNumberComponent) : Child()
        data class EnterOtp(val component: EnterOtpComponent) : Child()
        data class ProjectListing(val component: ProjectListingComponent) : Child()
        data class ProjectDetails(val component: ProjectDetailsComponent) : Child()
        data class ImagePreviewFullScreen(val component: ImagePreviewFullScreenComponent) : Child()
        data class Notifications(val component: NotificationsComponent) : Child()


    }

}
This is root component
Copy code
@Composable
internal fun RootContent(component: RootComponent) {
    Children(
        stack = component.childStack,
        animation = stackAnimation(fade()),
    ) {
        println("child stack =====  ${component.childStack.value}")
        when (val child = it.instance) {
            is RootComponent.Child.EnterMobileNumber -> LoginComponent(child.component)
            is RootComponent.Child.EnterOtp -> OtpComponent(child.component)
            is RootComponent.Child.ProjectListing -> ProjectListingMainScreen(child.component)
            is RootComponent.Child.ProjectDetails -> ProjectDetailsScreen(child.component)
            is RootComponent.Child.Notifications -> NotificationScreen(child.component)
            is RootComponent.Child.ImagePreviewFullScreen -> ImagePreviewFullScreen(child.component)
        }
    }
}
This is RootContent
Now when "ProjectDetailsComponent" is opened i want that this component hosts 5 more child components A,B,C,D,E. Can you help mw with example code on how to achieve this?
Copy code
class ProjectDetailsComponent(
    componentContext: ComponentContext,
    projectId: String,
    storeFactory: StoreFactory,
    private val output: (Output) -> Unit
) : ComponentContext by componentContext {

    private val projectStore = instanceKeeper.getStore {
        ProjectStoreFactory(
            storeFactory = storeFactory
        ).create()
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    val state: StateFlow<ProjectStore.State> = projectStore.stateFlow

    val projectId: String = projectId

    fun onEvent(event: ProjectStore.Intent) {
        projectStore.accept(event)
    }

    fun onOutput(output: Output) {
        output(output)
    }

    sealed class Output {
        object NavigateBack : Output()
        data class OpenFullScreenComponent(
            val imagesList: List<PreviewFile>,
            val initialImageIndex: Int,
        ) : Output()

        object Logout : Output()

    }

}
This is my ProjectDetailsComponent which i want to change
a
Sure, here is the code from the top of my head (I didn't try to compile). I only implemented the stack, you should also add your existing implementation there. Your
ProjectDetailsContent
should also use
Children
function, and show a bottom bar with the currently selected tab depending on the currently active child.
Copy code
class ProjectDetailsComponent(
    componentContext: ComponentContext,
    // ...
) : ComponentContext by componentContext {
    
    private val navigation = StackNavigation<Config>()
    
    private val _stack: Value<ChildStack<Config, Child>> = 
        childStack(
            source = navigation,
            childFactory = ::createChild,
        )
        
    val stack: Value<ChildStack<*, Child>> = _stack

    private fun createChild(config: Config, componentContext: ComponentContext): Child = 
        when (config) {
            is Config.A -> Child.A(AComponent(componentContext))
            is Config.B -> Child.B(BComponent(componentContext))
            // ...
        }

    private sealed class Config : Parcelable {
        @Parcelize
        object A : Config()

        @Parcelize
        object B : Config()
        
        // ...
    }
    
    sealed class Child {
        class A(val component: AComponent) : Child()
        class B(val component: BComponent) : Child()
        // ...
    }
}
s
Thanks , completed my code with similar implementation. Now how do i add bottom navigation such that it visible at bottom of all child components “A”,”B”. I want bottom sheet ui to be visible in ProjectDetailsComponent at bottom. @Arkadii Ivanov
a
Your
ProjectDetailsContent
should be similar to the RootContent in the example. It should display
BottomNavigation
below
Children
.
s
Thanks @Arkadii Ivanov !! 🙂
Hi @Arkadii Ivanov i am facing one challenge now. With reference to above discussion above BottomNavigation Example. My Children contains a component which contains a bottom sheet. Attaching some screenshots of issue :- 1. Bottom sheet hides behind BottomNav. 2. Bottom sheet doesn't hide completely. 3. Also as ModalBottomSheet is part of Children , so it start above BottomNav in Children Container. It Showld Start from Bottom. What i want :- BottomSheet should be shown above BottomNav not behind BottomNav. What all i solution i think of :- 1. Hide BottomNav when showing MOdalBottomSheet.
a
BottomSheet should be shown above BottomNav not behind BottomNav.
Could you elaborate?
s
@Arkadii Ivanov Consider a stack having two items, BottomNav & BottomSheet. BottomSheet should be on top on BottomNav . As you can see in the screenshot its hidden behing BottomNav
a
Looking at the screenshots, it's pretty difficult to understand where the bottom sheet actually is. But based on what you are saying, this looks like a general Compose question, because Decompose does no magic here. It's just a normal hierarchy of Composables. Maybe ask in #compose?