How to Create a Navigation Drawer in Android

How to Create a Navigation Drawer in Android


A navigation drawer is a slide‑in side panel that lets users jump between top-level screens in your app. In this step‑by‑step tutorial, you’ll learn how to create a navigation drawer in Android the modern way using Kotlin and Jetpack Compose (Material 3). You’ll also see a classic Views/XML example with DrawerLayout for learners who are not on Compose yet. By the end, you’ll know when to use a drawer, how to wire it to Navigation, how to make it adaptive for tablets, and how to fix common issues like the hamburger icon not opening the drawer.

What is a Navigation Drawer and when should you use it?

How to create a navigation drawer in Android: open drawer with Material menu icons on a phone screen
What an open Material Navigation Drawer looks like in an Android app.

A navigation drawer is a panel that slides from the left (start) edge and shows top-level destinations. It works well when you have more than 3–5 primary sections, or when your app needs more space than a bottom bar can offer. In Material 3 you have:

  • Modal drawer (phones) — overlays content and closes when the user taps outside.
  • Dismissible drawer (wider layouts) — the content slides alongside the drawer.
  • Permanent drawer (large screens) — always visible like a side panel.

Use a drawer for top-level destinations. For fewer destinations on phones, prefer a bottom navigation bar; on tablets/desktops, consider a navigation rail or a permanent drawer. Material 3 supports adaptive navigation so you can swap between these patterns by window size.

Drawer type How it behaves Use it when… Closes Typical screens
ModalNavigationDrawer Slides over content; content stays put. Compact layouts with limited width. Tap outside, select item, or Back. Phones (portrait), small tablets.
DismissibleNavigationDrawer Content shifts alongside the drawer. Medium+ width where a side panel fits. Programmatically or user swipe/back. Tablets, landscape phones.
PermanentDrawerSheet Always visible; app content sized beside it. Large screens where navigation should persist. Does not close (part of layout). Desktops, large tablets.
Tip: In adaptive UIs, start with ModalNavigationDrawer and switch to Dismissible or Permanent based on window size classes.

What you’ll build

Diagram of Android Navigation Drawer structure: DrawerLayout, NavigationView, AppBar, and fragment flow
Visual layout and navigation flow for DrawerLayout, NavigationView, and fragments.

We’ll create a simple “Home / Profile / Settings” app that demonstrates:

  • Compose Material 3 ModalNavigationDrawer for phones.
  • Navigation with Navigation Compose (NavController).
  • Edge‑to‑edge content and predictive back behavior.
  • Optional: Dismissible drawer for tablets.
  • Bonus: a classic DrawerLayout + NavigationView example (Kotlin and Java).

Prerequisites

  • Android Studio (latest stable).
  • Basic Kotlin knowledge (we’ll add a short Java snippet for the Views example).
  • Min SDK 21+ (Compose supports API 21+). Target the latest API.

Quick start: how to create a navigation drawer in Android with Compose (Kotlin)

This is the modern, recommended approach. If you’re searching for “android navigation drawer tutorial for beginners” or “create navigation drawer in Android using Kotlin step by step,” follow the steps below.

1) Create a new Compose project

In Android Studio: New Project → Empty Activity (Jetpack Compose enabled).

2) Add dependencies

Open your app-level Gradle file and add Material 3, Navigation Compose, and icons. Use the latest Compose BOM in your project.

Kotlin
// build.gradle (Module)
// Use the latest versions available in your project

dependencies {
implementation(platform("androidx.compose:compose-bom:<latest_version>"))

// Compose UI + Material 3
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-tooling-preview")

// Compose icons (for menu/home/settings icons)
implementation("androidx.compose.material:material-icons-extended")

// Navigation Compose
implementation("androidx.navigation:navigation-compose")

// Activity Compose + edge-to-edge helpers
implementation("androidx.activity:activity-compose")
}

3) Enable edge-to-edge in your Activity

Edge‑to‑edge is expected on Android 15+ and makes your UI draw behind system bars with proper insets.

Kotlin
Code
// MainActivity.kt
class="cd-package">import android.os.Bundle
class="cd-package">import androidx.activity.ComponentActivity
class="cd-package">import androidx.activity.compose.setContent
class="cd-package">import androidx.activity.enableEdgeToEdge

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            App()
        }
    }
}

