具有 TopAppBar 与导航集成的脚手架
Scaffold with TopAppBar integration with Navigation
如何根据 NavController 中的实际位置使用 Scaffold
在 TopAppBar
中显示导航图标(后退箭头或菜单)?我正在使用 Navigating with Compose 1.0.0-alpha02。下面是一个示例代码,其中描述了它应该如何工作
@Composable
fun App()
{
val navController = rememberNavController()
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "App title") },
navigationIcon = {
/*
Check if navController back stack has more
than one element. If so show BackButton.
Clicking on that button will move back
*/
val canMoveBack = true
if (canMoveBack)
{
IconButton(onClick = {
// Move back
navController.popBackStack()
}) {
Icon(asset = Icons.Outlined.ArrowBack)
}
}
else
{
IconButton(onClick = {
// show NavDrawer
}) {
Icon(asset = Icons.Outlined.Menu)
}
}
},
)
},
bodyContent = {
AppBody(navController)
}
)
}
我想过 navController.backStack.size
之类的东西,但我得到了错误 NavController.getBackStack can only be called from within the same library group (groupId=androidx.navigation)
。
第二个问题,如果我想更改 TopAppBar
文本,我是否必须提升此文本并为每个“屏幕”提供更改此文本的可能性,或者是否有任何简单的内置方法像在标准视图系统中那样执行此操作?
我今天只是想达到完全一样的效果。我认为这段代码将回答这两个问题:
@Composable
fun NavigationScaffold(
title: String? = null,
navController: NavController? = null,
bodyContent: @Composable (PaddingValues) -> Unit
) {
val navigationIcon: (@Composable () -> Unit)? =
if (navController?.previousBackStackEntry != null) {
{
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(Icons.Filled.ArrowBack)
}
}
} else {
// this can be null or another component
// If null, the navigationIcon won't be rendered at all
null
}
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = title.orEmpty())
},
navigationIcon = navigationIcon,
)
},
bodyContent = bodyContent
)
}
如您所见,您可以将标题作为字符串提供,这样您就不必担心传递文本可组合项。
这个脚手架是我所有屏幕的基础,要使用它,只需编写如下内容:
@Composable
fun Home(navController: NavController) {
NavigationScaffold(
title = "Home",
navController = navController
) {
// Screen's content...
}
}
多亏了 Abdelilah El Aissaoui,我知道了如何用一个 Scaffold
来完成它并且只是改变 bodyContent
。在这个方法中,我们不必将 navController
传递给任何 body 元素,一切都在基础 App 可组合中完成。下面是可以在两个物体之间导航的代码(课程 -> 学生)
应用程序:
@Composable
fun App(
viewModel: MainViewModel
)
{
val navController = rememberNavController()
val baseTitle = "" // stringResource(id = R.string.app_name)
val (title, setTitle) = remember { mutableStateOf(baseTitle) }
val (canPop, setCanPop) = remember { mutableStateOf(false) }
val scaffoldState: ScaffoldState = rememberScaffoldState()
navController.addOnDestinationChangedListener { controller, _, _ ->
setCanPop(controller.previousBackStackEntry != null)
}
// check navigation state and navigate
if (viewModel.navigateToStudents.value)
{
navController.navigate(route = STUDENT_SCREEN_ROUTE)
viewModel.studentsNavigated()
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = title) },
navigationIcon = {
if (canPop)
{
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(asset = Icons.Outlined.ArrowBack)
}
}
else
{
IconButton(onClick = {
scaffoldState.drawerState.open()
}) {
Icon(asset = Icons.Outlined.Menu)
}
}
},
)
},
scaffoldState = scaffoldState,
drawerContent = {
DrawerContent()
},
bodyContent = {
AppBody(
viewModel = viewModel,
navController = navController,
setTitle = setTitle
)
}
)
}
AppBody
@Composable
fun AppBody(
viewModel: MainViewModel,
navController: NavHostController,
setTitle: (String) -> Unit,
)
{
NavHost(
navController,
startDestination = LESSON_SCREEN_ROUTE
) {
composable(route = LESSON_SCREEN_ROUTE) {
LessonBody(
viewModel = viewModel,
setTitle = setTitle
)
}
composable(
route = STUDENT_SCREEN_ROUTE
) {
StudentBody(
viewModel = viewModel,
setTitle = setTitle
)
}
}
}
在 ViewModel 中,我使用此模式进行导航:
private val _navigateToStudents: MutableState<Boolean> = mutableStateOf(false)
val navigateToStudents: State<Boolean> = _navigateToStudents
fun studentsNavigated()
{
// here we can add any logic after doing navigation
_navigateToStudents.value = false
}
所以当我想导航到下一个片段时,我只需设置 _navigateToStudents.value = true
如何根据 NavController 中的实际位置使用 Scaffold
在 TopAppBar
中显示导航图标(后退箭头或菜单)?我正在使用 Navigating with Compose 1.0.0-alpha02。下面是一个示例代码,其中描述了它应该如何工作
@Composable
fun App()
{
val navController = rememberNavController()
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "App title") },
navigationIcon = {
/*
Check if navController back stack has more
than one element. If so show BackButton.
Clicking on that button will move back
*/
val canMoveBack = true
if (canMoveBack)
{
IconButton(onClick = {
// Move back
navController.popBackStack()
}) {
Icon(asset = Icons.Outlined.ArrowBack)
}
}
else
{
IconButton(onClick = {
// show NavDrawer
}) {
Icon(asset = Icons.Outlined.Menu)
}
}
},
)
},
bodyContent = {
AppBody(navController)
}
)
}
我想过 navController.backStack.size
之类的东西,但我得到了错误 NavController.getBackStack can only be called from within the same library group (groupId=androidx.navigation)
。
第二个问题,如果我想更改 TopAppBar
文本,我是否必须提升此文本并为每个“屏幕”提供更改此文本的可能性,或者是否有任何简单的内置方法像在标准视图系统中那样执行此操作?
我今天只是想达到完全一样的效果。我认为这段代码将回答这两个问题:
@Composable
fun NavigationScaffold(
title: String? = null,
navController: NavController? = null,
bodyContent: @Composable (PaddingValues) -> Unit
) {
val navigationIcon: (@Composable () -> Unit)? =
if (navController?.previousBackStackEntry != null) {
{
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(Icons.Filled.ArrowBack)
}
}
} else {
// this can be null or another component
// If null, the navigationIcon won't be rendered at all
null
}
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = title.orEmpty())
},
navigationIcon = navigationIcon,
)
},
bodyContent = bodyContent
)
}
如您所见,您可以将标题作为字符串提供,这样您就不必担心传递文本可组合项。
这个脚手架是我所有屏幕的基础,要使用它,只需编写如下内容:
@Composable
fun Home(navController: NavController) {
NavigationScaffold(
title = "Home",
navController = navController
) {
// Screen's content...
}
}
多亏了 Abdelilah El Aissaoui,我知道了如何用一个 Scaffold
来完成它并且只是改变 bodyContent
。在这个方法中,我们不必将 navController
传递给任何 body 元素,一切都在基础 App 可组合中完成。下面是可以在两个物体之间导航的代码(课程 -> 学生)
应用程序:
@Composable
fun App(
viewModel: MainViewModel
)
{
val navController = rememberNavController()
val baseTitle = "" // stringResource(id = R.string.app_name)
val (title, setTitle) = remember { mutableStateOf(baseTitle) }
val (canPop, setCanPop) = remember { mutableStateOf(false) }
val scaffoldState: ScaffoldState = rememberScaffoldState()
navController.addOnDestinationChangedListener { controller, _, _ ->
setCanPop(controller.previousBackStackEntry != null)
}
// check navigation state and navigate
if (viewModel.navigateToStudents.value)
{
navController.navigate(route = STUDENT_SCREEN_ROUTE)
viewModel.studentsNavigated()
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = title) },
navigationIcon = {
if (canPop)
{
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(asset = Icons.Outlined.ArrowBack)
}
}
else
{
IconButton(onClick = {
scaffoldState.drawerState.open()
}) {
Icon(asset = Icons.Outlined.Menu)
}
}
},
)
},
scaffoldState = scaffoldState,
drawerContent = {
DrawerContent()
},
bodyContent = {
AppBody(
viewModel = viewModel,
navController = navController,
setTitle = setTitle
)
}
)
}
AppBody
@Composable
fun AppBody(
viewModel: MainViewModel,
navController: NavHostController,
setTitle: (String) -> Unit,
)
{
NavHost(
navController,
startDestination = LESSON_SCREEN_ROUTE
) {
composable(route = LESSON_SCREEN_ROUTE) {
LessonBody(
viewModel = viewModel,
setTitle = setTitle
)
}
composable(
route = STUDENT_SCREEN_ROUTE
) {
StudentBody(
viewModel = viewModel,
setTitle = setTitle
)
}
}
}
在 ViewModel 中,我使用此模式进行导航:
private val _navigateToStudents: MutableState<Boolean> = mutableStateOf(false)
val navigateToStudents: State<Boolean> = _navigateToStudents
fun studentsNavigated()
{
// here we can add any logic after doing navigation
_navigateToStudents.value = false
}
所以当我想导航到下一个片段时,我只需设置 _navigateToStudents.value = true