Dynamic ActionBar title from a Fragment using AndroidX Navigation

AndroidAndroid JetpackAndroid Architecture-Navigation

Android Problem Overview


I am using the new Navigation component from Android Jetpack.

The root Activity setup is quite simple:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)
    
    val navController = findNavController(R.id.navigationFragment)
    setupActionBarWithNavController(navController)

    bottomNavigationView.setupWithNavController(navController)
}

It works well when the Fragment's title is defined in the navigation graph. But for one Fragment, I want to set the title dynamically.

I tried with findNavController().currentDestination.label = "Hello world" but it does nothing.

I could of course use a trick like (activity as? AppCompatActivity)?.supportActionBar?.title = "Hello world", but I feel that it will break the magic that setupActionBarWithNavController() does for me. It there any way to update the Action Bar title dynamically?

Android Solutions


Solution 1 - Android

As of 1.0.0-alpha08, you can have the NavigationUI bits dynamically set the title... if the dynamic bits are arguments on the navigation action.

So, for example, in your navigation graph, you could have something like this:

  <fragment
    android:id="@+id/displayFragment"
    android:name="com.commonsware.jetpack.sampler.nav.DisplayFragment"
    android:label="Title: {title}" >
    <argument
      android:name="modelId"
      app:argType="string" />
    <argument
      android:name="title"
      app:argType="string" />
  </fragment>

Here, the android:label attribute for our <fragment> has an argument name wrapped in braces ({title} in "Title: {title}". The app bar's title will then be set to the value of the label, with {title} replaced by the value of the title argument.

If you need something more elaborate than that — for example, you want to look up the model by ID and read a property from it — you will need to use more manual approaches, such as those outlined in other answers to this question.

Solution 2 - Android

The title can be changed in the fragment by casting the activity as AppCompatActivity.

Kotlin

(requireActivity() as AppCompatActivity).supportActionBar?.title = "Hello"

Java

((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle("Hello");

Solution 3 - Android

Taking consideration that your host activity is MainActivity, just add the following code to your MainActivity's onCreate fun

val navController = Navigation.findNavController(this, R.id.nav_host_fragment)

// setting title according to fragment
navController.addOnDestinationChangedListener { 
    controller, destination, arguments ->
        toolbar.title = navController.currentDestination?.label
}

Solution 4 - Android

If you use toolbar on setSupportActionBar in Activity and you would like to change its title in fragment, then below code may gonna help you ;)

(requireActivity() as MainActivity).toolbar.title = "Title here"

Solution 5 - Android

As of now, The Jetpack Navigation Architecture components do not provide any "built in" way to do this, and you'll have to implement your own "custom" method for doing it.

There is an existing feature request to get functionality for dynamic labels on destinations added to the new Jetpack Navigation Architecture Components. If you are here because you want/need this functionality, please star the existing feature request, here: https://issuetracker.google.com/issues/80267266

Solution 6 - Android

Remove label from graph.xml file

android:label="fragment_info"

and use old school approach if you want to set title of the fragment dynamically from the fragment itself

getActivity().setTitle("Your Title");

Solution 7 - Android

Well, now the Navigation UI supports this feature. Now the ActionBar title changes dynamically. You just have to setup the ActionBar with the NavController.

private lateinit var appBarConfiguration: AppBarConfiguration

private lateinit var navController: NavController

override fun onCreate(savedInstanceState: Bundle?) {
    preferedTheme()
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)
    navController = findNavController(R.id.nav_controller_fragment)
    appBarConfiguration = AppBarConfiguration(navController.graph)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

And set action bar label in nav graph:

<navigation 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/mobile_navigation"
        app:startDestination="@id/mainFragment">

<fragment android:id="@+id/mainFragment"
          android:name="com.cinderellaman.general.ui.fragments.MainFragment"
          android:label="General"
          tools:layout="@layout/main_fragment"/>

And now its also support Navigate Up:

override fun onSupportNavigateUp(): Boolean {
    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

Solution 8 - Android

Another solution is to use ViewModel and LiveData, attach viewmodel to your activity and fragments, add a livedata field inside viewmodel

val title = MutableLiveData<String>()

From your activity observe this field, and if it is changed update the toolbar title

viewModel?.title?.observe(this, Observer { 
        my_toolbar.title=it
    })

From your desired fragment change the title field inside the viewmodel

viewModel?.title?.value="New title"

Solution 9 - Android

Until the issue will be fixed, simple listener is working to me:

/**
 * Temporary solution to dynamically change title of actionbar controlled by Navigation component
 * Should be removed as soon as the bug on Navigation will be fixed: (https://issuetracker.google.com/issues/80267266)
 */
interface TempToolbarTitleListener {
    fun updateTitle(title: String)
}

class MainActivity : AppCompatActivity(), TempToolbarTitleListener {
    
    ...
    
    override fun updateTitle(title: String) {
        binding.toolbar.title = title
    }
}

change title from fragment:

(activity as TempToolbarTitleListener).updateTitle("custom title")

Solution 10 - Android

You can add addOnNavigatedListener inside your activity, and based on current destination change the title

 findNavController(nav_host_fragment).addOnNavigatedListener { controller, destination ->
        when(destination.id) {
            R.id.destination1 -> {
                my_toolbar.title= "Some title"
            }
            R.id.destination2 -> {
                my_toolbar.title= "Othertitle"
               
            }
           
    }
}

Solution 11 - Android

If your title is received from a concatenated string, for example:

ActionBar actionBar = ((AppCompatActivity) 
  requireActivity()).getSupportActionBar();
  
if(actionBar!=null)                   
   actionBar.setTitle(count + "items"); 

Solution 12 - Android

As per the below solutions, there is no need to access the toolbar manually.

If you are going to pass the Model into the destinations fragment. Then you can do as below.

Override toString() method in your model class

data class UserModel(
    val userId: String = ""
    val firstName: String = "",
    val lastName: String = ""  
) {

    override fun toString(): String {
        return "$firstName $lastName"
    }
}

Now, in the nav_grap.xml file, do as below. android:label="{UserData}" will be fetch label from toString() method of model class.

<fragment
    android:id="@+id/messagesFragment"
    android:name="com.app.mydemoapp.ui.messages.MessagesFragment"
    android:label="{UserData}"
    tools:layout="@layout/fragment_messages">

    <argument
        android:name="UserData"
        app:argType="com.app.mydemoapp.model.UserModel" />

</fragment>

I hope, this will be helpful.

Solution 13 - Android

Based on @kaustubh-trivedi answer, and if you are using MVVM (like the Android Studio FirstFragment/SecondFragment example):

KOTLIN version

val navController = Navigation.findNavController(this, R.id.nav_host_fragment)

// setting title according to fragment
navController.addOnDestinationChangedListener { 
    controller, destination, arguments ->
        toolbar.title = navController.currentDestination?.label
}

JAVA version

        navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
                binding.toolbar.setTitle(destination.getLabel());
            }
        });

