I'm developing an app with a Settings-like screen using Jetpack Compose, that adapts to different screen sizes. The app incorporates a NavGraph
which has a top-level screen containing a LazyColumn
. Clicking on an item in the LazyColumn
takes the user to different destinations within the NavGraph
. The destinations themselves can be graphs containing multiple screens, nested inside the top-level navigation component.
For small screen devices, I use a NavHost
to navigate from the top-level screen to various nested screens. A typical nested navigation on mobile would look like this:
However, for large screen devices, I would like to use a Two Pane layout, with the LazyColumn
positioned in the left pane and the selected destination in the right pane. The destinations may further lead to additional screens, which would open in the right pane of the Two Pane layout. Something like this:
Initially, I tried using separate NavHost
s for each screen size, with one containing the top-level screen and the other without it. This would allow using one NavHost
for small screen devices and the other NavHost
to be embedded in the right pane on large screen devices. However, managing and synchronizing the open screens between the two hosts would be overly complex and impractical.
I was finally able to accomplish this with the help of this video: Navigation Compose on every screen.
To achieve such a behavior, two NavHost
s are required. One would be top-level and the other would be inside the NestedScreen
. The top-level NavHost
would look something like this:
NavHost(navController = navController, startDestination = "topLevelRoute") {
composable("topLevelRoute") {
TopLevelRoute(isExpandedWindowSize = isExpandedWindowSize, selectedItemId = selectedItemId)
}
composable("nestedScreen1") { NestedScreen1(...) }
composable("nestedScreen2") { NestedScreen2(...) }
...
}
The TopLevelRoute
composable with a TwoPane
(available in Accompanist library) would look something like this:
@Composable
fun TopLevelRoute(
isExpandedWindowSize: Boolean,
selectedItemId: String?,
) {
if (isExpandedWindowSize) {
val navController = rememberNavController()
TwoPane(
first = { TopLevelScreen(...) },
second = {
NavHost(navController = navController, startDestination = "nestedScreen1") {
composable("nestedScreen1") { NestedScreen1(...) }
composable("nestedScreen2") { NestedScreen2(...) }
...
}
},
...
} else {
TopLevelScreen(...)
}
}
Now, on a small screen device, when the user clicks on an item in the LazyColumn
, we can navigate to the nested screen using the top-level navController
while on a large screen device, we can navigate using the navController
that is inside the TopLevelRoute
.