What you'll learn
Nearly every screen shares the same skeleton: a bar on top, content in the middle, maybe a floating button and a place for messages. Material 3 gives you Scaffold to assemble that skeleton correctly — including the fiddly parts like keeping content from hiding behind the bars.
By the end you'll be able to:
- Structure a screen with
Scaffoldand its slots - Add a
TopAppBarwith a title, navigation icon, and actions - Add a
FloatingActionButtonfor the primary action - Show
Snackbarmessages with an optional action
The Scaffold
Scaffold is a layout with named slots for the standard pieces — topBar, bottomBar, floatingActionButton, snackbarHost — and a content lambda for the body. Crucially, it hands your content an innerPadding value: the space the bars occupy. You must apply it, or your first list item will hide under the app bar:
@Composable
fun HomeScreen() {
Scaffold(
topBar = { TopAppBar(title = { Text("Tasks") }) },
floatingActionButton = {
FloatingActionButton(onClick = { /* add */ }) {
Icon(Icons.Default.Add, contentDescription = "Add task")
}
},
) { innerPadding ->
// Content receives padding that avoids the bars — always apply it!
LazyColumn(modifier = Modifier.padding(innerPadding)) {
items(tasks) { task -> TaskRow(task) }
}
}
}Watch out
innerPadding. Always pass it to your content's modifier (Modifier.padding(innerPadding)) — otherwise the top of your content slides under the TopAppBar.App bars
TopAppBar holds the screen title and up to three kinds of controls: a navigationIcon on the left (a back arrow or menu), and actions on the right (search, overflow, etc.). There are variants — CenterAlignedTopAppBar, LargeTopAppBar — for different looks:
TopAppBar(
title = { Text("Inbox") },
navigationIcon = {
IconButton(onClick = { /* open drawer / go back */ }) {
Icon(Icons.Default.Menu, contentDescription = "Menu")
}
},
actions = {
IconButton(onClick = { /* search */ }) {
Icon(Icons.Default.Search, contentDescription = "Search")
}
},
)Floating action button
The FAB represents a screen's single most important action — compose a message, add an item, start a chat. Put it in the Scaffold's floatingActionButton slot so it floats above the content in the corner. Give it a clear icon and, always, a contentDescription for accessibility. Use at most one FAB per screen.
Snackbars
A Snackbar is a brief message at the bottom of the screen, optionally with a single action like "Undo." Showing one is a suspend function, so you trigger it from a coroutine scope tied to the composition. Wire a SnackbarHostState into the Scaffold's snackbarHost slot, then call showSnackbar:
@Composable
fun UndoScreen() {
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
) { padding ->
Button(
onClick = {
scope.launch { // showing is a suspend call
val result = snackbarHostState.showSnackbar(
message = "Item deleted",
actionLabel = "Undo",
)
if (result == SnackbarResult.ActionPerformed) { /* undo */ }
}
},
modifier = Modifier.padding(padding),
) { Text("Delete") }
}
}Tip
showSnackbar returns a SnackbarResult — check for ActionPerformed to know the user tapped "Undo." This is the standard pattern for reversible actions: act immediately, show a snackbar, and undo if they ask. We'll cover coroutine scopes properly in Phase 4; for now, rememberCoroutineScope() gives you a safe one.Recap & quick check
Key takeaways
- Scaffold provides slots for topBar, bottomBar, floatingActionButton, and snackbarHost, plus a content body.
- Always apply the innerPadding Scaffold gives your content, or it hides behind the bars.
- TopAppBar holds the title, a navigationIcon (back/menu), and actions (search, overflow).
- Use at most one FloatingActionButton per screen for its primary action — with a contentDescription.
- Snackbars show brief messages with an optional action; showSnackbar is a suspend call returning a result.
Quick check
1. What does Scaffold's content lambda receive that you must apply?
2. Where does the screen title go?
3. How many FABs should a screen have?
4. Why is showSnackbar called inside a coroutine scope?
5. How do you know the user tapped a snackbar's action?
Your screens are properly structured. To finish Phase 2, let's make them yours with a custom theme, dark mode, and dynamic color.