封裝導覽程式碼

使用 Kotlin DSL 建構圖表時,將目的地和導覽事件保留在單一檔案中可能並不容易。如果您有多項獨立的功能,這一點尤其重要。

擷取目的地

您應將目的地移至 NavGraphBuilder 擴充功能函式。它們應位於靠近定義這些路線的路徑,以及顯示的畫面。舉例來說,假設以下應用程式層級程式碼會建立顯示聯絡人清單的目的地:

// MyApp.kt

@Serializable
object Contacts

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
    composable<Contacts> { ContactsScreen( /* ... */ ) }
  }
}

您應將導覽專用的程式碼移到個別檔案中:

// ContactsNavigation.kt

@Serializable
object Contacts

fun NavGraphBuilder.contactsDestination() {
    composable<Contacts> { ContactsScreen( /* ... */ ) }
}

// MyApp.kt

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
     contactsDestination()
  }
}

路徑和目的地定義現在與主要應用程式各自獨立,您可以獨立更新這些定義。主應用程式只依賴單一擴充功能函式。在本例中為 NavGraphBuilder.contactsDestination()

NavGraphBuilder 擴充功能函式構成無狀態畫面層級可組合函式和 Navigation 專屬邏輯之間的橋樑。此資料層也可定義該狀態的來源與事件處理方式。

範例

以下程式碼片段引入新的目的地,可顯示聯絡人的詳細資料,並更新現有的聯絡人清單目的地,以顯示導覽事件以顯示聯絡人的詳細資料。

以下是可以設為自身模組 internal 的一般畫面組合,這樣一來其他模組就無法存取:

// ContactScreens.kt

// Displays a list of contacts
@Composable
internal fun ContactsScreen(
  uiState: ContactsUiState,
  onNavigateToContactDetails: (contactId: String) -> Unit
) { ... }

// Displays the details for an individual contact
@Composable
internal fun ContactDetailsScreen(contact: ContactDetails) { ... }

建立目的地

下列 NavGraphBuilder 擴充功能函式會建立目的地,顯示 ConversationScreen 可組合項。此外,它現在會將畫面與提供畫面 UI 狀態的 ViewModel 連結,並處理畫面相關的商業邏輯。

系統會將導覽事件 (例如前往聯絡人詳細資料目的地) 提供給呼叫端,而非由 ViewModel 處理。

// ContactsNavigation.kt

@Serializable
object Contacts

// Adds contacts destination to `this` NavGraphBuilder
fun NavGraphBuilder.contactsDestination(
  // Navigation events are exposed to the caller to be handled at a higher level
  onNavigateToContactDetails: (contactId: String) -> Unit
) {
  composable<Contacts> {
    // The ViewModel as a screen level state holder produces the screen
    // UI state and handles business logic for the ConversationScreen
    val viewModel: ContactsViewModel = hiltViewModel()
    val uiState = viewModel.uiState.collectAsStateWithLifecycle()
    ContactsScreen(
      uiState,
      onNavigateToContactDetails
    )
  }
}

您可以使用相同的方法建立顯示 ContactDetailsScreen 的目的地。在此情況下,您可以直接從 NavBackStackEntry 取得 UI 狀態,而不必從檢視畫面模型取得 UI 狀態。

// ContactsNavigation.kt

@Serializable
internal data class ContactDetails(val id: String)

fun NavGraphBuilder.contactDetailsScreen() {
  composable<ContactDetails> { navBackStackEntry ->
    ContactDetailsScreen(contact = navBackStackEntry.toRoute())
  }
}

封裝導覽事件

就像封裝目的地的方式一樣,您可以封裝導覽事件,避免不必要的路徑類型公開。請在 NavController 上建立擴充功能函式。

// ContactsNavigation.kt

fun NavController.navigateToContactDetails(id: String) {
  navigate(route = ContactDetails(id = id))
}

合併使用

用於顯示聯絡人的導覽程式碼,現已與應用程式的導覽圖清楚區隔。應用程式必須符合以下條件:

  • 呼叫 NavGraphBuilder 擴充功能函式以建立目的地
  • 呼叫用於導覽事件的 NavController 擴充功能函式,即可連結這些目的地
// MyApp.kt

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
     contactsDestination(onNavigateToContactDetails = { contactId ->
        navController.navigateToContactDetails(id = contactId)
     })
     contactDetailsDestination()
  }
}

摘要

  • 藉由將導覽程式碼放入不同檔案,為一組相關的畫面封裝導覽程式碼
  • NavGraphBuilder 上建立擴充功能函式來公開目的地
  • NavController 上建立擴充功能函式來公開導覽事件
  • 使用 internal 確保畫面和路線類型不外洩