raamcosta / compose-destinations-docs

Website for Compose Destinations library.
Apache License 2.0
3 stars 17 forks source link

Manually defined navigation graphs migration to v2 #23

Closed miduch closed 3 months ago

miduch commented 3 months ago

Hello,

I'm not able to fine migration steps for manually defined navigation graph to v2.

https://composedestinations.rafaelcosta.xyz/defining-navgraphs?_highlight=navgraphspec https://composedestinations.rafaelcosta.xyz/defining-navgraphs?_highlight=navgraphspec#manually-defining-navigation-graphs

interface NavGraphSpec: Direction, Route {
    override val route: String
    val startRoute: Route
    val destinationsByRoute: Map<String, DestinationSpec<*>>
    val nestedNavGraphs: List<NavGraphSpec> get() = emptyList()
}

Is this documented somewhere?

raamcosta commented 3 months ago

Hi @miduch 👋

on v2 there’s no need to do that:

https://composedestinations.rafaelcosta.xyz/migrating-to-v2#15-if-implementing-navgraphspec-in-a-multi-module-setup-to-aggregate-graphs-and-destinations-from-other-modules

Let me know if this raises any other question

miduch commented 3 months ago

What is recommended way IF we can only build our graph after some data is available e.g from backend? What we have is defined destinations only but actual navigation graph is built later

raamcosta commented 3 months ago

I would be interested to know what’s the use case to make you want to do that 🤔

in any case, you can use vanilla NavHost:

https://composedestinations.rafaelcosta.xyz/v2/navhosts#vanilla-navhosts

miduch commented 3 months ago

Don't know if type safe compose navigation has changed something or not but previously following was only way it worked for us.

We receive some data from backend, based on this data we build bottom navigation items. Each bottom navigation item when clicked opens Same screen destination with different navArg. Orig issue with this was that as we're using same screen destination (same route) just different navArgs -> when navigating from to other screen it would replace previous entry on backstack. Eventually we ended up something like following

fun buildChildNavGraph(someIds: List<String>) {
    return object : NavGraphSpec {
        private val destinationsList = buildList<DestinationSpec<*>> {
            someIds.forEach {
                add(RoutePrefixWrapper("$it", ChildScreenDestination))
            }
        }

        override val route = Consts.CHILD_ROUTE
        override val destinationsByRoute = destinationsList.associateBy { it.route }
        override val startRoute = destinationsList.first()
    }    
}

this childGraphSpec is added to rootGraphSpec using destinationsByRoute. This bottom navigation example is just one of few cases. We have few others usecases as well where same Screen destination need to be re-used.

RoutePrefixWrapper looks like this

class RoutePrefixWrapper<T>(
    val prefix: String,
    val wrappedDestination: DestinationSpec<T>,
) : DestinationSpec<T> by wrappedDestination {
    private val safePrefix = prefix.replace('/', '_')
    override val baseRoute: String = safePrefix + wrappedDestination.baseRoute
    override val route: String = safePrefix + wrappedDestination.route
    override fun invoke(navArgs: T): Direction {
        val wrappedUrl = wrappedDestination.invoke(navArgs).route
        return Direction(route = safePrefix + wrappedUrl)
    }
}

Data coming from backend can change while app is running, and we do sometime rebuild whole graph with new data set.

Please share your thoughts on this and if you have any better ideas. Hopefully we can have something similar or better in compose destination v2

Thanks!