简体   繁体   English

在 Scaffold Jetpack Compose 内的特定屏幕上隐藏顶部和底部导航器

[英]hide Top and Bottom Navigator on a specific screen inside Scaffold Jetpack Compose

I'm creating a simple app with bottom navigation and drawer.我正在创建一个带有底部导航和抽屉的简单应用程序。

I wrap all screens inside a Scaffold with topbar and bottom bar.我将所有屏幕包裹在带有顶栏和底栏的脚手架内。 I want to hide top bar and bottom bar on a specific screen.我想在特定屏幕上隐藏顶栏和底栏。 Does anyone know to how achieve that有谁知道如何实现

here is the code for setting up navigation.这是设置导航的代码。

val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))

Scaffold(
    bottomBar = {
        AppBottomBar(navController)
    },
    topBar = {
        AppTopBar(scaffoldState)
    },
    drawerContent = {
        DrawerContent(navController, scaffoldState)
    },
    scaffoldState = scaffoldState
) {
    // ovoid bottom bar overlay content
    Column(modifier = Modifier.padding(bottom = 58.dp)) {
        AppNavigation(navController)
    }
}

AppNavigation contains NavHost for navigating to screens AppNavigation包含用于导航到屏幕的NavHost

I recommend you use AnimatedVisibility for BottomNavigation widget and TopAppBar widget, im my opinion it's clearest way for compose.我建议您将AnimatedVisibility用于BottomNavigation小部件和TopAppBar小部件,我认为这是最清晰的撰写方式。

  1. You should use remeberSaveable to store state of BottomBar and TopAppBar:您应该使用remeberSaveable来存储 BottomBar 和 TopAppBar 的 state:
// State of bottomBar, set state to false, if current page route is "car_details"
val bottomBarState = rememberSaveable { (mutableStateOf(true)) }

// State of topBar, set state to false, if current page route is "car_details"
val topBarState = rememberSaveable { (mutableStateOf(true)) }
  1. In composable function we used when for control state of BottomBar and TopAppBar, below we set bottomBarState and topBarState to true , if we would like to show BottomBar and TopAppBar, otherwise we set bottomBarState and topBarState to false :在可组合的 function 中,我们用于控制 BottomBar 和 TopAppBar 的 state when ,下面我们将bottomBarStatetopBarState设置为true ,如果我们想显示 BottomBar 和 TopAppBar,否则我们将bottomBarStatetopBarState设置为false
val navController = rememberNavController()

// Subscribe to navBackStackEntry, required to get current route
val navBackStackEntry by navController.currentBackStackEntryAsState()

// Control TopBar and BottomBar
when (navBackStackEntry?.destination?.route) {
    "cars" -> {
        // Show BottomBar and TopBar
        bottomBarState.value = true
        topBarState.value = true
    }
    "bikes" -> {
        // Show BottomBar and TopBar
        bottomBarState.value = true
        topBarState.value = true
    }
    "settings" -> {
        // Show BottomBar and TopBar
        bottomBarState.value = true
        topBarState.value = true
    }
    "car_details" -> {
        // Hide BottomBar and TopBar
        bottomBarState.value = false
        topBarState.value = false
    }
}

com.google.accompanist.insets.ui.Scaffold(
    bottomBar = {
        BottomBar(
            navController = navController,
            bottomBarState = bottomBarState
        )
    },
    topBar = {
        TopBar(
            navController = navController,
            topBarState = topBarState
        )
    },
    content = {
        NavHost(
            navController = navController,
            startDestination = NavigationItem.Cars.route,
        ) {
            composable(NavigationItem.Cars.route) {
                CarsScreen(
                    navController = navController,
                )
            }
            composable(NavigationItem.Bikes.route) {
                BikesScreen(
                    navController = navController
                )
            }
            composable(NavigationItem.Settings.route) {
                SettingsScreen(
                    navController = navController,
                )
            }
            composable(NavigationItem.CarDetails.route) {
                CarDetailsScreen(
                    navController = navController,
                )
            }
        }
    }
)

