
In this article, we’ll learn how to implement the scaffold layout in Material 3 Jetpack Compose.
Prerequisites:
What is a Scaffold in Android?
It helps us to create a basic structure for a screen. We can add different elements, such as app bars and floating action buttons, and the Scaffold makes them work together.
Example:

Let’s see how to implement it in the Android Studio.
First, create an empty Compose project and open the app-level gradle file. Add the following material icon dependency.
// this is the material icon dependency
// its size is large, so enable proguard in the production
implementation "androidx.compose.material:material-icons-extended"
Next, open MainActivity and add the following code.
import android.content.Context
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.ShoppingCart
import androidx.compose.material3.Button
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MyScaffoldLayout()
}
}
}
}
}
@Composable
fun MyScaffoldLayout() {
}
@Composable
fun MyTopAppBar() {
}
@Composable
fun MyFAB(contextForToast: Context) {
}
@Composable
fun MyBottomBar(contextForToast: Context) {
}
We added different composables for different elements such as the top app bar. We’ll write our code in them.
Jetpack Compose provides Scaffold() method. It looks like this:
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
content: @Composable (PaddingValues) -> Unit
)
modifier – The Modifier to be applied to this scaffold.
topBar – Top app bar of the screen, typically a SmallTopAppBar.
bottomBar – Bottom bar of the screen, typically a NavigationBar.
snackbarHost – Component to host Snackbars.
floatingActionButton – Main action button (FAB) of the screen, typically a FloatingActionButton.
floatingActionButtonPosition – Position of the FAB on the screen.
containerColor – The background of this scaffold. Use Color.Transparent to have no color.
contentColor – The preferred color for content inside this scaffold.
contentWindowInsets – Window insets to be passed to the content slot via PaddingValues params. The scaffold will take the insets into account from the top/bottom only if the topBar/bottomBar are not present, as the scaffold expects 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.
content – The content of the screen. The lambda receives PaddingValues that should be applied to the content root via Modifier.padding and Modifier.consumeWindowInsets to properly offset the top and bottom bars. If you are using Modifier.verticalScroll, apply this modifier to the child of the scroll, and not to the scroll itself.
In the MyScaffoldLayout(), add the following code.
@Composable
fun MyScaffoldLayout() {
Scaffold() { paddingValues ->
// rest of the app's UI
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Rest of the app UI")
}
}
}
Output:

Next, go to the MyTopAppBar(). We have different types of top app bars in Material 3 Jetpack Compose. Let’s add the smaller one.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyTopAppBar() {
TopAppBar(
title = { Text(text = "SemicolonSpace") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.4f)
)
)
}
Call it from the Scaffold’s topBar parameter.
@Composable
fun MyScaffoldLayout() {
Scaffold(
topBar = { MyTopAppBar() }
) { paddingValues ->
// rest of the app's UI
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Rest of the app UI")
}
}
}
Output:

Next, let’s add the navigation bar. Go to MyBottomBar() and add the following code.
@Composable
fun MyBottomBar(contextForToast: Context) {
val bottomBarItemsList = mutableListOf<BottomBarItem>()
bottomBarItemsList.add(BottomBarItem(icon = Icons.Default.Home, name = "Home"))
bottomBarItemsList.add(BottomBarItem(icon = Icons.Default.Person, name = "Profile"))
bottomBarItemsList.add(BottomBarItem(icon = Icons.Default.Favorite, name = "Favorites"))
var selectedItem by remember { mutableIntStateOf(0) }
NavigationBar {
bottomBarItemsList.forEachIndexed { index, item ->
NavigationBarItem(
icon = { Icon(item.icon, contentDescription = null) },
label = { Text(item.name) },
selected = selectedItem == index,
onClick = {
selectedItem = index
Toast.makeText(contextForToast, item.name, Toast.LENGTH_SHORT).show()
}
)
}
}
}
data class BottomBarItem(val icon: ImageVector, val name: String)
Call it from the Scaffold’s bottomBar parameter.
@Composable
fun MyScaffoldLayout() {
val contextForToast = LocalContext.current.applicationContext
Scaffold(
topBar = { MyTopAppBar() },
bottomBar = { MyBottomBar(contextForToast = contextForToast) }
) { paddingValues ->
// rest of the app's UI
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Rest of the app UI")
}
}
}
Output:

There are different types of floating action buttons in Material 3 Jetpack Compose. Let’s add the normal one.
@Composable
fun MyFAB(contextForToast: Context) {
FloatingActionButton(
onClick = {
Toast.makeText(contextForToast, "FAB", Toast.LENGTH_SHORT)
.show()
}
) {
Icon(imageVector = Icons.Default.Add, contentDescription = "add icon")
}
}
Call it from the Scaffold’s floatingActionButton parameter.
@Composable
fun MyScaffoldLayout() {
val contextForToast = LocalContext.current.applicationContext
Scaffold(
topBar = { MyTopAppBar() },
bottomBar = { MyBottomBar(contextForToast = contextForToast) },
floatingActionButton = { MyFAB(contextForToast = contextForToast) },
floatingActionButtonPosition = FabPosition.End,
) { paddingValues ->
// rest of the app's UI
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Rest of the app UI")
}
}
}
Output:

There are 3 positions available for the FAB:
- FabPosition.Start
- FabPosition.Center
- FabPosition.End
Let’s add the Snackbar.
@Composable
fun MyScaffoldLayout() {
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
val contextForToast = LocalContext.current.applicationContext
var clickCount by remember {
mutableIntStateOf(0) // or use mutableStateOf(0)
}
Scaffold(
topBar = { MyTopAppBar() },
bottomBar = { MyBottomBar(contextForToast = contextForToast) },
floatingActionButton = { MyFAB(contextForToast = contextForToast) },
floatingActionButtonPosition = FabPosition.End,
snackbarHost = { SnackbarHost(snackbarHostState) }
) { paddingValues ->
// rest of the app's UI
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Rest of the app UI")
Button(
onClick = {
coroutineScope.launch {
snackbarHostState.showSnackbar(
message = "Snackbar # ${++clickCount}",
duration = SnackbarDuration.Short
)
}
}
) {
Text(text = "Show Snackbar")
}
}
}
}
Output:

Material 3 Navigation Drawer with Scaffold:
Jetpack Compose provides ModalNavigationDrawer() method. The scaffold doesn’t have the parameter for it. So, instead, we add the Scaffold as the ModalNavigationDrawer content.
@Composable
fun MyScaffoldLayout() {
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
LocalContext.current.applicationContext
val drawerItemList = prepareNavigationDrawerItems()
val selectedItem = remember { mutableStateOf(drawerItemList[0]) }
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
// add drawer content here
// this is a column scope
// so, if you add multiple elements, they are placed vertically
Spacer(Modifier.height(12.dp))
drawerItemList.forEach { item ->
NavigationDrawerItem(
icon = { Icon(imageVector = item.icon, contentDescription = null) },
label = { Text(text = item.label) },
selected = (item == selectedItem.value),
onClick = {
coroutineScope.launch {
drawerState.close()
}
selectedItem.value = item
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
}
}
) {
// app content
// add scaffold here
Scaffold(
topBar = {
MyTopAppBar {
coroutineScope.launch {
drawerState.open()
}
}
}
) { paddingValues ->
// rest of the app's UI
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Rest of the app UI")
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyTopAppBar(onNavIconClick: () -> Unit) {
TopAppBar(
title = { Text(text = "SemicolonSpace") },
navigationIcon = {
IconButton(
onClick = {
onNavIconClick()
}
) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = "Open Navigation Items"
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.4f)
)
)
}
private fun prepareNavigationDrawerItems(): List<NavigationDrawerData> {
val drawerItemsList = arrayListOf<NavigationDrawerData>()
// add items
drawerItemsList.add(NavigationDrawerData(label = "Home", icon = Icons.Filled.Home))
drawerItemsList.add(NavigationDrawerData(label = "Profile", icon = Icons.Filled.Person))
drawerItemsList.add(NavigationDrawerData(label = "Cart", icon = Icons.Filled.ShoppingCart))
drawerItemsList.add(NavigationDrawerData(label = "Settings", icon = Icons.Filled.Settings))
return drawerItemsList
}
data class NavigationDrawerData(val label: String, val icon: ImageVector)
Output:

This is all about the Scaffold layout in Material 3 Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.
Related Articles:
References: