88

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?

16 Answers 16

164

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.

17
  • 15
    This solution is not working in navigation version 2.1.0-alpha02. It directly printing the Title: {title} as action bar title. Commented Apr 24, 2019 at 16:01
  • @RohitMaurya: If there is no bug report for this, and you can create a project that reproduces the problem, file a bug report. Commented Apr 24, 2019 at 16:06
  • 4
    To add; value binding seems to be sensitive to additional whitespaces in an argument wrapped with braces. I noticed that { title } would cause my app to crash where as {title} worked as expected. Just a minor gotcha incase it catches anyone else out. Commented Jan 3, 2020 at 0:59
  • 1
    @DacreDenny: Nice find! I reproduced that one easily enough and filed an issue. Commented Jan 3, 2020 at 1:13
  • 1
    What if I want to combine a string argument with a string resource? Is that possible? Commented May 15, 2020 at 5:33
44

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");
6
  • This is the only solution thus far that works with programmatically changing the Navigation UI's action bar/app bar after the label is defined in the Navigation Graph. Commented Dec 13, 2020 at 2:38
  • Simple and effective. Thanks :D Commented May 2, 2021 at 2:56
  • Just noticed that Android Studio returns back a warning saying : "Method invocation 'getSupportActionBar' may produce 'NullPointerException'". I was wondering is there any way to resolve this other than putting (if/else) ? Commented Jun 27, 2021 at 16:47
  • Doesn't work because fragment references FragmentActivity not AppCompatActivity
    – JPM
    Commented Nov 1, 2021 at 22:36
  • @JPM it doesn't matter. Fragment references the parent activity. So this works as long as your parent activity is AppCompatActivity. Commented Apr 26, 2022 at 14:43
19

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
}
3
  • 1
    Agree with this answer if you only have one activity. Simply set label in nav_main.xml
    – Yuan Fu
    Commented Jan 28, 2019 at 0:09
  • The title must come from the fragment and the code should be in the fragment itself. The activity should not have to know about every possible fragment. That's the reason we move from the activity implementing interfaces to support their fragments to Jetpack navigation and ViewModels. Commented Feb 21, 2020 at 7:22
  • The navigation destination is already given as an argument, so use it: toolbar.title = destination.label or supportActionBar?.title = destination.label
    – Pnemonic
    Commented Jun 4, 2021 at 7:54
11

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"
9

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

2
  • I see that the issue is fixed but still I can't the proper way. Anyone knows how to do it? Commented Feb 3, 2019 at 9:11
  • 1
    @MehulKanzariya: See this answer that I just added. Commented Feb 3, 2019 at 15:21
8

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");
1
  • This does not work if you need to make decisions with a navigationListener based on the navigation id, which happens to be the label defined in the nav graph. Commented Jun 8, 2020 at 22:20
4

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"
4
  • That could me a solution. However with the navigation component I have way more than one Fragment in the Activity. How would I now in the activity which Fragment is visible and how to react to it. It seems cumbersome. Commented Jun 7, 2018 at 11:16
  • You can use multiple fragments in activity, you changing the title from the fragment, it doesn't matter which fragment it is, just change the viewmodel field title to something else, and activity will know about the change in title value, and it will update the toolbar title accordingly
    – Alex
    Commented Jun 7, 2018 at 12:26
  • With the Navigation component that itself changes the ActionBar title, it does not really seem a good idea. Commented Jun 7, 2018 at 13:15
  • 2
    I like this solution, since I can't determine the fragment label when passing the arguments I decided to create base fragment class that changes toolbar title through live data in model and all the subclasses just implement abstract property to provide the title. And I removed all the labels in nav graph, so the Navigation component doesn't interfere. It's not super elegant but I guess it's not so ugly either.
    – Almighty
    Commented Dec 21, 2018 at 10:23
4

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()
}
1
  • You might want to pimp your code a bit by using extension functions which are available in the navigation-ui-ktx artifact, e.g. BottomNavigationView#setupWithNavController, AppCompatActivity#setupActionBarWithNavController and NavController#navigateUp.
    – JJD
    Commented Jul 25, 2019 at 9:45
2

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")
2

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());
            }
        });
1

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"

            }

    }
}
1
  • 8
    Thanks, but I don't want my Activity to know about the Fragments' titles. They should be self-contained. Commented Jun 4, 2018 at 7:16
1

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

ActionBar actionBar = ((AppCompatActivity) 
  requireActivity()).getSupportActionBar();
  
if(actionBar!=null)                   
   actionBar.setTitle(count + "items"); 
3
  • What if actionBar is null? I mean it is possible to be null? Commented Jun 27, 2021 at 16:48
  • It's like asking what if a reference to a text view that you did not include in the XML is null.
    – Ayia
    Commented Jun 28, 2021 at 15:05
  • Anyway, you can surround getSupportActionBar() with a null check just to be safe.
    – Ayia
    Commented Jun 28, 2021 at 15:06
1

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.

0

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>
0

Just use this code inside yourfragment.java if you are using navigation components , and you want to change the label of your fragment programmatically in JAVA

  Navigation.findNavController(view).addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
        @Override
        public void onDestinationChanged(@NonNull NavController navController, @NonNull NavDestination navDestination, @Nullable Bundle bundle) {

           navController.findDestination(R.id.YOUR_FRAGMENT_ID_HERE).setLabel(YOUR_LABEL_HERE);

        }
    });
1
  • does this overrides what we have defined using addOnDestinationChangedListener on the main activity?
    – C.F.G
    Commented Jul 20, 2023 at 3:17
-2

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

activity?.title="your title"
1
  • In case someone wonders why the negative votes: this solution is far from valid because the question already specifies that the activity is using a toolbar with setupActionBarWithNavController(), therefore, using activity?.title won't ever work because it's being overriden by the toolbar's title. Commented Jan 6, 2023 at 1:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.