Scaffold
Composable Component
Scaffold implements the basic Material Design visual layout structure.
Common
@Composable
fun Scaffold(
contentWindowInsets: WindowInsets,
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 (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,
)
Parameters
contentWindowInsets | window insets to be passed to content slot via PaddingValues params. Scaffold will take the insets into account from the top/bottom only if the topBar / bottomBar are not present, as the scaffold expect topBar /bottomBar to handle insets instead. Any insets consumed by other insets padding modifiers or consumeWindowInsets on a parent layout will be excluded from contentWindowInsets . |
modifier | optional Modifier for the root of the Scaffold |
scaffoldState | state of this scaffold widget. It contains the state of the screen, e.g. variables to provide manual control over the drawer behavior, sizes of components, etc |
topBar | top app bar of the screen. Consider using TopAppBar . |
bottomBar | bottom bar of the screen. Consider using BottomAppBar . |
snackbarHost | component to host Snackbar s that are pushed to be shown via SnackbarHostState.showSnackbar . Usually it's a SnackbarHost |
floatingActionButton | Main action button of your screen. Consider using FloatingActionButton for this slot. |
floatingActionButtonPosition | position of the FAB on the screen. See FabPosition for possible options available. |
isFloatingActionButtonDocked | whether floatingActionButton should overlap with bottomBar for half a height, if bottomBar exists. Ignored if there's no bottomBar or no floatingActionButton . |
drawerContent | content of the Drawer sheet that can be pulled from the left side (right for RTL). |
drawerGesturesEnabled | whether or not drawer (if set) can be interacted with via gestures |
drawerShape | shape of the drawer sheet (if set) |
drawerElevation | drawer sheet elevation. This controls the size of the shadow below the drawer sheet (if set) |
drawerBackgroundColor | background color to be used for the drawer sheet |
drawerContentColor | color of the content to use inside the drawer sheet. Defaults to either the matching content color for drawerBackgroundColor , or, if it is not a color from the theme, this will keep the same value set above this Surface. |
drawerScrimColor | color of the scrim that obscures content when the drawer is open |
backgroundColor | background of the scaffold body |
contentColor | color of the content in scaffold body. Defaults to either the matching content color for backgroundColor , or, if it is not a color from the theme, this will keep the same value set above this Surface. |
content | content of your screen. The lambda receives an PaddingValues that should be applied to the content root via Modifier.padding and Modifier.consumeWindowInsets to properly offset top and bottom bars. If using Modifier.verticalScroll , apply this modifier to the child of the scroll, and not on the scroll itself. |
Common
@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 (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,
)
Parameters
modifier | optional Modifier for the root of the Scaffold |
scaffoldState | state of this scaffold widget. It contains the state of the screen, e.g. variables to provide manual control over the drawer behavior, sizes of components, etc |
topBar | top app bar of the screen. Consider using TopAppBar . |
bottomBar | bottom bar of the screen. Consider using BottomAppBar . |
snackbarHost | component to host Snackbar s that are pushed to be shown via SnackbarHostState.showSnackbar . Usually it's a SnackbarHost |
floatingActionButton | Main action button of your screen. Consider using FloatingActionButton for this slot. |
floatingActionButtonPosition | position of the FAB on the screen. See FabPosition for possible options available. |
isFloatingActionButtonDocked | whether floatingActionButton should overlap with bottomBar for half a height, if bottomBar exists. Ignored if there's no bottomBar or no floatingActionButton . |
drawerContent | content of the Drawer sheet that can be pulled from the left side (right for RTL). |
drawerGesturesEnabled | whether or not drawer (if set) can be interacted with via gestures |
drawerShape | shape of the drawer sheet (if set) |
drawerElevation | drawer sheet elevation. This controls the size of the shadow below the drawer sheet (if set) |
drawerBackgroundColor | background color to be used for the drawer sheet |
drawerContentColor | color of the content to use inside the drawer sheet. Defaults to either the matching content color for drawerBackgroundColor , or, if it is not a color from the theme, this will keep the same value set above this Surface. |
drawerScrimColor | color of the scrim that obscures content when the drawer is open |
backgroundColor | background of the scaffold body |
contentColor | color of the content in scaffold body. Defaults to either the matching content color for backgroundColor , or, if it is not a color from the theme, this will keep the same value set above this Surface. |
content | content of your screen. The lambda receives an PaddingValues that should be applied to the content root via Modifier.padding and Modifier.consumeWindowInsets to properly offset top and bottom bars. If using Modifier.verticalScroll , apply this modifier to the child of the scroll, and not on the scroll itself. |
Code Examples
SimpleScaffoldWithTopBar
@Composable
fun SimpleScaffoldWithTopBar() {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
drawerContent = { Text("Drawer content") },
topBar = {
TopAppBar(
title = { Text("Simple Scaffold Screen") },
navigationIcon = {
IconButton(onClick = { scope.launch { scaffoldState.drawerState.open() } }) {
Icon(Icons.Filled.Menu, contentDescription = "Localized description")
}
},
)
},
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Inc") },
onClick = { /* fab click handler */ },
)
},
contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
content = { innerPadding ->
LazyColumn(contentPadding = innerPadding) {
items(count = 100) {
Box(Modifier.fillMaxWidth().height(50.dp).background(colors[it % colors.size]))
}
}
},
)
}
ScaffoldWithBottomBarAndCutout
@Composable
fun ScaffoldWithBottomBarAndCutout() {
val scaffoldState = rememberScaffoldState()
// Consider negative values to mean 'cut corner' and positive values to mean 'round corner'
val sharpEdgePercent = -50f
val roundEdgePercent = 45f
// Start with sharp edges
val animatedProgress = remember { Animatable(sharpEdgePercent) }
// Create a coroutineScope for the animation
val coroutineScope = rememberCoroutineScope()
// animation value to animate shape
val progress = animatedProgress.value.roundToInt()
// When progress is 0, there is no modification to the edges so we are just drawing a rectangle.
// This allows for a smooth transition between cut corners and round corners.
val fabShape =
if (progress < 0) {
CutCornerShape(abs(progress))
} else if (progress == roundEdgePercent.toInt()) {
CircleShape
} else {
RoundedCornerShape(progress)
}
// lambda to call to trigger shape animation
val changeShape: () -> Unit = {
val target = animatedProgress.targetValue
val nextTarget = if (target == roundEdgePercent) sharpEdgePercent else roundEdgePercent
coroutineScope.launch {
animatedProgress.animateTo(
targetValue = nextTarget,
animationSpec = TweenSpec(durationMillis = 600),
)
}
}
Scaffold(
scaffoldState = scaffoldState,
drawerContent = { Text("Drawer content") },
topBar = { TopAppBar(title = { Text("Scaffold with bottom cutout") }) },
bottomBar = {
BottomAppBar(cutoutShape = fabShape) {
IconButton(
onClick = { coroutineScope.launch { scaffoldState.drawerState.open() } }
) {
Icon(Icons.Filled.Menu, contentDescription = "Localized description")
}
}
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Change shape") },
onClick = changeShape,
shape = fabShape,
)
},
contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true,
content = { innerPadding ->
LazyColumn(contentPadding = innerPadding) {
items(count = 100) {
Box(Modifier.fillMaxWidth().height(50.dp).background(colors[it % colors.size]))
}
}
},
)
}
ScaffoldWithSimpleSnackbar
@Composable
fun ScaffoldWithSimpleSnackbar() {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
floatingActionButton = {
var clickCount by remember { mutableStateOf(0) }
ExtendedFloatingActionButton(
text = { Text("Show snackbar") },
onClick = {
// show snackbar as a suspend function
scope.launch {
scaffoldState.snackbarHostState.showSnackbar("Snackbar # ${++clickCount}")
}
},
)
},
contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
content = { innerPadding ->
Text(
text = "Body content",
modifier = Modifier.padding(innerPadding).fillMaxSize().wrapContentSize(),
)
},
)
}