跳转至

Scaffold

@Composable
fun Scaffold(
    modifier: Modifier? = Modifier,
    scaffoldState: ScaffoldState? = rememberScaffoldState(),
    topBar: (@Composable () -> Unit)? = {},
    bottomBar: (@Composable () -> Unit)? = {},
    snackbarHost: (@Composable (SnackbarHostState) -> Unit)? = { SnackbarHost(it) },
    floatingActionButton: (@Composable () -> Unit)? = {},
    floatingActionButtonPosition: FabPosition? = FabPosition.End,
    isFloatingActionButtonDocked: Boolean? = false,
    drawerContent: (@Composable @ExtensionFunctionType ColumnScope.() -> Unit)? = null,
    drawerGesturesEnabled: Boolean? = true,
    drawerShape: Shape? = MaterialTheme.shapes.large,
    drawerElevation: Dp? = DrawerDefaults.Elevation,
    drawerBackgroundColor: Color? = MaterialTheme.colors.surface,
    drawerContentColor: Color? = contentColorFor(drawerBackgroundColor),
    drawerScrimColor: Color? = DrawerDefaults.scrimColor,
    backgroundColor: Color? = MaterialTheme.colors.background,
    contentColor: Color? = contentColorFor(backgroundColor),
    content: (@Composable (PaddingValues) -> Unit)?
): Unit

Scaffold 是系统提供的一种脚手架,可以快速的搭建一个页面结构,包含

  • topBar 通常是一个 TopAppBar
  • bottomBar 通常是一个 BottomNavigation
  • floatingActionButton 悬浮按钮
  • floatingActionButtonPosition 悬浮按钮位置
  • isFloatingActionButtonDocked 悬浮按钮是否贴到 bottomBar 上
  • drawerContent 侧滑菜单
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ScaffoldSample2() {
    val bottomData = listOf("Home", "List", "User")
    var bottomNavigationCurrentIndex by remember {
        mutableStateOf(0)
    }

    Scaffold(
        topBar = {
            TopAppBar(title = { Text(text = "Title") }, navigationIcon = {
                Icon(imageVector = Icons.Default.NavigateBefore, contentDescription = null)
            }, actions = {
                Icon(imageVector = Icons.Default.Add, contentDescription = null)
            })
        },
        bottomBar = {
            BottomNavigation() {
                bottomData.forEachIndexed { index, item ->
                    BottomNavigationItem(
                        selected = bottomNavigationCurrentIndex == index,
                        onClick = { bottomNavigationCurrentIndex = index }, icon = {
                            BadgeBox(badgeContent = {
                                Text("1")
                            }) {
                                Icon(
                                    imageVector = Icons.Default.AccountBox,
                                    contentDescription = null
                                )
                            }
                        }, label = { Text(item) })
                }
            }
        },
        floatingActionButton = {
            ExtendedFloatingActionButton(
                icon = {
                    Icon(
                        imageVector = Icons.Default.SupervisedUserCircle,
                        contentDescription = null
                    )
                },
                text = { Text("Button") },
                onClick = { /*TODO*/ })
        },
        floatingActionButtonPosition = FabPosition.Center,
        isFloatingActionButtonDocked = true
    ) {
        Text("Body Content")
    }
}

Scaffold

BackdropScaffold

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ScaffoldSample6() {
    val scope = rememberCoroutineScope()
    val selection = remember { mutableStateOf(1) }
    val scaffoldState = rememberBackdropScaffoldState(BackdropValue.Concealed)
    LaunchedEffect(scaffoldState) {
        scaffoldState.reveal()
    }
    BackdropScaffold(
        scaffoldState = scaffoldState,
        appBar = {
            TopAppBar(
                title = { Text("Backdrop scaffold") },
                navigationIcon = {
                    if (scaffoldState.isConcealed) {
                        IconButton(onClick = { scope.launch { scaffoldState.reveal() } }) {
                            Icon(Icons.Default.Menu, contentDescription = "Localized description")
                        }
                    } else {
                        IconButton(onClick = { scope.launch { scaffoldState.conceal() } }) {
                            Icon(Icons.Default.Close, contentDescription = "Localized description")
                        }
                    }
                },
                actions = {
                    var clickCount by remember { mutableStateOf(0) }
                    IconButton(
                        onClick = {
                            // show snackbar as a suspend function
                            scope.launch {
                                scaffoldState.snackbarHostState
                                    .showSnackbar("Snackbar #${++clickCount}")
                            }
                        }
                    ) {
                        Icon(Icons.Default.Favorite, contentDescription = "Localized description")
                    }
                },
                elevation = 0.dp,
                backgroundColor = Color.Transparent
            )
        },
        backLayerContent = {
            LazyColumn {
                items(if (selection.value >= 3) 3 else 5) {
                    ListItem(
                        Modifier.clickable {
                            selection.value = it
                            scope.launch { scaffoldState.conceal() }
                        },
                        text = { Text("Select $it") }
                    )
                }
            }
        },
        frontLayerContent = {
            Text("Selection: ${selection.value}")
            LazyColumn {
                items(50) {
                    ListItem(
                        text = { Text("Item $it") },
                        icon = {
                            Icon(
                                Icons.Default.Favorite,
                                contentDescription = "Localized description"
                            )
                        }
                    )
                }
            }
        }
    )

}

BackdropScaffold

BottomSheetScaffold

在 Scaffold 的基础上还可以从底部上拉出菜单

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ScaffoldSample7() {
    val scope = rememberCoroutineScope()
    val scaffoldState = rememberBottomSheetScaffoldState()
    BottomSheetScaffold(
        sheetContent = {
            Box(
                Modifier.fillMaxWidth().height(128.dp),
                contentAlignment = Alignment.Center
            ) {
                Text("Swipe up to expand sheet")
            }
            Column(
                Modifier.fillMaxWidth().padding(64.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text("Sheet content")
                Spacer(Modifier.height(20.dp))
                Button(
                    onClick = {
                        scope.launch { scaffoldState.bottomSheetState.collapse() }
                    }
                ) {
                    Text("Click to collapse sheet")
                }
            }
        },
        scaffoldState = scaffoldState,
        topBar = {
            TopAppBar(
                title = { Text("Bottom sheet scaffold") },
                navigationIcon = {
                    IconButton(onClick = { scope.launch { scaffoldState.drawerState.open() } }) {
                        Icon(Icons.Default.Menu, contentDescription = "Localized description")
                    }
                }
            )
        },
        floatingActionButton = {
            var clickCount by remember { mutableStateOf(0) }
            FloatingActionButton(
                onClick = {
                    // show snackbar as a suspend function
                    scope.launch {
                        scaffoldState.snackbarHostState.showSnackbar("Snackbar #${++clickCount}")
                    }
                }
            ) {
                Icon(Icons.Default.Favorite, contentDescription = "Localized description")
            }
        },
        floatingActionButtonPosition = FabPosition.End,
        sheetPeekHeight = 128.dp,
        drawerContent = {
            Column(
                Modifier.fillMaxWidth().padding(16.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text("Drawer content")
                Spacer(Modifier.height(20.dp))
                Button(onClick = { scope.launch { scaffoldState.drawerState.close() } }) {
                    Text("Click to close drawer")
                }
            }
        }
    ) { innerPadding ->
        LazyColumn(contentPadding = innerPadding) {
            items(100) {
                Box(
                    Modifier
                        .fillMaxWidth()
                        .height(50.dp)
                )
            }
        }
    }

}

BottomSheetScaffold

视频教程