package __PACKAGE__.presentation.navigation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import __PACKAGE__.presentation.components.AppBottomBar import __PACKAGE__.presentation.components.BaseScreen /** * Generic bottom-nav shell. Parameterized by a [tabs] list โ€” NOT role-hardcoded. * Hosts the selected tab's content inside a [BaseScreen] so each tab gets correct insets; * the bottom bar reserves the navigation-bar inset exactly once. * * The bar itself is [__PACKAGE__.presentation.components.AppBottomBar] โ€” promoted out of * this file into the governed component registry (ยง4.3 of the component-vocabulary * proposal): it was already a mature component, just invisible to the registry as a * `private` composable here. */ @Composable fun AppShell(tabs: List) { var selected by rememberSaveable { mutableIntStateOf(0) } BaseScreen( // testTag exposure for automation is applied once on the NavHost in AppNavHost.kt โ€” // the graph root, so every destination inherits it, not just these tabs. // The bottom bar owns the navigation-bar inset; the body must not also pad it. applyNavBarPadding = false, bottomBar = { AppBottomBar( tabs = tabs, selectedIndex = selected, onSelect = { selected = it }, ) }, ) { Box(Modifier.fillMaxSize()) { tabs.getOrNull(selected)?.content?.invoke() } } }