13

I'm trying to set the toolbar title dynamically, I don't know if it's possible or not.

Assume I have list of items every item I clicked it's open new fragment, thus I trying to change toolbar title for each item dynamically.

I tried :

it.findNavController().navigate(direction)
it.findNavController().currentDestination!!.label = someTitle

But it doesn't work.

There are some related topics i.e:

How to set title in app bar with Navigation Architecture Component

But it doesn't solve my problem efficiently, It's a work-around.

Ibrahim Ali
  • 1,237
  • 9
  • 20
  • Does this answer your question? [Dynamic ActionBar title from a Fragment using AndroidX Navigation](https://stackoverflow.com/questions/50599238/dynamic-actionbar-title-from-a-fragment-using-androidx-navigation) – lcnicolau May 01 '20 at 06:11
  • @lcnicolau Yea it's – Ibrahim Ali May 02 '20 at 17:20

3 Answers3

28

Navigation supports arguments in labels as of Navigation 1.0.0-alpha08 or higher:

Destination labels, when used with NavigationUI methods, will now automatically replace {argName} instances in your android:label with the correct argument b/80267266

Therefore you can set your label to android:label="{dynamicTitle}", then pass in an argument to your navigate call. As you're using Safe Args, you'd want to add an argument to your destination:

<fragment
    android:id="@+id/myFragment"
    android:name=".MyFragment"
    android:label="{dynamicTitle}">
  <argument
      android:name="dynamicTitle"
      app:argType="string"/>
</fragment>

Then pass in your dynamic title when constructing your directions:

val directions = YourDirections.actionToMyFragment(someTitle)
it.findNavController().navigate(directions)

Of course, you can listen for navigation events yourself and use your own OnDestinationChangedListener to do whatever you want, including setting the label to whatever you want. There's no requirement to use NavigationUI and any listener to add after calling the NavigationUI methods will override whatever it sets.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • 3
    I found an issue with this approach, if the device is rotated (activity/views destroyed the title is reset (will become blank). Let me know if you are able to reproduce – Joaquim Ley Jun 14 '19 at 11:03
  • @JoaquimLey - did you file a bug with a sample project that reproduces your issue? – ianhanniballake Jun 14 '19 at 13:33
  • @rguerra - it most certainly is. If you're seeing differently, you should file an issue. – ianhanniballake Sep 11 '19 at 13:36
  • 1
    right. But if you use `destination.label` it returns {dynamicTitle} – rafaelasguerra Sep 11 '19 at 14:21
  • @rguerra - you wouldn't be using your own `OnDestinationChangedListener` approach if you're using destination labels - it is the NavigationUI helpers that parse the labels and update the title for you, no separate listener required. – ianhanniballake Sep 11 '19 at 14:28
  • @ianhanniballake right, but you lose flexibility if you have a custom toolbar. – rafaelasguerra Sep 11 '19 at 14:30
  • 1
    @rguerra - if you're not using NavigationUI, you'll need to do things manually, that is absolutely expected and has been the case for all versions of Navigation, both before and after this feature was introduced. Feel free to file a feature request if you'd like to see more of the helper functions in NavigationUI made public for use in your own custom implementation. – ianhanniballake Sep 11 '19 at 14:33
  • @ianhanniballake Can we set `defaultValue` from string resource? if I use `app:argType="reference"` it shows the resource id instead text! – Anoop M Maddasseri Nov 14 '20 at 07:58
  • @JoaquimLey The title will not reset upon rotation if you specify a toolbar title programmatically `supportActionBar?.title = getString(R.string.app_name)` or through xml in your toolbar layout: `app:title="@string/app_name"` – Thomas W. Apr 10 '21 at 17:59
3

In case you are passing a custom Object as parameter, you can use navController.addOnDestinationChangedListener.

navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
    @Override
    public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
        Log.i(TAG, "onDestinationChanged");
        switch (destination.getId()) {
            case R.id.mainFragment:
                updateToolbarAndBottomNavigation("Main Title", "Main Subtitle", View.VISIBLE);
                break;
            case R.id.shopFragment:
                updateToolbarAndBottomNavigation("custom title", null, View.VISIBLE);
                break;
            case R.id.shopcartFragment:
                StoreEntity store = (StoreEntity) arguments.get("storeEntity");
                Log.i(TAG, "onDestinationChanged: ShopCartFragment args: "+store.getName());
                updateToolbarAndBottomNavigation(store.getName(), null, View.GONE);
                break;
        }

    }
});

private void updateToolbarAndBottomNavigation(String title, String subtitle, int visibility) {
    getSupportActionBar().setTitle(title);
    getSupportActionBar().setSubtitle(subtitle);
    bottomNavigationView.setVisibility(visibility);
}

Where the arguments.get() was retrieved from android:name in the nav_graph.xml.

<fragment
        android:id="@+id/shopcartFragment"
        android:name="com.myapp.ShopcartFragment"
        tools:layout="@layout/fragment_shopcart" >
        <argument
            android:name="storeEntity" // GET ARGUMENTS NAME HERE
            app:argType="com.myapp.LocalDatabase.StoreEntity" />
    </fragment>

I hope it can help more people!

Aliton Oliveira
  • 1,224
  • 1
  • 15
  • 26
1

you can add a method for updating the fragment title and call it in fragment onStart() method

fun updateToolbarTitle(title: String) {
    supportActionBar?.title = title
}

and remove lable attribute from tag in your nav_graph.xml so it'll be like that

<fragment
    android:id="@+id/myFragment"
    android:name=".MyFragment"/>
MohamedHarmoush
  • 1,033
  • 11
  • 17