nayan dhabarde
nayan dhabarde

Reputation: 2356

How to change title for scaffold top bar when using popBackStack from jetpack compose navigation?

I am migrating my multiple activity app to single activity app for compose.

I have created a composable Home which contains a Top app bar with a title as shown below:


@Composable
fun Home() {
val navController = rememberNavController()
    var actionBarTitle by rememberSaveable { mutableStateOf("Home") }
    var actionBarSubtitle by rememberSaveable { mutableStateOf("") }
    Scaffold(topBar = {
        Header(title = actionBarTitle, subTitle = actionBarSubtitle,
                onBackPress = { navController.popBackStack() },
            showInfo = true, onActionClick = {
                navController.navigate(Screen.Info.route)
            }, modifier = Modifier.fillMaxWidth())
    }) {
        AppNavigation(navController = navController, onNavigate = { title, subtitle ->
                actionBarTitle = title
                actionBarSubtitle = subtitle
            })
}


onNavigate is triggered whenever I use navController.navigate for any screen as shown below:


onNavigate("Top up", "Please topm up with minimum of X amount")
navController.navigateTo(Screen.TopUp.route)

My question is when I use backpress I don't know to which screen composable I will be navigated to, so how can I call onNavigate to change the title.

Upvotes: 11

Views: 7542

Answers (3)

Scmile
Scmile

Reputation: 21

You can get the label of the current destination from navHostcontrollor, just use it as the title

    val navController = rememberNavController()
    val currentBackStackEntry by navController.currentBackStackEntryAsState()
    val title = currentBackStackEntry?.destination?.label

The default composable function is implemented as follows

/**
 * Add the [Composable] to the [NavGraphBuilder]
 *
 * @param route route for the destination
 * @param arguments list of arguments to associate with destination
 * @param deepLinks list of deep links to associate with the destinations
 * @param content composable for the destination
 */
public fun NavGraphBuilder.composable(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    content: @Composable (NavBackStackEntry) -> Unit
) {
    addDestination(
        ComposeNavigator.Destination(provider[ComposeNavigator::class], content).apply {
            this.route = route
            arguments.forEach { (argumentName, argument) ->
                addArgument(argumentName, argument)
            }
            deepLinks.forEach { deepLink ->
                addDeepLink(deepLink)
            }
        }
    )
}

overload it:

fun NavGraphBuilder.composable(
    route: String,
    label: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    content: @Composable (NavBackStackEntry) -> Unit
) {
    addDestination(
        ComposeNavigator.Destination(provider[ComposeNavigator::class], content).apply {
            this.route = route
            this.label = label
            arguments.forEach { (argumentName, argument) ->
                addArgument(argumentName, argument)
            }
            deepLinks.forEach { deepLink ->
                addDeepLink(deepLink)
            }
        }
    )
}

You can use it this way:

      composable("route", "title") {
        ...
      }

Upvotes: 0

Monica Patel
Monica Patel

Reputation: 241

1. Use LiveData to change the Screen Title while using Composable

implementation "androidx.compose.runtime:runtime-livedata:1.2.0-beta02"

2. Create ViewModel Class

class MainViewModel: ViewModel() {
    private var _screenTitle = MutableLiveData("")
    val screenTitle: LiveData<String>
        get() = _screenTitle

    fun setTitle(newTitle: String) {
        _screenTitle.value = newTitle
    }
}

3. In Your Activity Class

setContent {
        Surface(color = MaterialTheme.colors.onPrimary) {
            LoadMainScreen()
        }
    } 

// Observe ScreenTitle

@Composable
fun LoadMainScreen(mainViewModel: MainViewModel = viewModel()){

val title: String by mainViewModel.screenTitle.observeAsState("")

 Scaffold(
    topBar = {
        TopAppBar(title = { title?.let { Text(it) } },

          navigationIcon = {
          Icon(
                imageVector = Icons.Default.Menu,
                contentDescription = "Menu",
                tint = Color.White
              )
          }           
        )
      } 
    )
}  

4. Change the Title Value from Screen

@Composable
fun ScreenOne(mainViewModel: MainViewModel) {
    LaunchedEffect(Unit){
        mainViewModel.setTitle("One")
    }
}
@Composable
fun ScreenTwo(mainViewModel: MainViewModel) {
    LaunchedEffect(Unit){
        mainViewModel.setTitle("Two")
    }
}

Upvotes: 5

nglauber
nglauber

Reputation: 24044

You can observe the navigation changes using the currentBackstackEntryFlow.

@Composable
fun Home() {
    val context = LocalContext.current
    val navController = rememberNavController()
    ...
    LaunchedEffect(navController) {
        navController.currentBackStackEntryFlow.collect { backStackEntry ->
            // You can map the title based on the route using:
            actionBarTitle = getTitleByRoute(context, backStackEntry.destination.route)
        }
    }
    ...
}

Of course, you would need write this getTitleByRoute() to get the correct title in according to the navigation route. It would be something like:

fun getTitleByRoute(context: Context, route:String): String {
    return when (route) {
        "Screen1" -> context.getString(R.string.title_screen_1)
        // other cases
        else -> context.getString(R.string.title_home)
    }
}

Upvotes: 7

Related Questions