Introduction
Jetpack Compose has revolutionized the way Android developers build user interfaces, moving towards a more declarative programming style. Among the various UI elements it provides, the Scaffold
component is particularly valuable, as it creates a structured UI layout with elements like toolbars, drawers, and floating action buttons integrated seamlessly. This guide explores the use of Scaffold
with a drawer in Jetpack Compose, providing a clear example of how to create a layout with a top app bar and a side navigation drawer.
This example, built in Kotlin, illustrates how to implement a scaffold with a drawer, customize its appearance, and handle click events for drawer items. The Scaffold
component makes it easy to set up a cohesive layout for Android apps, giving developers control over the look and feel while simplifying the code needed to achieve it. We’ll walk through the code structure, focusing on how each composable function contributes to a polished and user-friendly UI.
Main Activity Setup
In the MainActivity.kt
file, we begin by setting up a basic AppCompatActivity
with Jetpack Compose content. The MainActivity
class overrides the onCreate
function, where setContent
is used to display the composable function GetScaffold
. This function encapsulates the main UI structure of the app, including the Scaffold
component that provides the framework for the top app bar, main content area, and navigation drawer.
The GetScaffold
function initializes the state for the Scaffold
, using rememberScaffoldState
to store its configuration, including the state of the drawer. Additionally, it defines a MutableState
variable named clickedItem
, which stores the text of the item clicked in the drawer. This clicked item’s value updates dynamically based on user interactions with the drawer items, ensuring the content area reflects the selected option.
Scaffold Setup and Customization
The Scaffold
component in Jetpack Compose provides a flexible structure for creating complex UI layouts. In this example, the Scaffold
configuration includes properties for the scaffoldState
, topBar
, content
, and drawerContent
, among others. The backgroundColor
and drawerBackgroundColor
are set to custom colors to give the app a unique look, while drawerScrimColor
and drawerContentColor
control the color overlay and text color within the drawer.
One notable customization is the drawerShape
parameter, which applies a CutCornerShape
to the bottom end of the drawer. This gives the drawer a distinct look, adding personality to the app. By setting these properties, the Scaffold
achieves a clean, visually appealing structure, with the flexibility to control colors and shapes across the app layout.
Top App Bar with a Menu Icon
The TopAppBarContent
composable function is responsible for rendering the top app bar. This app bar includes a title, background color, and a navigation icon button that opens the drawer. The navigation icon button uses the IconButton
and Icon
composable functions to display a menu icon. When the user clicks the menu icon, it triggers a coroutine that opens the drawer by modifying the drawerState
.
Using rememberCoroutineScope
, the app creates a coroutine scope, enabling asynchronous actions within composable functions. Here, the scope.launch
function opens the drawer smoothly, ensuring a seamless user experience. The top app bar not only improves navigation but also reinforces the app's brand identity by displaying a customizable title.
Drawer Content and Item Click Handling
The DrawerContent
composable function defines the structure and content of the navigation drawer. This drawer includes an app title and two clickable items, "ThumbUp" and "Favorite," each with corresponding icons. These items use the Row
composable to arrange icons and text horizontally, creating a simple yet functional layout.
Each drawer item has an associated click event, defined within the clickable
modifier. When an item is clicked, it updates the clickedItem
state with the corresponding label (e.g., "ThumbUp Clicked" or "Favorite Clicked"). This event also closes the drawer by calling scaffoldState.drawerState.close()
within a coroutine. This design allows the app to dynamically display the selected item in the main content area, reflecting user interaction in real time.
Displaying the Selected Item in Main Content
The MainContent
composable displays the text of the currently selected item from the drawer. This text is stored in the clickedItem
state, which updates whenever a drawer item is clicked. Using the Box
composable, the MainContent
centers the text both vertically and horizontally, ensuring it is easy to read and visually prominent.
The Text
composable within MainContent
displays the clickedItem
text in a specific typography style (h6
) defined by the Material theme. This minimalistic design effectively highlights the selected item, creating a responsive and engaging experience as users interact with the drawer.
Preview Function for UI Testing
The ComposablePreview
function provides a preview setup, enabling the developer to visualize the scaffold and its contents in the Android Studio preview window. While it is currently commented out, developers can uncomment GetScaffold()
within ComposablePreview
to see a live preview of the UI without running the entire app. This preview feature is beneficial for testing different UI components and verifying the layout’s appearance.
Summary
This example illustrates how to set up and customize a Scaffold
with a drawer in Jetpack Compose, creating a user-friendly interface that handles click events and dynamic content updates. The combination of Scaffold
, TopAppBar
, and drawer functionality provides a structured layout that integrates essential UI elements, such as a top app bar with a navigation menu and a responsive drawer.
By following this example, developers can better understand the flexibility of Jetpack Compose for building structured and interactive UIs in Android apps. The use of coroutine scope, composable functions, and state handling showcases a powerful, declarative approach to managing UI elements, making it easier to create engaging Android applications.
package com.cfsuman.jetpackcompose
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
GetScaffold()
}
}
@Composable
fun GetScaffold(){
val scaffoldState: ScaffoldState = rememberScaffoldState(
rememberDrawerState(DrawerValue.Closed)
)
val clickedItem:MutableState<String> = remember {
mutableStateOf("")}
Scaffold(
scaffoldState = scaffoldState,
topBar = { TopAppBarContent(scaffoldState) },
content = {MainContent(clickedItem)},
backgroundColor = Color(0xFFEDEAE0),
drawerContent = {DrawerContent(scaffoldState,clickedItem)},
drawerBackgroundColor = Color(0xFFF0F8FF),
drawerScrimColor = Color(0XFFFAE7B5),
drawerContentColor = Color(0xFF0048BA),
drawerShape = CutCornerShape(bottomEnd = 45.dp)
)
}
@Composable
fun TopAppBarContent(scaffoldState: ScaffoldState) {
val scope = rememberCoroutineScope()
TopAppBar(
title = { Text(text = "Compose - Scaffold + Drawer")},
backgroundColor = Color(0xFFC0E8D5),
navigationIcon = {
IconButton(onClick = {
// do something here
scope.launch{
scaffoldState.drawerState.open()
}
}) {
Icon(
Icons.Filled.Menu,
contentDescription = "Localized description"
)
}
}
)
}
@Composable
fun DrawerContent(
scaffoldState: ScaffoldState,
clickedItem: MutableState<String>){
val scope = rememberCoroutineScope()
Column(modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
){
Row() {
Text(
text = "APP Title",
style = MaterialTheme.typography.h5
)
}
Spacer(modifier = Modifier.requiredHeight(12.dp))
Row(modifier = Modifier
.clickable {
scope.launch {
scaffoldState.drawerState.close()
clickedItem.value = "ThumbUp Clicked"
}
}.padding(8.dp)) {
Icon(Icons.Filled.ThumbUp, contentDescription = "")
Spacer(modifier = Modifier.requiredWidth(12.dp))
Text(text ="ThumbUp")
}
Row(modifier = Modifier
.clickable {
scope.launch {
scaffoldState.drawerState.close()
clickedItem.value = "Favorite Clicked"
}
}.padding(8.dp)) {
Icon(Icons.Filled.Favorite, contentDescription = "")
Spacer(modifier = Modifier.requiredWidth(12.dp))
Text(text = "Favorite")
}
}
}
@Composable
fun MainContent(clickedItem:MutableState<String>){
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
){
Text(
text = "${clickedItem.value}",
style = MaterialTheme.typography.h6
)
}
}
@Preview
@Composable
fun ComposablePreview(){
//GetScaffold()
}
}
- jetpack compose - TopAppBar center title
- jetpack compose - TopAppBar menu
- jetpack compose - Open close drawer in code
- jetpack compose - Scaffold with Snackbar
- jetpack compose - TabRow custon indicator
- jetpack compose - Get primary language
- jetpack compose - Get screen orientation
- jetpack compose - How to hide system bars
- jetpack compose - Detect screen orientation change
- jetpack compose ktor - How to get api data
- jetpack compose - Kotlinx serialization handle null values
- jetpack compose - Kotlinx serialization not encode null values
- jetpack compose - Kotlinx serialization encode to string
- jetpack compose - Convert list to flow
- jetpack compose - Count down flow