Jetpack Compose: Scaffold with Drawer

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.


MainActivity.kt

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()
    }
}
More android jetpack compose tutorials