【 Jetpack Compose 】 で画面遷移を実装

## 今回実装する機能
観光地リストから観光地の詳細情報が載っている詳細画面に移動する機能です。

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747809/b903a4e7-997d-61f2-c99c-e12edd0ebf07.gif width=300>

XML は一切使わずに全てjetpack compose で画面も実装します。
## 必要なもの
今回はいくつかのプログラムファイルに分けて実装しています。

1. NavController の宣言
2. NavHost で遷移する画面の宣言
3. 引数のタイプをあらかじめ宣言
4. クリック時の処理に詳細画面のルートを指定して遷移するようにする

## プログラムの動作
今回のアプリはこのような動作をします。
mermaid により図を作成

```mermaid
flowchart TD

LandmarkJsonFile["観光地情報JSON ファイル読み込み"]
LandmarkList("観光地の件数分LandmarkListを表示")
Detail("クリックされた観光地の詳細情報表示")

LandmarkJsonFile --> LandmarkList --> |任意のリストセルをクリック|Detail

```
## プログラムの構成

アプリは以下のような構成で成り立っています。
mermaid により図を作成

```mermaid
classDiagram



MainActivity <|-- LandmarkViewModel
SetLandmarkView <|-- MainActivity
SetLandmarkView <|-- Screen
LandmarkList <|-- SetLandmarkView
ListItem <|-- LandmarkList
LandmarkDetail <|-- SetLandmarkView
LandmarkDetail <|-- ListItem

class LandmarkViewModel {
    + Int id
    + String name
    + String description
    + String airport
}

class SetLandmarkView {
 - LandmarkViewModel
 - navController
 - ScreenName
 + navHost(LandmarkList,LandmarkDetail)
 + LandmarkList(ScreenName)
}

class LandmarkList {
 - Int id
 - String name
 - String description
 - String airport
 ListItem()
}

class ListItem {
- Int id
- String name
- String description
Clickable(navController.navigate(LandmarkDetail))
}

class LandmarkDetail {
    - int id
    - String name
    - String description
    - String airport
}

class MainActivity {
+ LandmarkViewModel
 LandmarkList()
}

class Screen {
 + ScreenName
}

```

### Activity
```MainActivity.kt
// 今回はviewModelを使用します。
private val LandmarkViewModel: LandmarkViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            JetpackListTheme {
                Surface {
                    //navController を引数として view セットメソッドに渡します。
                    val navController = rememberNavController()
                    SetLandmarkView(LandmarkViewModel,navController = navController)
                }
            }
        }
    }

```

### 画面名 enum class

```screen.kt

// これは画面の名前を設定して遷移するために使用します
enum class Screen {
    LANDMARKS,
    LANDMARK_DETAIL
}
```

### リスト表示部

```LandmarkList.kt
@Composable
fun SetLandmarkView(landmarkViewModel: LandmarkViewModel,navController: NavHostController){
    // NavController から NavHost を設定します
    // startDestination で最初に表示する画面を決定します。
    NavHost(navController = navController, startDestination = Screen.LANDMARKS.name) {
        // 観光地リストを表示する画面
        // リストせるをクリックしたときに詳細画面への遷移のためにnavController を渡しておきます
        composable(Screen.LANDMARKS.name) {
            LandmarkList(viewModel = landmarkViewModel,navController)
        }
        // 詳細画面を表示する
        // 表示するための id を設定する
        composable("${Screen.LANDMARK_DETAIL.name}/{landmarkId}/{landmarkName}/{description}/{airport}",
        // 引数はここでどんなタイプのものを使用するのかを宣言する必要あり
            arguments = listOf(
            navArgument("landmarkId") {type = NavType.IntType},
        navArgument("landmarkName") {type = NavType.StringType},
            navArgument("description"){type = NavType.StringType},
            navArgument("airport"){type = NavType.StringType},
        )
        )
        { backStackEntry ->
            val landmarkId = backStackEntry.arguments?.getInt("landmarkId") ?: 0
            val landmarkName = backStackEntry.arguments?.getString("landmarkName") ?: ""
            val description = backStackEntry.arguments?.getString("description") ?: ""
            val airport = backStackEntry.arguments?.getString("airport") ?: ""
            LandmarkDetailView(
                landmarkId=landmarkId,
                landmarkName=landmarkName,
                description=description,
                airport=airport
            )
        }
    }
}
```

### リスト部品部

```LandmarkList.kt
 @Composable
    fun ListItem(landmark: LandmarkData,navController: NavController) {
        Column(modifier = Modifier
            .fillMaxWidth()
            // クリックしたときに画面遷移するように 詳細画面の id を指定する
            .clickable {
                navController.navigate("${Screen.LANDMARK_DETAIL.name}/${landmark.id}/${landmark.name}/${landmark.description}/${landmark.airport}")
            }
            //...
    }
```

## 引数の関係
NavHost にて定義した引数は以下のように対応します。
### 観光地リストを表示する composable 関数
```LandmarkList.kt

NavHost(navController = navController, startDestination = Screen.LANDMARKS.name){
    composable(Screen.LANDMARKS.name) {
            LandmarkList(viewModel = landmarkViewModel,navController)
        }
}
```

```LandmarkList.kt
@Composable
fun LandmarkList(viewModel: LandmarkViewModel,navController: NavController)
```

### 観光地詳細情報を表示する composable 関数
```LandmarkList.kt

composable("${Screen.LANDMARK_DETAIL.name}/{landmarkId}/{landmarkName}/{description}/{airport}",
            arguments = listOf(
            navArgument("landmarkId") {type = NavType.IntType},
        navArgument("landmarkName") {type = NavType.StringType},
            navArgument("description"){type = NavType.StringType},
            navArgument("airport"){type = NavType.StringType},
        )
    )
```
```LandmarkList.kt
@Composable
fun LandmarkDetailView(landmarkId: Int,landmarkName: String,description:String,airport:String)
```

このようにすれば、 Jetpack Compose で実装した画面間を移動できるはずです。