Solution 14 - Android

On trying the activity's title it seems to override the title for fragment. Being on safe side you must put on onResume.

override fun onResume() {
    super.onResume()
    activity?.toolbar.title = "YOUR_TITLE_HERE"
}

it works for me !

Note : Must have Toolbar Widget in activity

Add toolbar like this in your activity's xml

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

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </com.google.android.material.appbar.AppBarLayout>

    <!-- Other Widgets -->

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Solution 15 - Android

you can remove android:label in navigation graph then write in onCreateView()

activity?.title="your title"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJonas SchmidView Question on Stackoverflow
Solution 1 - AndroidCommonsWareView Answer on Stackoverflow
Solution 2 - AndroidPatrioticView Answer on Stackoverflow
Solution 3 - AndroidKaustubh TrivediView Answer on Stackoverflow
Solution 4 - AndroidErcanView Answer on Stackoverflow
Solution 5 - AndroidBradleycornView Answer on Stackoverflow
Solution 6 - AndroidNaveen RaoView Answer on Stackoverflow
Solution 7 - AndroidHussnain HaidarView Answer on Stackoverflow
Solution 8 - AndroidAlexView Answer on Stackoverflow
Solution 9 - AndroidFrancisView Answer on Stackoverflow
Solution 10 - AndroidAlexView Answer on Stackoverflow
Solution 11 - AndroidAyiaView Answer on Stackoverflow
Solution 12 - AndroidParth PatelView Answer on Stackoverflow
Solution 13 - AndroiddanielroseroView Answer on Stackoverflow
Solution 14 - AndroidDilroop SinghView Answer on Stackoverflow
Solution 15 - AndroidAmina BekirView Answer on Stackoverflow