How to create a custom Navigation drawer in Kotlin with Day-Night toggle
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.customnavigationdrawerimport android.os.Bundleimport androidx.fragment.app.Fragmentimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroup// TODO: Rename parameter arguments, choose names that match// the fragment initialization parameters, e.g. ARG_ITEM_NUMBERprivate 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 parametersprivate var param1: String? = nullprivate var param2: String? = nulloverride 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 fragmentreturn 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@JvmStaticfun 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 --><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:text="@string/hello_blank_fragment" /></FrameLayout>
SecondFragment.kt
package com.nk.customnavigationdrawerimport android.os.Bundleimport androidx.fragment.app.Fragmentimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroup// TODO: Rename parameter arguments, choose names that match// the fragment initialization parameters, e.g. ARG_ITEM_NUMBERprivate 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 parametersprivate var param1: String? = nullprivate var param2: String? = nulloverride 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 fragmentreturn 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@JvmStaticfun 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 --><TextViewandroid: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.xmlic_inbox.xmlic_alarm.xmlic_bedtime.xmlic_amp.xmlic_cloud.xmlic_home.xmlic_reaction.xml