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

Viewed 854

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.

2 Answers

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)
    }
}

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")
    }
}
Related