Jetpack Compose - 使用和不使用 BottomBar 进行导航

Jetpack Compose - Navigate both with and without BottomBar

我有一个 Jetpack Compose 项目,其中包括使用 ScaffoldBottomBar 进行导航。顺便说一下,在特定屏幕中,我希望能够在 ScaffoldinnerPadding 范围之外导航,并且只需要全屏显示可组合项。

使用 official docs 方法很好,除非您只需要导航单击 BottomBar 的项目。

我在这个 中找到了一个变通解决方案,您在其中使用了这个:

@Composable
fun OneScreen(navController: NavHostController) {
    MainScaffold(
        bottomBar = { BottomBar(navController = navController) },
        content = {
        // content
     })
}

对于需要显示 BottomBar 的屏幕,并且:

@Composable
fun AnotherScreen() {
    MainScaffold ()  {
        //content
    }
}

对于不需要显示的屏幕。

我称之为“变通解决方案”,因为通过这种方式,您需要在每个屏幕上重新生成 BottomBar,由于重组(与标准 BottomBar 导航不同),每次您单击一个项目时它都会闪烁.

我觉得应该有一个更优雅的解决方案,但我缺乏确定它的经验,直到现在我才找到它。

编辑我在相关方面错了。闪烁不是因为重组,而是因为 Navigation 组件提供的 Transition Animation (Fade out/Fade in)。因此,可以消除 'flashing' 问题设置无过渡动画。当然,这仍然是一个变通解决方案,因为它限制了用户在应用程序动画方面的灵活性。

我发现了一种解决方法,我认为它比之前 post 中链接的解决方法更好。 我决定使用以下方法:

  • 对不包含 BottomBar
  • 的所有 Screens 使用 NavGraph 导航
  • BottomBar 个屏幕项目使用条件可组合生成

通过这种方式,我认为更容易管理 Transition Animations 并具有可读代码。 我确定这不是最好的解决方案(我不会将其标记为解决方案)但我认为值得分享该方法。

主要活动

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyBottomNavTestTheme {
                val navController = rememberNavController()
                SetupNavGraph(navController)
            }
        }
    }
}

导航图

const val TEST_ROUTE = "test_route"

@Composable
fun SetupNavGraph(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = Screen.MainScreen.route,
        route = TEST_ROUTE
    ) {
        composable(Screen.MainScreen.route) { MainScreen(navController) }
        composable(Screen.AnotherScreen.route) { AnotherScreen(navController) }
    }
}

主屏幕

@Composable
fun MainScreen(navController: NavHostController){

    var selectedItem by remember { mutableStateOf(0)}

    Scaffold(
        bottomBar = { MyBottomNavBar() {selectedItem = it} }
    ) {
            selectedItem ->
            when (selectedItem){
                0 -> TabOne(navController)
                1 -> TabTwo(navController)
                2 -> TabThree(navController)
            }

    }
}

@Composable
fun MyBottomNavBar(
    onSelectedItem: (Int) -> Unit
) {
    BottomNavigation() {
        BottomNavigationItem(
            selected = true,
            onClick = { onSelectedItem(0) },
            icon = { Icon(imageVector = Icons.Filled.Person, contentDescription = "Person Icon") },
            enabled = true,
        )
        BottomNavigationItem(
            selected = true,
            onClick = { onSelectedItem(1) },
            icon = { Icon(imageVector = Icons.Filled.Phone, contentDescription = "Phone Icon") },
            enabled = true,
        )
        BottomNavigationItem(
            selected = true,
            onClick = { onSelectedItem(2) },
            icon = { Icon(imageVector = Icons.Filled.Place, contentDescription = "Place Icon") },
            enabled = true,
        )
    }
}

@Composable
fun TabOne(navController: NavHostController){
    Surface(
        modifier = Modifier
            .fillMaxSize()
    ){
        Text(
            text = "TabOne"
        )
    }
}

@Composable
fun TabTwo(navController: NavHostController){
    Surface(
        modifier = Modifier
            .fillMaxSize()
    ){
        Text(
            text = "TabTwo"
        )
    }
}

@Composable
fun TabThree(navController: NavHostController){
    Surface(
        modifier = Modifier
            .fillMaxSize()
    ){
        Column(){
            Text(
                text = "TabThree"
            )
            Button(onClick = {navController.navigate(Screen.AnotherScreen.route)}){
                Text("Go to AnotherScreen")
            }
        }

    }
}

另一个屏幕

@Composable
fun AnotherScreen(navController: NavHostController){
    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Button(onClick = {navController.navigate(Screen.MainScreen.route)}) {
            Text("Go to MainScreen")
        }
    }
}