4) Define destinations

Create a simple model for your drawer items (route, label, icon). We’ll use three destinations.

Kotlin
Code
class="cd-package">import androidx.compose.material.icons.Icons
class="cd-package">import androidx.compose.material.icons.filled.Home
class="cd-package">import androidx.compose.material.icons.filled.Person
class="cd-package">import androidx.compose.material.icons.filled.Settings
class="cd-package">import androidx.compose.ui.graphics.vector.ImageVector

data class DrawerDestination(
    val route: String,
    val label: String,
    val icon: ImageVector
)

val HomeDest = DrawerDestination("home", "Home", Icons.Filled.Home)
val ProfileDest = DrawerDestination("profile", "Profile", Icons.Filled.Person)
val SettingsDest = DrawerDestination("settings", "Settings", Icons.Filled.Settings)

val DrawerItems = listOf(HomeDest, ProfileDest, SettingsDest)

5) Build the drawer + Navigation with Compose

In Material 3, the drawer is a dedicated composable (not a parameter on Scaffold). Use ModalNavigationDrawer for phones and wrap your Scaffold content inside it. The hamburger icon opens the drawer.

Kotlin
Code
class="cd-package">import androidx.activity.compose.BackHandler
class="cd-package">import androidx.compose.foundation.layout.fillMaxSize
class="cd-package">import androidx.compose.foundation.layout.padding
class="cd-package">import androidx.compose.material3.*
class="cd-package">import androidx.compose.material3.ModalNavigationDrawer
class="cd-package">import androidx.compose.material3.ModalDrawerSheet
class="cd-package">import androidx.compose.material3.NavigationDrawerItem
class="cd-package">import androidx.compose.runtime.*
class="cd-package">import androidx.compose.ui.Modifier
class="cd-package">import androidx.compose.ui.unit.dp
class="cd-package">import androidx.navigation.compose.NavHost
class="cd-package">import androidx.navigation.compose.composable
class="cd-package">import androidx.navigation.compose.currentBackStackEntryAsState
class="cd-package">import androidx.navigation.compose.rememberNavController
class="cd-package">import kotlinx.coroutines.launch

class="cd-annotation">@OptIn(ExperimentalMaterial3Api::class)
class="cd-annotation">@Composable
fun App() {
    val navController = rememberNavController()
    val drawerState = rememberDrawerState(DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    val backStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = backStackEntry?.destination?.route ?: HomeDest.route

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet {
                Text(
                    text = "My Compose App",
                    style = MaterialTheme.typography.titleMedium,
                    modifier = Modifier.padding(16.dp)
                )
                DrawerItems.forEach { dest ->
                    val selected = currentRoute == dest.route
                    NavigationDrawerItem(
                        label = { Text(dest.label) },
                        icon = { Icon(dest.icon, contentDescription = dest.label) },
                        selected = selected,
                        onClick = {
                            if (!selected) {
                                navController.navigate(dest.route) {
                                    launchSingleTop = true
                                    restoreState = true
                                    popUpTo(navController.graph.startDestinationId) {
                                        saveState = true
                                    }
                                }
                            }
                            scope.launch { drawerState.close() }
                        },
                        modifier = Modifier.padding(horizontal = 8.dp)
                    )
                }
            }
        }
    ) {
        Scaffold(
            topBar = {
                SmallTopAppBar(
                    title = {
                        Text(DrawerItems.find { it.route == currentRoute }?.label ?: "App")
                    },
                    navigationIcon = {
                        IconButton(onClick = { scope.launch { drawerState.open() } }) {
                            Icon(Icons.Filled.Menu, contentDescription = "Open navigation drawer")
                        }
                    }
                )
            }
        ) { innerPadding ->
            NavHost(
                navController = navController,
                startDestination = HomeDest.route,
                modifier = Modifier
                    .padding(innerPadding)
                    .fillMaxSize()
            ) {
                composable(HomeDest.route) { SimpleScreen("Home") }
                composable(ProfileDest.route) { SimpleScreen("Profile") }
                composable(SettingsDest.route) { SimpleScreen("Settings") }
            }
        }
    }

    // Predictive/back behavior: close the drawer first
    BackHandler(enabled = drawerState.isOpen) {
        scope.launch { drawerState.close() }
    }
}

