共有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 {
}
モジュール作成ウィザードを使わずにモジュールを作成したため、ビルド対象モジュールに手動で追記します。
:
rootProject.name = "Portal"
include(":app")
:
include(":feature:depart:navigation")
追記後、『Sync Now』で内容をプロジェクトに反映させます。
: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"
}
共有NavGraphの作成で必要な依存ライブラリを親モジュール:feature:depart:routeのbuild.gradle.ktsに追記します。
:
dependencies {
:
implementation(project(":feature:depart:navigation"))
:
}
追記後、『Sync Now』で内容をプロジェクトに反映させます。
共有する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()
}
}
:feature:depart:routeモジュールのdiディレクトリ(パッケージ)直下にDepartRouteEditNavGraphModule.ktを作成し、共有NavGraphをDI(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()
}
}
経路一覧編集画面の表示で作成したNavGraphのDI(Hilt)モジュールDepartRouteNavGraphModule.ktは、共有NavGraphのDI(Hilt)登録でネストされて登録されますので削除します。
外部モジュールが経路一覧編集画面に直接遷移すると、共有ViewModelのスコープを正しく管理できなくなるため、外部モジュールに対して経路一覧編集画面のNavGraphを隠蔽して、経路一覧編集画面に直接遷移できないようにします。
: const val DEPART_ROUTE = "depart_route" internal const val DEPART_ROUTE = "depart_route" :
発車予定表示機能のホーム画面は、経路一覧編集画面に直接遷移していますので、共有NavGraphに遷移するように変更します。
共有NavGraphの名前を管理しているモジュールを:feature:depart:homeの依存に追記します。
:
dependencies {
:
implementation(project(":feature:depart:navigation"))
}
発車予定表示機能のホーム画面の遷移先を共有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) }
: