Close
Close full mode
logoAppDevAssist

How to create a custom Navigation drawer in Kotlin with Day-Night toggle

Last update: 2 months ago by n-k-dReading time: 4 min

How to create a custom Navigation drawer in Kotlin with Day-Night toggle:

In this post, we will learn how to create a Navigation drawer in Kotlin with Day-Night theme toggle. If user will change theme to dark in the settings page, it will pick the night theme. Else, it will pick the day theme.

YouTube video:

I have published 3 videos on YouTube explaining the process in step by step.

Video 1: Learn to build the Navigation drawer header:

Video 2: Learn to build the Navigation drawer:

Video 3: Learn to add Dark-night mode to the Navigation drawer:

Android Studio Project:

Open Android studio and create one basic android kotlin project. This project one activity and two fragments. I am creating two fragments, but in a real project, you need to create one fragment for each navigation drawer item.

build.gradle file:

Following are the dependencies I am using for this project:

implementation 'org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'

Fragments:

Following are the fragments and xml layout files:

FirstFragment.kt

package com.nk.customnavigationdrawer
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [FirstFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class FirstFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment FirstFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
FirstFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}

fragment_first.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>

SecondFragment.kt

package com.nk.customnavigationdrawer
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [SecondFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class SecondFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment SecondFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}

fragment_second.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>

res/values:

We have few attribute names, colors and *strings:

values/app_attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name ="drawerItemColorChecked" format="color"/>
<attr name ="drawerItemColorNormal" format="color"/>
<attr name ="drawerItemBackground" format="color"/>
<attr name="drawerBackground" format="color"/>
<attr name="navHeaderTitleColor" format="color"/>
<attr name="navHeaderDescriptionColor" format="color"/>
</resources>

values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="orange_500">#ff9800</color>
<color name="orange_600">#fb8c00</color>
<color name="gray_100">#F5F5F5</color>
<color name="gray_200">#EEEEEE</color>
<color name="gray_800">#424242</color>
<color name="gray_900">#757575</color>
<color name="yellow_500">#ffeb3b</color>
<color name="yellow_800">#F9A825</color>
<color name="white">#ffffff</color>
<color name="blue_gray_700">#455A64</color>
<color name="blue_gray_900">#263238</color>
</resources>

values/string.xml

<resources>
<string name="app_name">CustomNavigationDrawer</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>

values/themes

We have two themes. One is for the dark mode and one is for night mode.

values/themes.xml

<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.CustomNavigationDrawer" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/orange_600</item>
<item name="colorPrimaryVariant">@color/orange_600</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="drawerItemColorChecked">@color/yellow_500</item>
<item name="drawerItemColorNormal">@color/gray_900</item>
<item name="drawerItemBackground">@color/gray_800</item>
<item name="drawerBackground">@color/white</item>
<item name="navHeaderTitleColor">@color/gray_900</item>
<item name="navHeaderDescriptionColor">@color/gray_800</item>
</style>
<style name="Theme.NavigationDrawer" parent="ThemeOverlay.MaterialComponents.Light">
<item name="android:scrollbarThumbVertical">@android:color/transparent</item>
<item name="colorControlHighlight">@color/yellow_500</item>
<item name="colorPrimary">@color/orange_500</item>
</style>
</resources>

values-night/themes.xml

<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.CustomNavigationDrawer" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/gray_900</item>
<item name="colorPrimaryVariant">@color/gray_900</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="drawerItemColorChecked">@color/orange_600</item>
<item name="drawerItemColorNormal">@color/gray_100</item>
<item name="drawerItemBackground">@color/blue_gray_700</item>
<item name="drawerBackground">@color/blue_gray_900</item>
<item name="navHeaderTitleColor">@color/gray_100</item>
<item name="navHeaderDescriptionColor">@color/gray_200</item>
</style>
<style name="Theme.NavigationDrawer" parent="ThemeOverlay.MaterialComponents.Light">
<item name="android:scrollbarThumbVertical">@android:color/transparent</item>
<item name="colorControlHighlight">@color/yellow_500</item>
<item name="colorPrimary">@color/orange_500</item>
</style>
</resources>

You can see that we are using different colors with same item name in both themes.

res/color

This is a color xml file to use as the background for drawer items. It picks different color if the drawer item is selected.

res/color/drawer_item_text_icon.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/drawerItemColorChecked" android:state_checked="true" />
<item android:color="?attr/drawerItemColorNormal" />
</selector>

res/drawable:

There are few drawable files that you need to add to your project. You can download them from the link I have added below.

icon:

It has only one icon, a drawable to show for the user profile image. It is avatar.png

drawer_item_bg.xml

It is the background for the navigation drawer items if it is selected.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?attr/drawerItemBackground"/>
</shape>

It picks different color for day and night mode

drawer_item_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/drawer_item_bg"/>
</selector>

Background for each drawer item. It shows drawer_item_bg for selected items.

icons:

There are eight icons to show with the drawer items:

ic_adb.xml
ic_inbox.xml
ic_alarm.xml
ic_bedtime.xml
ic_amp.xml
ic_cloud.xml
ic_home.xml
ic_reaction.xml

ic_nav_background.xml:

It is the background for the navigation header.

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1440dp"
android:height="320dp"
android:viewportWidth="1440"
android:viewportHeight="320">
<path
android:pathData="M0,160L120,165.3C240,171 480,181 720,208C960,235 1200,277 1320,298.7L1440,320L1440,0L1320,0C1200,0 960,0 720,0C480,0 240,0 120,0L0,0Z"
android:fillColor="#9e9e9e"/>
</vector>

It is a vector asset.

res/menu

We have only one menu file and that file is shown in the navigation drawer. It is main_menu.xml.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:icon="@drawable/ic_home"
android:id="@+id/firstFragment"
android:title="Home" />
<item
android:id="@+id/secondFragment"
android:icon="@drawable/ic_inbox"
android:title="Inbox" />
</group>
<item android:title="Profile">
<menu >
<group android:checkableBehavior="single">
<item
android:id="@+id/thirdFragment"
android:icon="@drawable/ic_adb"
android:title="Manage Coins" />
<item
android:id="@+id/fourthFragment"
android:icon="@drawable/ic_amp"
android:title="Your Coins" />
<item
android:id="@+id/fifthFragment"
android:icon="@drawable/ic_alarm"
android:title="Buy Coins" />
<item
android:id="@+id/sixthFragment"
android:icon="@drawable/ic_reaction"
android:title="Sell Coins" />
</group>
</menu>
</item>
<item android:title="Account">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/seventhFragment"
android:icon="@drawable/ic_bedtime"
android:title="Manage Account" />
<item
android:id="@+id/eithFargment"
android:icon="@drawable/ic_cloud"
android:title="Followers" />
<item
android:id="@+id/ninethFragment"
android:icon="@drawable/ic_adb"
android:title="Followings" />
<item
android:id="@+id/tenthFragment"
android:icon="@drawable/ic_inbox"
android:title="Logout" />
</group>
</menu>
</item>
<group android:id="@+id/about">
<item
android:checkable="true"
android:title="About"/>
<item
android:checkable="true"
android:title="Contact Us"/>
</group>
</menu>

Note that we are diving them in groups.

res/layout

Subscribe to our Newsletter

Previous
How to create a Floating Action Button in Android using Kotlin
Next
How to add Chip and ChipGroup in Android Studio