I try to migrate from `compose-router` to jetpacks...
# compose
l
I try to migrate from
compose-router
to jetpacks
navigation-compose
and I'm wondering how to handle constructs like those properly:
Copy code
// (container) screen with a BottomNavigationBar
fun ScreenContainer(
    ...
    childs: @Composable (PaddingValues, SomeState, SomeHighOrderFunction) -> Unit
) 

ScreenContainer(
    ...
) { innerPadding, SomeState, onError -> // How to easily pass things like innerPading or highorder functions to child screens?
    NestedRouter(startPoint = Screen.A) { backStack ->
        when(backStack.last())
           is ScreenA -> ScreenA()
           is ScreenB -> ScreenB()
    }
}

vs.

NavHost(navController = navController, startDestination = "container") {
    composable("container") { ScreenContainer(
        ...
    ) // childs are now called via navController
    composable("screena") { ScreenA(innerPadding = ?, state = ?, onError = ?) }  // How to get these parameters?
    composable("screenb") { ScreenB(innerPadding = ?, state = ?, onError = ?) }  
}
Thanks in advance!
🧵 1
j
Well think about do you actually need those there? What does inner padding have to do with navigation? What does the error have to do with navigation?
the navigation component is mainly used to add simple keys/values to be able to construct a screen (correct me if I am wrong 🙂 )
this allows for deeplinking support etc.
l
From this point of view, you are right. Fortunately my tiny company app won't need deep linking and I would expect that child screens of a (parent/container) screen with a BottomNavagationBar are tighty coupled to its parent. Indeed I need those parameters. For example, innerPadding is needed to add the padding of the Bar to the LazyColumns of the child screens and the onError function is needed because I want to handle errors in one place (in parent) not in every single child (duplicate code for every child). In my specific case I need child screens to be tightly coupled to its parent. Btw..any other compose navigation library is doing it this way to support such cases.
i
FWIW, the latest Navigation Compose release offered support for passing Modifiers directly to NavHost specifically for things like `innerPadding`: https://developer.android.com/jetpack/androidx/releases/navigation#compose-1.0.0-alpha10
l
Thanks for the info. What about other parameters like state, functions. Any chance for that? @Ian Lake I have 2 options. Stick with non-jetpack navigation library or rethink my use case. But to be honest I'm struggling so hard with the right implementation of it. While the parent screen (with BottomNavigationBar) establishs a bluetooth connection and also handles it errors, the child screens "fetches" data packets from the bluetooth device which is needed to load the child screen content. Every operation represents a flow in a
presenter/view model
so even child screens can receive errors and to not handle errors for every screen I want to hoist the "onError" events up to the parent so error handling is done in one place. The other reason for the tight coupling is that I pass down the status of the connection to the child screens, so that when device is connected the child screen starts fetching its screen content from the device. What do you think, how would I implement this properly?
j
@Lilly for my app I make I also will have issues with the navigation component to be fair, I pass a Props (just like React) object and in there I pass usually an id for the object the screen has to display, and a handler to handle results from a screen by a parent. This makes any screen independent from its parent, but of course it wouldn't work with the navigation component. Therefore I will most likely not use the navigation component, or I will have to find another way of doing things 🙂
l
Yes I understand. I already noticed that navigation compose might (at least for this specific use case) not be the right library. Anyway I'm really struggling with the implementation of my use case despite of what library I use. I also think it's a good practise to loose the coupling between childs and parent but how? 😕 I need error handling of multiple flows (whereas parent and child screens have its own flow) in one place --> this forces me to keep them coupled?! and otherwise I would do error handling in every single screen which can't be the right approach?! -> for every new child screen I'm copying the error handling code
i
I don't know what your problem is. It is totally fine for a
composable
destination to reference state created and updated outside of the
NavHost,
same with bubbling events up beyond the
NavHost
j
I just checked my code and you are absolutely right, I am doing that right now with my own transition component 😕 The transition router takes anything that usually just contains the key of a page, but after that it passes on the handlers as well from outside the component. Very well, will keep it until transitions are a thing 🙂
l
@Ian Lake So you mean this is ok?:
Copy code
lateinit var state: SomeState
val onError: (String) -> Unit = { ... }

NavHost(navController = navController, startDestination = "container") {
    composable("container") { ScreenContainer(onError = onError)
    composable("screena") { ScreenA(innerPadding = ?, state = state, onError = onError) }  // How to get these parameters?
    composable("screenb") { ScreenB(innerPadding = ?, state = state, onError = onError) }  
}
@Joost Klitsie Transitions aren't supported yet right?
j
not yet
i
@Lilly - yep, that's just classic state hoisting and bubbling events up. Navigation doesn't change that.
l
ah ok sorry. I'm just wondering because this approach makes the infos available for every screen, also those which shouldn't have access to it. This isn't a problem for me and if you say it's legit , I'm fine with this