Reputation: 2356
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
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
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
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