class="cd-annotation">@Composable
fun SimpleScreen(title: String) {
    Surface(modifier = Modifier.fillMaxSize()) {
        Text(
            text = "$title screen",
            style = MaterialTheme.typography.headlineSmall,
            modifier = Modifier.padding(24.dp)
        )
    }
}
Drawer interaction flow (Compose): how to create a navigation drawer in Android that feels native
User taps hamburger IconButton in SmallTopAppBar
scope.launch { drawerState.open() } opens ModalNavigationDrawer
User selects a NavigationDrawerItem (e.g., Settings)
Navigate with state-safe options:
navController.navigate(route) { launchSingleTop = true; restoreState = true; popUpTo(start) { saveState = true } }
scope.launch { drawerState.close() } closes drawer; selected item stays highlighted
Back press behavior: if drawer open → BackHandler closes it; otherwise → navController.navigateUp()

6) Make it adaptive for tablets/desktops (optional)

On larger screens, a dismissible or permanent drawer improves usability. Below is a minimal example using DismissibleNavigationDrawer when the width is medium or above. You can compute a window size class in your Activity and pass it down.

Kotlin
Code
// In MainActivity, compute once and pass the size class to App(windowWidthClass)
class="cd-package">import androidx.activity.compose.setContent
class="cd-package">import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
class="cd-package">import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
class="cd-package">import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass

class="cd-annotation">@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    val windowSizeClass = calculateWindowSizeClass(this)
    setContent { AppAdaptive(windowSizeClass.widthSizeClass) }
}
Kotlin
Code
class="cd-package">import androidx.compose.material3.DismissibleNavigationDrawer
class="cd-package">import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass

class="cd-annotation">@OptIn(ExperimentalMaterial3Api::class)
class="cd-annotation">@Composable
fun AppAdaptive(widthSizeClass: WindowWidthSizeClass) {
    val useDismissible = widthSizeClass >= WindowWidthSizeClass.Medium
    if (useDismissible) {
        // Same content as App(), but wrap with DismissibleNavigationDrawer
        DismissibleApp()
    } else {
        App() // the ModalNavigationDrawer version above
    }
}

// Sketch of a Dismissible drawer variant (structure mirrors App())
class="cd-annotation">@Composable
fun DismissibleApp() { /* ...use DismissibleNavigationDrawer and a similar body... */ }

For very large screens, consider a permanent drawer (always visible) or Material 3’s adaptive NavigationSuite that can switch between a drawer, rail, or bottom bar automatically.

7) Why we chose this approach

  • Material 3 uses dedicated drawer composables such as ModalNavigationDrawer and DismissibleNavigationDrawer, not Scaffold’s old drawer slots.
  • Navigation Compose keeps your app stateful, restores state when reselecting, and avoids duplicate destinations with launchSingleTop and popUpTo.
  • Edge-to-edge drawing is on by default for Android 15+, so we call enableEdgeToEdge() and let Material 3 handle insets in top bars and sheets.
  • Predictive back is supported; our BackHandler ensures back closes the drawer first.
Build checklist: how to create a navigation drawer in Android (quick)
Compose (Kotlin)
  • ✅ Add Material 3, Navigation Compose, and icons dependencies (using the Compose BOM).
  • ✅ Call enableEdgeToEdge() in Activity.
  • ✅ Wrap Scaffold inside ModalNavigationDrawer with a ModalDrawerSheet.
  • ✅ Keep a single rememberNavController() and a single rememberDrawerState() at the app root.
  • ✅ Wire the hamburger IconButton to scope.launch { drawerState.open() }.
  • ✅ On item click: navigate with launchSingleTop/restoreState, then drawerState.close().
  • ✅ Add BackHandler(enabled = drawerState.isOpen) to close the drawer first.
  • ✅ Use start/end for RTL and test dark mode + edge-to-edge.
Views/XML
  • ✅ Make DrawerLayout the root, put content first, then NavigationView with layout_gravity="start".
  • ✅ Set MaterialToolbar as the support ActionBar: setSupportActionBar(toolbar).
  • ✅ Create AppBarConfiguration with your top-level destination IDs and the DrawerLayout.
  • ✅ Call setupActionBarWithNavController and navView.setupWithNavController.
  • ✅ Keep drawer items in res/menu/ and destinations in a NavGraph.

