【问题标题】:Scaffold with TopAppBar integration with NavigationTopAppBar 与 Navigation 集成的脚手架
【发布时间】:2020-12-01 23:16:16
【问题描述】:

如何根据导航控制器中的实际位置使用ScaffoldTopAppBar 中显示导航图标(BackArrow 或 Menu)?我正在使用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 文本,我是否必须提升此文本并为每个“屏幕”提供更改此文本的可能性,或者是否有任何简单的内置方法可以做到这一点在标准视图系统中?

【问题讨论】:

    标签: android kotlin android-jetpack-navigation android-jetpack-compose


    【解决方案1】:

    感谢Abdelilah El Aissaoui,我知道如何使用Scaffold 来实现它,只需更改bodyContent。在这种方法中,我们不必将navController 传递给任何body 元素,一切都在base 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

    【讨论】:

    • 很高兴你能够解决它:)
    【解决方案2】:

    我今天只是想实现完全相同的目标。我认为这段代码可以回答这两个问题:

    @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
        )
    }
    

    如您所见,您可以将标题作为字符串提供,这样您就不必担心传递可组合的文本。

    这个 Scaffold 是我所有屏幕的基础,使用它只需编写类似的内容:

    @Composable
    fun Home(navController: NavController) {
        NavigationScaffold(
                title = "Home",
                navController = navController
            ) {
            // Screen's content...
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2022-11-13
      • 1970-01-01
      • 2012-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-19
      • 2010-09-23
      • 1970-01-01
      相关资源
      最近更新 更多