◀️
[Jetpack Compose] NavigationBar のタブ選択中にタブをタップしたときにタブの開始画面に遷移する
はじめに
前回の記事 の発展で、タイトルの動きを実装します。
コード
class BarItem(
val label: String,
val icon: ImageVector,
val route: String,
)
@Composable
fun MyAppNavigationBar(
navController: NavController,
) {
val backStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = backStackEntry?.destination
val barItems = listOf(
BarItem("Home", Icons.Outlined.Home, Route.Home.route),
BarItem("Books", Icons.Outlined.List, Route.Books.route),
BarItem("Settings", Icons.Outlined.Settings, Route.Settings.route)
)
NavigationBar {
barItems.forEach { item ->
val selected = currentDestination?.hierarchy?.any { it.route == item.route } == true
NavigationBarItem(
selected = selected,
onClick = { onBarItemClick(item, currentDestination, navController) },
label = { Text(text = item.label) },
icon = { Icon(item.icon, contentDescription = null) }
)
}
}
}
/**
* NavigationBarItem.onClick に与えるコールバック
*/
fun onBarItemClick(item: BarItem, currentDestination: NavDestination?, navController: NavController) {
val tabStartDestination = currentDestination?.parent?.startDestinationRoute
if (
currentDestination != null
&& currentDestination.hierarchy.any { it.route == item.route }
&& currentDestination.parent != navController.graph
&& tabStartDestination != null
&& currentDestination.route != tabStartDestination
) {
// タブの開始画面まで戻る
Log.d("App", "popup")
val result = navController.popBackStack(
route = tabStartDestination,
inclusive = false,
saveState = false,
)
// 戻ることに失敗した場合(直接別タブから詳細ページに飛んだため、
// タブの開始画面がバックスタックにない場合など)はタブの開始画面に遷移する
if (!result) {
navController.navigate(tabStartDestination) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
} else {
Log.d("App", "switch tab")
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
}
解説
NavDestination.parent で 親の NavGraph? 、 NavGraph.startDestinationRoute でそのグラフの開始画面に設定した NavDestination.route が取得できるので、
// タブ選択状態で
currentDestination.hierarchy.any { it.route == item.route }
// 現在の画面がネストされたグラフ(親がルートではない)であり
currentDestination.parent != navController.graph
// 現在の画面がタブの開始画面ではない場合
currentDestination.route != tabStartDestination
// タブの開始画面まで戻る
という感じで書けます。
終わりに
もっとシンプルに書く方法があれば教えてください!!!
Discussion