Important: Scaffold from Accompanist, initialized in build.gradle.重要提示: Accompanist 的脚手架,在 build.gradle 中初始化。 We use Scaffold from Accompanist, because we need full control of paddings, for example in default Scaffold from Compose we can't disable padding for content from top if we have TopAppBar.我们使用 Accompanist 的 Scaffold,因为我们需要完全控制填充,例如,在 Compose 的默认 Scaffold 中,如果我们有 TopAppBar,我们不能禁用顶部内容的填充。 In our case it's required because we have animation for TopAppBar, content should be under TopAppBar and we manually control padding for each pages.在我们的例子中,它是必需的,因为我们有用于 TopAppBar 的 animation,内容应该在 TopAppBar 下,我们手动控制每个页面的填充。 Documentation from Accompanist: https://google.github.io/accompanist/insets/ .伴奏者的文档: https://google.github.io/accompanist/insets/

  1. Put BottomNavigation inside AnimatedVisibility , set visible value from bottomBarState and set enter and exit animation, in my case I use slideInVertically for enter animation and slideOutVertically for exit animation:BottomNavigation放入AnimatedVisibility ,从bottomBarState设置visible值并设置enterexit animation,在我的情况下,我使用slideInVertically enter animation 和slideOutVertically exit Z6F1C25ED1523962BZBBF:9
AnimatedVisibility(
        visible = bottomBarState.value,
        enter = slideInVertically(initialOffsetY = { it }),
        exit = slideOutVertically(targetOffsetY = { it }),
        content = {
            BottomNavigation {
                val navBackStackEntry by navController.currentBackStackEntryAsState()
                val currentRoute = navBackStackEntry?.destination?.route

                items.forEach { item ->
                    BottomNavigationItem(
                        icon = {
                            Icon(
                                painter = painterResource(id = item.icon),
                                contentDescription = item.title
                            )
                        },
                        label = { Text(text = item.title) },
                        selected = currentRoute == item.route,
                        onClick = {
                            navController.navigate(item.route) {
                                popUpTo(navController.graph.findStartDestination().id) {
                                    saveState = true
                                }
                                launchSingleTop = true
                                restoreState = true
                            }
                        }
                    )
                }
            }
        }
    )
  1. Put TopAppBar inside AnimatedVisibility , set visible value from topBarState and set enter and exit animation, in my case I use slideInVertically for enter animation and slideOutVertically for exit animation:TopAppBar放入AnimatedVisibility ,从topBarState设置visible值并设置enterexit animation,在我的情况下,我使用slideInVertically enter animation 和slideOutVertically exit Z6F1C25ED1525062B1BBFEED9
AnimatedVisibility(
        visible = topBarState.value,
        enter = slideInVertically(initialOffsetY = { -it }),
        exit = slideOutVertically(targetOffsetY = { -it }),
        content = {
            TopAppBar(
                title = { Text(text = title) },
            )
        }
    )

Full code of MainActivity: MainActivity 的完整代码:

package codes.andreirozov.bottombaranimation

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.res.painterResource
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import codes.andreirozov.bottombaranimation.screens.BikesScreen
import codes.andreirozov.bottombaranimation.screens.CarDetailsScreen
import codes.andreirozov.bottombaranimation.screens.CarsScreen
import codes.andreirozov.bottombaranimation.screens.SettingsScreen
import codes.andreirozov.bottombaranimation.ui.theme.BottomBarAnimationTheme

@ExperimentalAnimationApi
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BottomBarAnimationApp()
        }
    }
}

@ExperimentalAnimationApi
@Composable
fun BottomBarAnimationApp() {

    // State of bottomBar, set state to false, if current page route is "car_details"
    val bottomBarState = rememberSaveable { (mutableStateOf(true)) }

    // State of topBar, set state to false, if current page route is "car_details"
    val topBarState = rememberSaveable { (mutableStateOf(true)) }

    BottomBarAnimationTheme {
        val navController = rememberNavController()

        // Subscribe to navBackStackEntry, required to get current route
        val navBackStackEntry by navController.currentBackStackEntryAsState()

        // Control TopBar and BottomBar
        when (navBackStackEntry?.destination?.route) {
            "cars" -> {
                // Show BottomBar and TopBar
                bottomBarState.value = true
                topBarState.value = true
            }
            "bikes" -> {
                // Show BottomBar and TopBar
                bottomBarState.value = true
                topBarState.value = true
            }
            "settings" -> {
                // Show BottomBar and TopBar
                bottomBarState.value = true
                topBarState.value = true
            }
            "car_details" -> {
                // Hide BottomBar and TopBar
                bottomBarState.value = false
                topBarState.value = false
            }
        }

        // IMPORTANT, Scaffold from Accompanist, initialized in build.gradle.
        // We use Scaffold from Accompanist, because we need full control of paddings, for example
        // in default Scaffold from Compose we can't disable padding for content from top if we
        // have TopAppBar. In our case it's required because we have animation for TopAppBar,
        // content should be under TopAppBar and we manually control padding for each pages.
        com.google.accompanist.insets.ui.Scaffold(
            bottomBar = {
                BottomBar(
                    navController = navController,
                    bottomBarState = bottomBarState
                )
            },
            topBar = {
                TopBar(
                    navController = navController,
                    topBarState = topBarState
                )
            },
            content = {
                NavHost(
                    navController = navController,
                    startDestination = NavigationItem.Cars.route,
                ) {
                    composable(NavigationItem.Cars.route) {
                        // show BottomBar and TopBar
                        LaunchedEffect(Unit) {
                            bottomBarState.value = true
                            topBarState.value = true
                        }
                        CarsScreen(
                            navController = navController,
                        )
                    }
                    composable(NavigationItem.Bikes.route) {
                        // show BottomBar and TopBar
                        LaunchedEffect(Unit) {
                            bottomBarState.value = true
                            topBarState.value = true
                        }
                        BikesScreen(
                            navController = navController
                        )
                    }
                    composable(NavigationItem.Settings.route) {
                        // show BottomBar and TopBar
                        LaunchedEffect(Unit) {
                            bottomBarState.value = true
                            topBarState.value = true
                        }
                        SettingsScreen(
                            navController = navController,
                        )
                    }
                    composable(NavigationItem.CarDetails.route) {
                        // hide BottomBar and TopBar
                        LaunchedEffect(Unit) {
                            bottomBarState.value = false
                            topBarState.value = false
                        }
                        CarDetailsScreen(
                            navController = navController,
                        )
                    }
                }
            }
        )
    }
}