Classic Views/XML: DrawerLayout + NavigationView (Kotlin/Java)

If you need a “navigation drawer Android Studio step by step” example with XML, use DrawerLayout + NavigationView and wire it to the Jetpack Navigation component. This is the typical “android drawerlayout tutorial simple” pattern and works great for “android navigation drawer with fragments for beginners.”

1) Layout XML

Create res/layout/activity_main.xml with DrawerLayout as the root. Note the NavigationView is aligned to start for RTL support.

XML
Code
<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:title="My Views App" />

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="?attr/actionBarSize"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/drawer_menu" />

</androidx.drawerlayout.widget.DrawerLayout>

2) Drawer menu

Create res/menu/drawer_menu.xml with your items.

XML
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/homeFragment"
android:title="Home"
android:icon="@drawable/ic_home" />
<item
android:id="@+id/profileFragment"
android:title="Profile"
android:icon="@drawable/ic_person" />
<item
android:id="@+id/settingsFragment"
android:title="Settings"
android:icon="@drawable/ic_settings" />
</menu>

3) Navigation graph

Create res/navigation/nav_graph.xml and add your fragment destinations.

XML
Code
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.HomeFragment"
        android:label="Home" />

    <fragment
        android:id="@+id/profileFragment"
        android:name="com.example.ProfileFragment"
        android:label="Profile" />

    <fragment
        android:id="@+id/settingsFragment"
        android:name="com.example.SettingsFragment"
        android:label="Settings" />

</navigation>

4) Activity (Kotlin)

Use NavigationUI to connect the Toolbar and NavigationView to your NavController. This is the modern alternative to manually wiring an ActionBarDrawerToggle.

Kotlin
Code
class="cd-package">import android.os.Bundle
class="cd-package">import androidx.appcompat.app.AppCompatActivity
class="cd-package">import androidx.drawerlayout.widget.DrawerLayout
class="cd-package">import androidx.navigation.findNavController
class="cd-package">import androidx.navigation.ui.AppBarConfiguration
class="cd-package">import androidx.navigation.ui.navigateUp
class="cd-package">import androidx.navigation.ui.setupActionBarWithNavController
class="cd-package">import androidx.navigation.ui.setupWithNavController
class="cd-package">import com.google.android.material.appbar.MaterialToolbar
class="cd-package">import com.google.android.material.navigation.NavigationView

class MainActivity : AppCompatActivity() {

    class="cd-keyword cd-access">private lateinit var appBarConfiguration: AppBarConfiguration

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar: MaterialToolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)

        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        val navController = findNavController(R.id.nav_host_fragment)

        // Top-level destinations show the hamburger icon
        appBarConfiguration = AppBarConfiguration(
            setOf(R.id.homeFragment, R.id.profileFragment, R.id.settingsFragment),
            drawerLayout
        )

        setupActionBarWithNavController(navController, appBarConfiguration)
        navView.setupWithNavController(navController)
    }

    override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }
}

5) Activity (Java)

For “java navigation drawer example android,” here’s the equivalent Activity code in Java.

Java
Code
class="cd-package">import android.os.Bundle;
class="cd-package">import androidx.appcompat.app.AppCompatActivity;
class="cd-package">import androidx.drawerlayout.widget.DrawerLayout;
class="cd-package">import androidx.navigation.NavController;
class="cd-package">import androidx.navigation.Navigation;
class="cd-package">import androidx.navigation.ui.AppBarConfiguration;
class="cd-package">import androidx.navigation.ui.NavigationUI;
class="cd-package">import com.google.android.material.appbar.MaterialToolbar;
class="cd-package">import com.google.android.material.navigation.NavigationView;

class="cd-keyword cd-access">public class MainActivity extends AppCompatActivity {

    class="cd-keyword cd-access">private AppBarConfiguration appBarConfiguration;

    class="cd-annotation">@Override class="cd-keyword cd-access">protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MaterialToolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);

        appBarConfiguration = new AppBarConfiguration.Builder(
                R.id.homeFragment, R.id.profileFragment, R.id.settingsFragment)
                .setOpenableLayout(drawerLayout)
                .build();

        NavigationView navView = findViewById(R.id.nav_view);
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(navView, navController);
    }

    class="cd-annotation">@Override class="cd-keyword cd-access">public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp();
    }
}

Troubleshooting: hamburger icon doesn’t show or open

  • Views/XML: Ensure you call setSupportActionBar(toolbar), set up AppBarConfiguration with your top-level destination IDs, and call setupActionBarWithNavController. Do not rely on ActionBarDrawerToggle alone when using the Navigation component.
  • Views/XML: The NavigationView must have android:layout_gravity="start" and be a direct child of DrawerLayout. Ensure it’s declared after the main content.
  • Compose: Place Scaffold inside the ModalNavigationDrawer, and open the drawer in the top app bar with scope.launch { drawerState.open() }.
  • Compose: If clicks don’t respond, verify you’re using rememberDrawerState and that the drawer is not nested inside another conflicting layout.
  • Both: On back press, close the drawer first. In Compose, use BackHandler as shown.

Output / Result

On phones, tapping the hamburger icon slides a modal drawer from the left. Selecting “Home,” “Profile,” or “Settings” navigates between screens while keeping the selected state highlighted in the drawer. Tapping outside the drawer or pressing back closes it. On wider screens, the dismissible drawer stays visible as the content shifts, making multitasking easier.

FAQ: People also ask

What is a Navigation Drawer in Android and when should I use it?

A navigation drawer is a side panel for top-level destinations. Use it when you have multiple primary sections and need more space than a bottom bar offers. Prefer modal drawers for phones, and dismissible or permanent drawers for large screens. For very few destinations, a bottom bar may be better.

How do I create a Navigation Drawer in Android Studio from scratch?

Compose path: start a Compose project, add Material 3 and Navigation Compose, wrap your content in ModalNavigationDrawer, build NavigationDrawerItems, and navigate via NavController. Classic path: use DrawerLayout + NavigationView with a NavHostFragment, and connect them using NavigationUI. Both are shown above step by step.

Which is easier for beginners, Kotlin or Java, to build a Navigation Drawer?

Kotlin with Jetpack Compose is the easiest for new Android learners today. It requires less boilerplate, works seamlessly with Material 3, and integrates with Navigation Compose. If you must work with legacy code, Kotlin or Java both work with DrawerLayout, but Kotlin is generally more concise.

How do I connect Navigation Drawer items to open fragments or activities?

Compose: in NavigationDrawerItem(onClick), call navController.navigate(route) with launchSingleTop and popUpTo. Views: call navView.setupWithNavController(navController); it automatically navigates to destinations defined in your Navigation graph. For activities, prefer a single-activity app with fragments and the Navigation component.

Why doesn’t the hamburger icon show or open the drawer and how can I fix it?

Compose: ensure your top app bar’s navigation icon calls drawerState.open() and the drawer wraps the Scaffold. Views: set the Toolbar as the support action bar, create AppBarConfiguration with your top-level IDs and DrawerLayout, then call setupActionBarWithNavController. Also ensure your NavigationView has layout_gravity="start" and is a direct child of DrawerLayout.

Best practices and tips

  • Don’t overload the drawer; keep it for top-level destinations only. Use secondary surfaces (settings/about) at the bottom.
  • Preserve state when reselecting items using the Navigation options shown above.
  • Test edge-to-edge and dark mode. Material 3 handles insets and contrast for you.
  • Support RTL by using start/end, not left/right, and let the drawer mirror automatically.
  • Make navigation adaptive: drawer on large screens, bottom bar/rail on compact screens when appropriate.

Why this tutorial is up to date

This “material design navigation drawer android tutorial” uses the current Material 3 pattern. Scaffold no longer hosts the drawer; you should use dedicated drawer composables. We also covered predictive back, edge-to-edge, and adaptive navigation to fit phones, tablets, and desktops.

Wrapping up

If you’re learning how to add a navigation drawer in Android Studio, start with the Compose version above. It’s concise, modern, and beginner-friendly. If your project still uses Views, the DrawerLayout + NavigationView approach is stable and easy to follow. Either way, you now have a complete, step-by-step guide for how to create a navigation drawer in Android with working code and best practices.

Sources / Further reading

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted