目次

05.ナビゲーションモジュールの作成

画面遷移において、各画面(機能)のモジュールと:appモジュールの依存関係を疎にするため、ナビゲーションモジュールを作成します。

Compose Navigationとkotlin-jvmの導入

ナビゲーション(画面遷移)はCompose Navigationを導入して行います。
また、ナビゲーションモジュールには、後述するLocalNavController:appモジュールから遷移するための遷移先NavGraph名だけを持たせますので、最小構成のbuild.gradle.ktsにします。

バージョンカタログにCompose Navigationと最小構成のbuild.gradle.ktsに必要なkotlinプラグインをバージョンカタログに追記します。

[versions]
   :
navigation = "2.9.5"                # https://mvnrepository.com/artifact/androidx.navigation/navigation-compose

[libraries]
   :
navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation" }
   
[plugins]
   :
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
   :

3行目
コメント部分のURLを参照して、最新安定バージョンを指定します。
7行目
Compose Navigationの依存ライブラリを定義しています。
11行目
kotlin.jvmのプラグインを定義しています。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

最上位build.gradle.ktskotlin.jvmの使用宣言を追記します。

plugins {
   :
  alias(libs.plugins.kotlin.jvm) apply false
   :
}

3行目
バージョンカタログファイルのkotlin.jvmプラグインを参照します。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

build.gradle.ktsの作成

ナビゲーションモジュールは、Android Studioモジュール作成ウィザードを使わずにモジュールを作成します。

/coreディレクトリ直下にnavigationディレクトリを追加し、最小構成のbuild.gradle.ktsを作成します。

plugins {
  alias(libs.plugins.kotlin.jvm)
}

java {
  toolchain {
    languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get().toInt()))
  }
}

dependencies {
  implementation(platform(libs.compose.bom))
  implementation(libs.compose.runtime)
  implementation(libs.navigation.compose)
}

3行目
kotlin.jvmのプラグインを使用します。
5行目~9行目
Javaの言語バージョンを宣言しています。
12行目~14行目
LocalNavControllerの定義に必要なライブラリを依存しています。

ビルド対象に追記

モジュール作成ウィザードを使わずにモジュールを作成したため、ビルド対象モジュールに手動で追記します。

   :
rootProject.name = "Portal"
include(":app")
   :
include(":core:navigation")

5行目
ビルド対象モジュールに追記します。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

LocalNavControllerの定義

モジュール分割におけるNavControllerの管理を容易にするため、LocalNavControllerを利用します。

LocalNavControllerを利用すると、各画面がNavControllerを引数で受け取る必要がなくなるため、後述するNavGraphDI(Hilt)に提供する方法との相性がよくなります。
※引数で受け取らないため、DI(Hilt)NavControllerを意識する必要がなくなります。

LocalNavControllerとは、UIツリー内に確保したNavHostControllerを格納するローカルな変数のことで、その変数にアクセスするキーをアプリケーションが取得します。
各画面はそのキーを使用してNavHostControllerを取得することになります。

アクセスするキーを保存するため、:core:navigation内にsrc/main/kotlinディレクトリとパッケージjp.co.progress_llc.portal.core.navigationディレクトリ(パッケージ)を追加し、直下にLocalNavController.ktを作成します。

package jp.co.progress_llc.portal.core.navigation

import androidx.compose.runtime.staticCompositionLocalOf
import androidx.navigation.NavHostController

val LocalNavController = staticCompositionLocalOf<NavHostController> {
  error("No NavController provided")
}

6行目~8行目
UIツリー内にNavHostControllerのローカルな変数を確保して、そのキーをLocalNavControllerに格納しています。

NavGraphNavHostへの登録は下記の手順で行います。

  1. 各画面は、NavGraphBuilderの機能を用いて自分自身のモジュール内NavGraphを作成します
  2. 作成したNavGraphDI(Hilt)に提供(provide)します
  3. アプリケーション起動時にDI(Hilt)からNavGraphを取得します
  4. 取得したNavGraphNavHostに一括登録します

これにより、:appモジュールはNavHost登録時に各機能モジュールのNavGraph関数名を知る必要がなくなります

:appモジュールは、各機能モジュールに遷移するためにNavGraph名を依存で知る必要はありますが、それ以外を知る必要はないため、各機能モジュールの独立性が担保され、開発やテストを各機能モジュール内に閉じて行うことができます。

ナビゲーションのコンテナNavHost:appモジュールに作成します。

:appモジュールにNavHostの作成に必要な依存を追記します。

   :
dependencies {
   :
  implementation(libs.navigation.compose)
  implementation(project(":core:navigation"))   :
   :
}

4行目
Compose Navigationライブラリへの依存を追記しています。
5行目
LocalNavControllerを使用するため追記します。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

:appモジュールにnavigationディレクトリ(パッケージ)を追加し、直下にAppNavHost.ktを作成します。

package jp.co.progress_llc.portal.navigation

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import jp.co.progress_llc.portal.core.navigation.LocalNavController

@Composable
fun AppNavHost(
  navGraphs: List<NavGraphBuilder.() -> Unit>,
  startDestination: String
) {
  val navController = rememberNavController()
  CompositionLocalProvider(LocalNavController provides navController) {
    NavHost(
      navController = navController,
      startDestination = startDestination
    ) {
      navGraphs.forEach { it() }
    }
  }
}

12行目
NavGraphBuilderで登録されたNavGraphの一覧を受け取ります。
各画面は自分でNavGraphBuilderを使ってNavGraphを登録します。
15行目~16行目
LocalNavControllerNavControllerを提供します。
スコープ内ではLocalNavControllerをキーとしてNavControllerにアクセスできます。
NavGraphNavControllerを引数として渡す必要がなくなります。
17行目~22行目
NavHostを定義しています。
21行目
NavGraphの一覧からNavGraphNavHostに登録しています。