2023/07/01
composeのサンプルコードを触って、ナビゲーションボトムバーをどう実装するかみた。
@Composable fun PlayGroundScreen() { val navController = rememberNavController() Scaffold( bottomBar = { BottomNavigation( backgroundColor = Color.LightGray, ) { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination items.forEach { screen -> BottomNavigationItem( icon = { Icon(screen.iconImage, contentDescription = null) }, label = { Text(screen.route) }, selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, onClick = { navController.navigate(screen.route) { // navigateを実行するとバックスタックにdestinationの情報がどんどん積まれのを避けるためpopup popUpTo(navController.graph.findStartDestination().id) { saveState = true } // 同じdestinationが再度選択されたとき、インスタンスを再利用する。 launchSingleTop = true // 同じdestinationが再度選択されたとき、状態をレストアする。 restoreState = true } } ) } } }, content = { innerPadding -> NavHost( navController = navController, startDestination = Screen.Profile.route, modifier = Modifier.padding(innerPadding), ) { composable(Screen.Profile.route) { Profile(navController) } composable(Screen.FriendsList.route) { Favotite(navController) } } } ) }
rememberNavController()
でNavControllerを取得する。NavControllerは各destinationのbackstackやnavigation graphを持っているので、それをcompositionを超えて保存しておきたいからrememberしてる感じ。
画面が破棄されたときはどうするんだろうと思ったけど、中でrememberSaveableを使っているので大丈夫そう。
@Composable public fun rememberNavController( vararg navigators: Navigator<out NavDestination> ): NavHostController { val context = LocalContext.current return rememberSaveable(inputs = navigators, saver = NavControllerSaver(context)) { createNavController(context) }.apply { for (navigator in navigators) { navigatorProvider.addNavigator(navigator) } } }
popupしているところは少し複雑かも。
navigateが実行されるたびにバックスタックにdestinationが積まれてしまう。そうすると、例えば戻るボタンをタップしても(backstackに積まれた分だけ)同じ画面が表示され続ける(多分)。UXとしては最悪そうなのでちゃんとpopupしておく。
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
すでにbackstackのtopに積まれているdestinationと同じところへnavigateするとき、新たにbackstackに積むのは無駄なのでインスタンスを再利用する。つまり何もしないことっぽい(多分)。
launchSingleTop = true
これも同じところにnavigateするとき、stateが変わっちゃう(UIがリセットされる?)のを防ぐために設定するっぽい(多分)。
restoreState = true
あとはScaffoldのcontentにある通り、遷移先で画面に描画したいComposable関数を呼んであげる。
NavHost( navController = navController, startDestination = Screen.Profile.route, modifier = Modifier.padding(innerPadding), ) { composable(Screen.Profile.route) { Profile(navController) } composable(Screen.FriendsList.route) { Favotite(navController) } }
SwiftUIの方がシンプルにかける感じはした。