目次

08.共有NavGraphの作成

共有ViewModelを各画面で共有するには、共有NavGraphを作成して、そのNavGraph内で画面を遷移させます。
同じNavGraph内で画面を遷移させることにより、共有ViewModelのスコープも同じになります。

共有NavGraphは親となるモジュール内に作成しますが、その際に子となるモジュールのNavGraphが必要になるため、子となるモジュールを依存します。
一方で、子となるモジュールでは共有ViewModelの実体を取得するため、共有NavGraph名を必要とします。

共有NavGraph名を親となるモジュールで定義すると、子となるモジュールから親となるモジュールを依存しますので循環依存になります。

循環依存を回避するため、別途ナビゲーションモジュールを作成し、共有NavGraph名だけをナビゲーションモジュールに持たせることにします。

ナビゲーションモジュールは、共有NavGraph名だけのモジュールですので、Android Studioモジュール作成ウィザードを使わずにモジュールを作成します。

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

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

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

dependencies {
}

3行目
kotlin.jvmのプラグインを使用します。
5行目~9行目
Javaの言語バージョンを宣言しています。

ビルド対象に追記

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

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

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

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

共有NavGraph名の定義

:feature:depart:navigationモジュールにsrc/main/kotlinディレクトリを追加し、さらにjp.co.progress_llc.portal.feature.depart.navigationパッケージ(ディレクトリ)を作成します。

パッケージ内にNavGraph名を定義するDepartNavGraphName.ktを作成します。

package jp.co.progress_llc.portal.feature.depart.navigation

object DepartNavGraphName {
  const val DEPART_ROUTE_EDIT = "depart_route_edit"
}

3行目
共有NavGraph名を定義しています。

依存の追記

共有NavGraphの作成で必要な依存ライブラリを親モジュール:feature:depart:routebuild.gradle.ktsに追記します。

   :
dependencies {
   :
  implementation(project(":feature:depart:navigation"))
   :
}

4行目
共有NavGraphの名称を使用するのでナビゲーションモジュールへの依存を追記します。

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

共有NavGrapの作成

共有するViewModelを持つ画面のNavGraphをまとめたDepartRouteEditNavGraph.ktを、親となる:feature:depart:routeモジュールのnavigationディレクトリ(パッケージ)に作成します。

package jp.co.progress_llc.portal.feature.depart.route.navigation

import androidx.navigation.NavGraphBuilder
import androidx.navigation.navigation
import jp.co.progress_llc.portal.feature.depart.navigation.DepartNavGraphName

fun NavGraphBuilder.departRouteEditNavGraph() {
  navigation(
    startDestination = DEPART_ROUTE,
    route = DepartNavGraphName.DEPART_ROUTE_EDIT
  ) {
    departRouteNavGraph()
  }
}

9行目
共有NavGraphに遷移したときに表示するNavGraphを定義しています。
10行目
共有NavGraphrouteの名前で登録しています。
12行目
共有する対象のNavGraphを定義しています。
現時点では親のNavGraphしかありません。

共有NavGraphをDI(Hilt)に提供

:feature:depart:routeモジュールのdiディレクトリ(パッケージ)直下にDepartRouteEditNavGraphModule.ktを作成し、共有NavGraphDI(Hilt)に提供します。

package jp.co.progress_llc.portal.feature.depart.route.di

import androidx.navigation.NavGraphBuilder
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoSet
import jp.co.progress_llc.portal.feature.depart.route.navigation.departRouteEditNavGraph

@Module
@InstallIn(SingletonComponent::class)
object DepartRouteEditNavGraphModule {
  @Provides
  @IntoSet
  fun provideDepartEditRouteNavGraph(): NavGraphBuilder.() -> Unit = {
    departRouteEditNavGraph()
  }
}

16行目~18行目
共有NavGraphを提供しています。

経路一覧編集画面の表示で作成したNavGraphDI(Hilt)モジュールDepartRouteNavGraphModule.ktは、共有NavGraphDI(Hilt)登録でネストされて登録されますので削除します。

経路一覧編集画面を隠蔽

外部モジュールが経路一覧編集画面に直接遷移すると、共有ViewModelのスコープを正しく管理できなくなるため、外部モジュールに対して経路一覧編集画面のNavGraphを隠蔽して、経路一覧編集画面に直接遷移できないようにします。

   :
const val DEPART_ROUTE = "depart_route"
internal const val DEPART_ROUTE = "depart_route"
   :

2行目~3行目
DEPART_ROUTEinternalを付けて他のモジュールから参照できないようにします。

ホーム画面からの遷移先を変更

発車予定表示機能のホーム画面は、経路一覧編集画面に直接遷移していますので、共有NavGraphに遷移するように変更します。

共有NavGraphの名前を管理しているモジュールを:feature:depart:homeの依存に追記します。

   :
dependencies {
   :
implementation(project(":feature:depart:navigation"))
}

4行目
共有NavGraphの名前を管理しているモジュールを依存ライブラリに追記します。

発車予定表示機能のホーム画面の遷移先を共有NavGraphに変更します。

   :
import jp.co.progress_llc.portal.feature.depart.route.navigation.DEPART_ROUTE
import jp.co.progress_llc.portal.feature.depart.navigation.DepartNavGraphName
   :
fun DepartHomeScreen() {
   :
      CustomExposedDropdownMenuBox(
   :
        onAdditionalItemClick = { navController.navigate(DEPART_ROUTE) }
        onAdditionalItemClick = { navController.navigate(DepartNavGraphName.DEPART_ROUTE_EDIT) }
   :

2行目~3行目、9行目~10行目
遷移先を共有NavGpathに変更します。