@ExperimentalAnimationApi
@Composable
fun BottomBar(navController: NavController, bottomBarState: MutableState<Boolean>) {
    val items = listOf(
        NavigationItem.Cars,
        NavigationItem.Bikes,
        NavigationItem.Settings
    )

    AnimatedVisibility(
        visible = bottomBarState.value,
        enter = slideInVertically(initialOffsetY = { it }),
        exit = slideOutVertically(targetOffsetY = { it }),
        content = {
            BottomNavigation {
                val navBackStackEntry by navController.currentBackStackEntryAsState()
                val currentRoute = navBackStackEntry?.destination?.route

                items.forEach { item ->
                    BottomNavigationItem(
                        icon = {
                            Icon(
                                painter = painterResource(id = item.icon),
                                contentDescription = item.title
                            )
                        },
                        label = { Text(text = item.title) },
                        selected = currentRoute == item.route,
                        onClick = {
                            navController.navigate(item.route) {
                                popUpTo(navController.graph.findStartDestination().id) {
                                    saveState = true
                                }
                                launchSingleTop = true
                                restoreState = true
                            }
                        }
                    )
                }
            }
        }
    )
}

@ExperimentalAnimationApi
@Composable
fun TopBar(navController: NavController, topBarState: MutableState<Boolean>) {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val title: String = when (navBackStackEntry?.destination?.route ?: "cars") {
        "cars" -> "Cars"
        "bikes" -> "Bikes"
        "settings" -> "Settings"
        "car_details" -> "Cars"
        else -> "Cars"
    }

    AnimatedVisibility(
        visible = topBarState.value,
        enter = slideInVertically(initialOffsetY = { -it }),
        exit = slideOutVertically(targetOffsetY = { -it }),
        content = {
            TopAppBar(
                title = { Text(text = title) },
            )
        }
    )
}

Result:结果:

BottomBar 和 TopBar 动画

Don't forget to use @ExperimentalAnimationApi annotation for compose functions.不要忘记使用 @ExperimentalAnimationApi 注释来编写函数。

Update: with Compose version 1.1.0 and above @ExperimentalAnimationApi not required.更新:不需要使用 Compose 版本 1.1.0 及更高版本@ExperimentalAnimationApi

22.02.2022 Update: I made some research, and update point 2. Now we use when for control topBarState and bottomBarState . 22.02.2022 更新:我做了一些研究,更新了点 2。现在我们使用when来控制topBarStatebottomBarState

Full code available on gitHub: https://github.com/AndreiRoze/BottomBarAnimation/tree/with_animated_topbar gitHub 上提供的完整代码: https://github.com/AndreiRoze/BottomBarAnimation/tree/with_animated_topbar

Examples of animations available in the official documentation: https://developer.android.com/jetpack/compose/animation官方文档中可用的动画示例: https://developer.android.com/jetpack/compose/animation

for now, I can achieve that by checking current route to show or hide bottomBar, topBar.现在,我可以通过检查当前路线来显示或隐藏底部栏、顶部栏来实现这一点。 But I think there's must be better solutions.但我认为必须有更好的解决方案。 The way I wrap all screens inside Scaffold might not right.我将所有屏幕包裹在 Scaffold 内的方式可能不对。

val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))

