Jetpack Composeでの画面遷移(Navigation)の基本とベストプラクティス
はじめに
前回Androidアプリ開発におけるNavigationを構成する3つの要素
- NavGraph
- NavController
- NavHost
について簡単に概念をまとめました。今回はそれの引き続き、詳しくコードをもとにまとめてみた記事です。
1. NavHostの画面マッピングについて
- 画面マッピングに関して、NavHostの中で行われます。
NavHostにはnavController(状態管理⇒どのページに飛ぶべきか)とstartDestination(初期画面)が引数として渡されます。
//マッピング操作サンプルコード
@Composable
fun AppNavigation() {
// NavControllerの生成
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home" // 最初に表示する画面のルート名
) {
// ルート "home" に HomeScreen を紐づける
composable("home") {
HomeScreen(
onNextButtonClicked = {
navController.navigate("detail")
}
)
}
// ルート "detail" に DetailScreen を紐づける
composable("detail") {
DetailScreen(
onBackButtonClicked = {
navController.popBackStack() // 戻る操作
}
)
}
}
}
画面の数だけcomposableを付け加えるのみ
2. 画面遷移の具体的な操作方法
- 実際に画面遷移するときは
navController.navigate(route)を使う - ボタン押下時のイベントコールバックとして渡すのが基本
- 例:
onNextButtonClicked = { navController.navigate("detail") }
@Composable
fun HomeScreen(onNextButtonClicked: () -> Unit) {
Button(onClick = onNextButtonClicked) {
Text(text = "次の画面へ")
}
}
@Composable
fun DetailScreen(onBackButtonClicked: () -> Unit) {
Button(onClick = onBackButtonClicked) {
Text(text = "戻る")
}
}
この様に、NavHost内で定義したonNextButtonClickedやonBackButtonClickedを画面ごとに引数として渡して、仮に画面内でボタンが押されたときに実行されます。
3. バックスタックとパラメータ付き遷移
- 画面遷移の履歴(バックスタック)は
popBackStack()で戻る
画面をもどるときはデータ構造のスタック(Stack)と同様の動き方をします。
例えば初期ホーム画面Aを開いていたとします。
次にB⇒Cと開いて、戻るボタンをその時押したときに、現在の画面Cの前はB画面であるため、戻るとB画面になります。(あたりまえですよね)
navController.popBackStack("home", inclusive = false)
このコードのようにバックスタックが実装されますが、引数として2つ渡されます。
"home"は戻る先のルート
inclusiveはBool値です
このBool値がTrueの時に、指定したルートもスタックから除外します。
navController.popBackStack("home", inclusive = false)
// "home"画面まで戻るが、"home"画面は残る
navController.popBackStack("home", inclusive = true)
// "home"画面もスタックから削除する(さらに1画面戻る)
4. インテントの役割
- IntentはOSが提供する「別アプリやシステムUIを呼び出す仕組み」
- 共有画面などはIntent +
startActivity()で呼び出す
例えば占いサイトで自身を占ったら大吉だった!
この結果をみんなに共有したい!
こんな時に活用できます。
内部では
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, "大吉が出た!")
}
context.startActivity(Intent.createChooser(intent, "共有するアプリを選択"))
このようにコードが書かれます。
これにより、OS側が
LINEとかInstagramとか共有機能がついてるアプリあるよ!
と勝手に探してきてくれます。
5. NavControllerの渡し方と設計のベストプラクティス
アプリが成長してくると、遷移先が増えたり画面が複雑になったりします。
そのときに柔軟に対応できるよう、画面とナビゲーションロジックは分離するのが望ましいです。
実際navControllerを全画面に渡していけば画面遷移はできるのですが、これでは柔軟性に欠けます。
//悪い例
@Composable
fun HomeScreen(navController: NavController) {
Button(onClick = { navController.navigate("next") }) {
Text("次へ")
}
}
👆このコードのようにHomeScreenが次に画面遷移することを知る必要はないのです。
//良い例
@Composable
fun HomeScreen(onNextClicked: () -> Unit) {
Button(onClick = onNextClicked) {
Text("次へ")
}
}
👆このコードではHomeScreenはボタンがいつ押されたのかの情報しか知りません。
ボタンが押されたことによりどうなるのか(どこに画面遷移するのか)はNavHostのみ知ればいいのです。
こうすることで、保守性・拡張性・テスト性が向上する(そうです...)
最後に
今回初学者なりにまとめてみましたが大枠は理解できたと思います。しかし、まだ0からコードを書けと言われると厳しいものがあります。
今後も学習を進めていきたいと思います。
また、今回の記事は以下の公式ドキュメントを参考にしました。