Setup Android BottomNavigationView With Fragment (Kotlin)

NOTE: Refer Android Setup BottomNavigationView With Jetpack Navigation UI (Kotlin).

We shall create an Activity with BottomNavigationView which switches the main view the fragments.

You can create a Bottom Navigation Activity using Android Studio wizard: File -> New -> Activity -> Bottom Navigation Activity.

MainActivity.kt

class MainActivity : AppCompatActivity() {    // optional    companion object {        const val PARAM_NAVIGATION_ID = "navigation_id"        fun newInstance(context: Context, navigationId: Int) =  Intent(context, MainActivity::class.java).apply {            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK            putExtra(PARAM_NAVIGATION_ID, navigationId)        }    }    private val onNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->        /*        when (item.itemId) {            R.id.navigation_home -> {                loadFragment(item.itemId)                message.setText(R.string.title_home)                return@OnNavigationItemSelectedListener true            }            R.id.navigation_dashboard -> {                message.setText(R.string.title_dashboard)                return@OnNavigationItemSelectedListener true            }            R.id.navigation_notifications -> {                message.setText(R.string.title_notifications)                return@OnNavigationItemSelectedListener true            }        }         */        loadFragment(item.itemId)        true    }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        navigation.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener)        // loadFragment(navigation.selectedItemId)        // loadFragment(R.id.navigation_dashboard)        val navigationId = intent.getIntExtra(PARAM_NAVIGATION_ID, R.id.navigation_dashboard)        navigation.selectedItemId = navigationId    }    private fun loadFragment(itemId: Int) {        val tag = itemId.toString()        var fragment = supportFragmentManager.findFragmentByTag(tag) ?: when (itemId) {            R.id.navigation_home -> {                TestFragment.newInstance()            }            R.id.navigation_dashboard -> {                Test2Fragment.newInstance()            }            R.id.navigation_notifications -> {                Test3Fragment.newInstance()            }            else -> {                null            }        }        // replace fragment        if (fragment != null) {            supportFragmentManager                .beginTransaction()                .replace(R.id.fragmentContainer, fragment, tag)                .commit()        }    }}

NOTE: The above example replace the fragment upon navigation, so UI state will be lost (e.g. when you edit EditText and didn't save/reload the value, the changes shall be lost). You probably need to implement your own onSaveInstanceState to save/restore UI state. The benefit of fragment replace is memory saving (as only one fragment is active at any one time).

You can use Android Switch Fragment Without Losing Ui State (Kotlin) example to replace loadFragment.

Replace code after // replace fragment with the following code

// show/hide fragmentif (fragment != null) {    val transaction = supportFragmentManager.beginTransaction()    if (viewModel.lastActiveFragmentTag != null) {        val lastFragment = supportFragmentManager.findFragmentByTag(viewModel.lastActiveFragmentTag)        if (lastFragment != null)            transaction.hide(lastFragment)    }    if (!fragment.isAdded) {        transaction.add(R.id.fragmentContainer, fragment, tag)    }    else {        transaction.show(fragment)    }    transaction.commit()    viewModel.lastActiveFragmentTag = tag}

NOTE: ViewModel is required to store lastActiveFragmentTag, else the state is lost upon configuration change/screen rotation.

res/layouts/activity_main.xml.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <FrameLayout
            android:id="@+id/fragmentContainer"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/navigation"
            android:layout_marginLeft="0dp"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            android:layout_marginRight="0dp"
            android:layout_marginBottom="0dp"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            >

    </FrameLayout>

    <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/navigation"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="0dp"
            android:layout_marginStart="0dp"
            android:background="?android:attr/windowBackground"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:menu="@menu/navigation">
    </com.google.android.material.bottomnavigation.BottomNavigationView>

</androidx.constraintlayout.widget.ConstraintLayout>

NOTE: In the default generated layout, BottomNavigationView will overlap/cover part of the main view. The ConstraintLayout setup above solve this issue.

NOTE: The generated activity used traditional Activity without AppBarLayout. If you are implementing AppBarLayout, refer to Android Prevent BottomNavigationView Cover/Overlap Content/RecyclerView.

res/menu/navigation.xml.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
            android:id="@+id/navigation_home"
            android:icon="@drawable/ic_home_black_24dp"
            android:title="@string/title_home"/>

    <item
            android:id="@+id/navigation_dashboard"
            android:icon="@drawable/ic_dashboard_black_24dp"
            android:title="@string/title_dashboard"/>

    <item
            android:id="@+id/navigation_notifications"
            android:icon="@drawable/ic_notifications_black_24dp"
            android:title="@string/title_notifications"/>

</menu>

TestFragment.kt.

class TestFragment : Fragment() {    companion object {        @JvmStatic        fun newInstance() =            TestFragment().apply {                arguments = Bundle().apply {                    // putString(ARG_PARAM1, param1)                }            }    }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        arguments?.let {            // param1 = it.getString(ARG_PARAM1)        }    }    override fun onCreateView(        inflater: LayoutInflater, container: ViewGroup?,        savedInstanceState: Bundle?    ): View? {        // Inflate the layout for this fragment        return inflater.inflate(R.layout.fragment_test, container, false)    }    override fun onActivityCreated(savedInstanceState: Bundle?) {        super.onActivityCreated(savedInstanceState)        // Initialize UI    }}

res/layouts/fragment_test.xml.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".view.TestFragment">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Test"
            android:id="@+id/textView"/>

</androidx.constraintlayout.widget.ConstraintLayout>

NOTE: Refer Android Setup BottomNavigationView With Jetpack Navigation UI (Kotlin).

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.