Scaffold(
    bottomBar = {
        if (currentRoute(navController) != "Example Screen") {
            AppBottomBar(navController)
        }
    },
    topBar = {
        AppTopBar(scaffoldState)
    },
    drawerContent = {
        DrawerContent(navController, scaffoldState)
    },
    floatingActionButton = {
        FloatingButton(navController)
    },
    scaffoldState = scaffoldState
) {
    // ovoid bottom bar overlay content
    Column(modifier = Modifier.padding(bottom = 58.dp)) {
        AppNavigation(navController)
    }
}

@Composable
public fun currentRoute(navController: NavHostController): String? {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    return navBackStackEntry?.arguments?.getString(KEY_ROUTE)
}

Easiest solution I found (without animation)我找到的最简单的解决方案(没有动画)

fun MainScreen(modifier: Modifier = Modifier) {

    val navController = rememberNavController()
    var showBottomBar by rememberSaveable { mutableStateOf(true) }
    val navBackStackEntry by navController.currentBackStackEntryAsState()

    showBottomBar = when (navBackStackEntry?.destination?.route) {
        "RouteOfScreenA" -> false // on this screen bottom bar should be hidden
        "RouteOfScreenB" -> false // here too
        else -> true // in all other cases show bottom bar
    }

    Scaffold(
        modifier = modifier,
        bottomBar = { if (showBottomBar) MyBottomNavigation(navController = navController) }
    ) { innerPadding ->
        MyNavHost(
            navController = navController,
            modifier = Modifier.padding(innerPadding)
        )
    }
}

you can use compositionlocal , and as you wrap your mainActivity with the CompositionLocalProvider , pass the supportActionBar to your child composable At the screen where you wish to hide the topBar, invoke the.hide() method at the top.您可以使用compositionlocal ,并且当您使用CompositionLocalProvider包装 mainActivity 时,将 supportActionBar 传递给您的子可组合项 在您希望隐藏 topBar 的屏幕上,调用顶部的 .hide() 方法。 See below:见下文:

data class ShowAppBar(val show: ActionBar?)

internal val LocalAppBar = compositionLocalOf<ShowAppBar>{ error("No ActionBar provided") }

In mainActivity, pass the ActionBar via在 mainActivity 中,通过 ActionBar

val showy = ShowAppBar(show = supportActionBar )
.....

 CompositionLocalProvider(
   LocalAppBar provides showy
  ) {
      YourTheme {
        yourApp()
      }
   }

Invoking at the screen >> LocalAppBar.current.show?.hide()在屏幕上调用 >> LocalAppBar.current.show?.hide()

This worked for me.这对我有用。 You get the current route from the currentRoute function and do a check in your bottombar composable to either hide or show the BottomNavigationView.您从 currentRoute function 获取当前路线,并检查底部栏可组合以隐藏或显示 BottomNavigationView。

  @Composable
fun currentRoute(navController: NavHostController): String? {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    return navBackStackEntry?.destination?.route
}

@Composable
fun MainScreenView() {
    val navController = rememberNavController()
    Scaffold(bottomBar = {
        if (currentRoute(navController) != BottomNavItem.Setup.screen_route)
            BottomNavigation(navController = navController)
    }
    ) {
        NavigationGraph(navController = navController)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Jetpack Compose 脚手架 + 模态底板 - Jetpack Compose Scaffold + Modal Bottom Sheet 在jetpack compose中将图像添加到脚手架顶部栏 - Adding image to scaffold top bar in jetpack compose Jetpack Compose - 无法为脚手架底部栏的高度设置动画 - Jetpack Compose - Cannot animate Scaffold's bottom bar's height 什么是脚手架? 喷气背包组成 - What is Scaffold? Jetpack compose 如何让脚手架出现在 Jetpack Compose 的顶部通知栏上? - How to get Scaffold to Appear Over Top Notification Bar in Jetpack Compose? 使用脚手架 android 折叠喷气背包中的顶部应用程序栏 - Collapsing top app bar in jetpack compose using scaffold android 如何在 android jetpack compose 中从 Scaffold 隐藏或展开 Topbar - How to hide or expand Topbar from Scaffold in android jetpack compose 如何更改 Jetpack Compose 抽屉内的 Scaffold 内的内容? - How to change the content inside a Scaffold that exists inside drawer in Jetpack Compose? 在 Jetpack Compose 中导航到没有先前脚手架的另一个屏幕 - Navigate to another screen without the previous scaffold in Jetpack Compose 屏幕滚动到顶部(Jetpack Compose 分页) - Screen scrolls to the top (Jetpack Compose Pagination)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM