āϰāϚāύāĻž āϏāĻš āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ

āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ āĻœā§‡āϟāĻĒā§āϝāĻžāĻ• āĻ•āĻŽā§āĻĒā§‹āϜ āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ⧇āϰ āϜāĻ¨ā§āϝ āϏāĻŽāĻ°ā§āĻĨāύ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰ⧇āĨ¤ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āωāĻĒāĻžāĻĻāĻžāύ⧇āϰ āĻĒāϰāĻŋāĻ•āĻžāĻ āĻžāĻŽā§‹ āĻāĻŦāĻ‚ āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝāϗ⧁āϞāĻŋāϰ āϏ⧁āĻŦāĻŋāϧāĻž āĻ—ā§āϰāĻšāĻŖ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āφāĻĒāύāĻŋ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞāϗ⧁āϞāĻŋāϰ āĻŽāĻ§ā§āϝ⧇ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤

āϰāϚāύāĻžāϰ āϜāĻ¨ā§āϝ āĻŦāĻŋāĻļ⧇āώāĻ­āĻžāĻŦ⧇ āύāĻŋāĻ°ā§āĻŽāĻŋāϤ āϏāĻ°ā§āĻŦāĻļ⧇āώ āφāϞāĻĢāĻž āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋāϰ āϜāĻ¨ā§āϝ, āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ 3 āĻĄāϕ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āĻļāύ āĻĻ⧇āϖ⧁āύāĨ¤

āϏ⧇āϟāφāĻĒ

āϰāϚāύāĻž āϏāĻŽāĻ°ā§āĻĨāύ āĻ•āϰāϤ⧇, āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ āĻŽāĻĄāĻŋāωāϞ⧇āϰ build.gradle āĻĢāĻžāχāϞ⧇ āύāĻŋāĻŽā§āύāϞāĻŋāĻ–āĻŋāϤ āύāĻŋāĻ°ā§āĻ­āϰāϤāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ:

āĻ—ā§āϰ⧋āĻ­āĻŋ

dependencies {
    def nav_version = "2.9.3"

    implementation "androidx.navigation:navigation-compose:$nav_version"
}

āϕ⧋āϟāϞāĻŋāύ

dependencies {
    val nav_version = "2.9.3"

    implementation("androidx.navigation:navigation-compose:$nav_version")
}

āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύ

āĻāĻ•āϟāĻŋ āĻ…ā§āϝāĻžāĻĒ⧇ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻĒā§āϰāϝāĻŧā§‹āĻ— āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ, āĻāĻ•āϟāĻŋ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻšā§‹āĻ¸ā§āϟ, āĻ—ā§āϰāĻžāĻĢ āĻāĻŦāĻ‚ āĻ•āĻ¨ā§āĻŸā§āϰ⧋āϞāĻžāϰ āĻĒā§āϰāϝāĻŧā§‹āĻ— āĻ•āϰ⧁āύāĨ¤ āφāϰāĻ“ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ, āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ“āĻ­āĻžāϰāĻ­āĻŋāω āĻĻ⧇āϖ⧁āύāĨ¤

āĻ•āĻŽā§āĻĒā§‹āĻœā§‡ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻāĻ•āϟāĻŋ NavController āϤ⧈āϰāĻŋ āĻ•āϰāĻŦ⧇āύ āϏ⧇ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ, āĻāĻ•āϟāĻŋ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ•āĻ¨ā§āĻŸā§āϰ⧋āϞāĻžāϰ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ āĻāϰ āϰāϚāύāĻž āĻŦāĻŋāĻ­āĻžāĻ—āϟāĻŋ āĻĻ⧇āϖ⧁āύāĨ¤

āĻāĻ•āϟāĻŋ NavHost āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

āĻ•āĻŽā§āĻĒā§‹āĻœā§‡ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻāĻ•āϟāĻŋ NavHost āϤ⧈āϰāĻŋ āĻ•āϰāĻŦ⧇āύ āϏ⧇ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ, āφāĻĒāύāĻžāϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ—ā§āϰāĻžāĻĢ āĻĄāĻŋāϜāĻžāχāύ⧇āϰ āϰāϚāύāĻž āĻŦāĻŋāĻ­āĻžāĻ—āϟāĻŋ āĻĻ⧇āϖ⧁āύāĨ¤

āĻāĻ•āϟāĻŋ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ⧇ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāĻžāϰ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ, āφāĻ°ā§āĻ•āĻŋāĻŸā§‡āĻ•āϚāĻžāϰ āĻĄāϕ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āĻļāύ⧇ āĻāĻ•āϟāĻŋ āĻ—āĻ¨ā§āϤāĻŦā§āϝ⧇ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻĻ⧇āϖ⧁āύāĨ¤

āĻ•āĻŽā§āĻĒā§‹āϜāϝ⧋āĻ—ā§āϝ āĻ—āĻ¨ā§āϤāĻŦā§āϝāϗ⧁āϞāĻŋāϰ āĻŽāĻ§ā§āϝ⧇ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻĒāĻžāϏ āĻ•āϰāĻžāϰ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ, āφāĻĒāύāĻžāϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ—ā§āϰāĻžāĻĢ āĻĄāĻŋāϜāĻžāχāύ āĻ•āϰ⧁āύ āĻāϰ āϰāϚāύāĻž āĻŦāĻŋāĻ­āĻžāĻ—āϟāĻŋ āĻĻ⧇āϖ⧁āύāĨ¤

āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āϜāϟāĻŋāϞ āĻĄā§‡āϟāĻž āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧁āύ

āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āϜāϟāĻŋāϞ āĻĄā§‡āϟāĻž āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸā§‡āϰ āφāĻļ⧇āĻĒāĻžāĻļ⧇ āύāĻž āϝāĻžāĻ“āϝāĻŧāĻžāϰ āϜāĻ¨ā§āϝ āĻĻ⧃āĻĸāĻŧāĻ­āĻžāĻŦ⧇ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāϝāĻŧ, āĻŦāϰāĻ‚ āĻ¨ā§āϝāĻžāĻ­āĻŋāϗ⧇āĻļāύ āĻ…ā§āϝāĻžāĻ•āĻļāύāϗ⧁āϞāĻŋ āϏāĻŽā§āĻĒāĻžāĻĻāύ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻšāĻŋāϏāĻžāĻŦ⧇ āĻ¨ā§āϝ⧂āύāϤāĻŽ āĻĒā§āϰāϝāĻŧā§‹āϜāύ⧀āϝāĻŧ āϤāĻĨā§āϝ, āϝ⧇āĻŽāύ āĻāĻ•āϟāĻŋ āĻ…āύāĻ¨ā§āϝ āĻļāύāĻžāĻ•ā§āϤāĻ•āĻžāϰ⧀ āĻŦāĻž āĻ…āĻ¨ā§āϝ āϧāϰāύ⧇āϰ āφāχāĻĄāĻŋ āĻĒāĻžāϏ āĻ•āϰ⧁āύ:

// Pass only the user ID when navigating to a new destination as argument
navController.navigate(Profile(id = "user1234"))

āϜāϟāĻŋāϞ āĻŦāĻ¸ā§āϤ⧁āϗ⧁āϞāĻŋāϕ⧇ āĻĄā§‡āϟāĻž āĻ¸ā§āϤāϰ⧇āϰ āĻŽāϤ⧋ āϏāĻ¤ā§āϝ⧇āϰ āĻāĻ•āĻ• āĻ‰ā§ŽāϏ⧇ āĻĄā§‡āϟāĻž āĻšāĻŋāϏāĻžāĻŦ⧇ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰāĻž āωāϚāĻŋāϤāĨ¤ āĻāĻ•āĻŦāĻžāϰ āφāĻĒāύāĻŋ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāĻžāϰ āĻĒāϰ⧇ āφāĻĒāύāĻžāϰ āĻ—āĻ¨ā§āϤāĻŦā§āϝ⧇ āĻ…āĻŦāϤāϰāĻŖ āĻ•āϰāϞ⧇, āφāĻĒāύāĻŋ āĻĒāĻžāϏ āĻ•āϰāĻž āφāχāĻĄāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āϏāĻ¤ā§āϝ⧇āϰ āĻāĻ•āĻ• āĻ‰ā§ŽāϏ āĻĨ⧇āϕ⧇ āĻĒā§āϰāϝāĻŧā§‹āϜāύ⧀āϝāĻŧ āϤāĻĨā§āϝ āϞ⧋āĻĄ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤ āĻĄā§‡āϟāĻž āĻ¸ā§āϤāϰ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻĻāĻžāϝāĻŧā§€ āφāĻĒāύāĻžāϰ ViewModel āĻāϰ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟāϗ⧁āϞāĻŋ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰāϤ⧇, ViewModel āĻāϰ SavedStateHandle āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ:

class UserViewModel(
    savedStateHandle: SavedStateHandle,
    private val userInfoRepository: UserInfoRepository
) : ViewModel() {

    private val profile = savedStateHandle.toRoute<Profile>()

    // Fetch the relevant user information from the data layer,
    // ie. userInfoRepository, based on the passed userId argument
    private val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(profile.id)

// â€Ļ

}

āĻāχ āĻĒāĻĻā§āϧāϤāĻŋāϟāĻŋ āĻ•āύāĻĢāĻŋāĻ—āĻžāϰ⧇āĻļāύ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ⧇āϰ āϏāĻŽāϝāĻŧ āĻĄā§‡āϟāĻž āĻ•ā§āώāϤāĻŋ āĻĒā§āϰāϤāĻŋāϰ⧋āϧ āĻ•āϰāϤ⧇ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āϝāĻ–āύ āĻĒā§āϰāĻļā§āύ⧇ āĻĨāĻžāĻ•āĻž āĻŦāĻ¸ā§āϤ⧁āϟāĻŋ āφāĻĒāĻĄā§‡āϟ āĻŦāĻž āĻŽāĻŋāωāĻŸā§‡āϟ āĻ•āϰāĻž āĻšāϝāĻŧ āϤāĻ–āύ āϕ⧋āύ⧋ āĻ…āϏāĻ™ā§āĻ—āϤāĻŋāĨ¤

āφāĻĒāύāĻŋ āϕ⧇āύ āϜāϟāĻŋāϞ āĻĄā§‡āϟāĻžāϕ⧇ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻĒāĻžāϏ āĻ•āϰāĻž āĻāĻĄāĻŧāĻŋāϝāĻŧ⧇ āϝāĻžāĻŦ⧇āύ, āϏ⧇āχāϏāĻžāĻĨ⧇ āϏāĻŽāĻ°ā§āĻĨāĻŋāϤ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻĒā§āϰāĻ•āĻžāϰ⧇āϰ āϤāĻžāϞāĻŋāĻ•āĻžāϰ āφāϰāĻ“ āĻ—āĻ­ā§€āϰ āĻŦā§āϝāĻžāĻ–ā§āϝāĻžāϰ āϜāĻ¨ā§āϝ, āĻ—āĻ¨ā§āϤāĻŦā§āϝ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻĄā§‡āϟāĻž āĻĒāĻžāϏ āĻ•āϰ⧁āύ āĻĻ⧇āϖ⧁āύāĨ¤

āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ•āĻŽā§āĻĒā§‹āϜ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ•āϗ⧁āϞāĻŋāϕ⧇ āϏāĻŽāĻ°ā§āĻĨāύ āĻ•āϰ⧇ āϝāĻž composable() āĻĢāĻžāĻ‚āĻļāύ⧇āϰ āĻ…āĻ‚āĻļ āĻšāĻŋāϏāĻžāĻŦ⧇āĻ“ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āĻāϰ deepLinks āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāϟāĻžāϰāϟāĻŋ NavDeepLink āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸā§‡āϰ āĻāĻ•āϟāĻŋ āϤāĻžāϞāĻŋāĻ•āĻž āĻ—ā§āϰāĻšāĻŖ āĻ•āϰ⧇ āϝāĻž navDeepLink() āĻĒāĻĻā§āϧāϤāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻĻā§āϰ⧁āϤ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

@Serializable data class Profile(val id: String)
val uri = "https://www.example.com"

composable<Profile>(
  deepLinks = listOf(
    navDeepLink<Profile>(basePath = "$uri/profile")
  )
) { backStackEntry ->
  ProfileScreen(id = backStackEntry.toRoute<Profile>().id)
}

āĻāχ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ•āϗ⧁āϞāĻŋ āφāĻĒāύāĻžāϕ⧇ āĻāĻ•āϟāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āχāωāφāϰāĻāϞ, āĻ…ā§āϝāĻžāĻ•āĻļāύ āĻŦāĻž āĻŽāĻžāχāĻŽ āϟāĻžāχāĻĒ āĻāĻ•āϟāĻŋ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ⧇āϰ āϏāĻžāĻĨ⧇ āϏāĻ‚āϝ⧁āĻ•ā§āϤ āĻ•āϰāϤ⧇ āĻĻ⧇āϝāĻŧāĨ¤ āĻĄāĻŋāĻĢāĻ˛ā§āϟāϰ⧂āĻĒ⧇, āĻāχ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ•āϗ⧁āϞāĻŋ āĻŦāĻžāĻšā§āϝāĻŋāĻ• āĻ…ā§āϝāĻžāĻĒāϗ⧁āϞāĻŋāϰ āĻ•āĻžāϛ⧇ āĻĒā§āϰāĻ•āĻžāĻļ āĻ•āϰāĻž āĻšāϝāĻŧ āύāĻžā§ˇ āĻāχ āĻĄāĻŋāĻĒ āϞāĻŋāĻ™ā§āĻ•āϗ⧁āϞāĻŋāϕ⧇ āĻŦāĻžāĻšā§āϝāĻŋāĻ•āĻ­āĻžāĻŦ⧇ āωāĻĒāϞāĻŦā§āϧ āĻ•āϰāϤ⧇ āφāĻĒāύāĻžāϕ⧇ āĻ…āĻŦāĻļā§āϝāχ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ⧇āϰ manifest.xml āĻĢāĻžāχāϞ⧇ āωāĻĒāϝ⧁āĻ•ā§āϤ <intent-filter> āωāĻĒāĻžāĻĻāĻžāύ āϝ⧋āĻ— āĻ•āϰāϤ⧇ āĻšāĻŦ⧇āĨ¤ āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āωāĻĻāĻžāĻšāϰāϪ⧇ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ•āϟāĻŋ āϏāĻ•ā§āώāĻŽ āĻ•āϰāϤ⧇, āφāĻĒāύāĻžāϕ⧇ āĻŽā§āϝāĻžāύāĻŋāĻĢ⧇āĻ¸ā§āĻŸā§‡āϰ <activity> āωāĻĒāĻžāĻĻāĻžāύ⧇āϰ āĻ­āĻŋāϤāϰ⧇ āύāĻŋāĻŽā§āύāϞāĻŋāĻ–āĻŋāϤāϟāĻŋ āϝ⧁āĻ•ā§āϤ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇:

<activity â€Ļ>
  <intent-filter>
    ...
    <data android:scheme="https" android:host="www.example.com" />
  </intent-filter>
</activity>

āĻ¨ā§āϝāĻžāĻ­āĻŋāϗ⧇āĻļāύ āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āϏ⧇āχ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ⧇ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ•āϗ⧁āϞāĻŋāϕ⧇ āϏāĻ‚āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧇ āϝāĻ–āύ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ•āϟāĻŋ āĻ…āĻ¨ā§āϝ āĻ…ā§āϝāĻžāĻĒ āĻĻā§āĻŦāĻžāϰāĻž āĻŸā§āϰāĻŋāĻ—āĻžāϰ āĻšāϝāĻŧāĨ¤

āĻāχ āĻāĻ•āχ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ•āϗ⧁āϞāĻŋ āĻāĻ•āϟāĻŋ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āĻĨ⧇āϕ⧇ āωāĻĒāϝ⧁āĻ•ā§āϤ āĻ—āĻ­ā§€āϰ āϞāĻŋāĻ™ā§āĻ• āϏāĻš āĻāĻ•āϟāĻŋ PendingIntent āϤ⧈āϰāĻŋ āĻ•āϰāϤ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/profile/$id".toUri(),
    context,
    MyActivity::class.java
)

val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

āϤāĻžāϰāĻĒāϰ āφāĻĒāύāĻŋ āĻĄāĻŋāĻĒ āϞāĻŋāĻ™ā§āϕ⧇āϰ āĻ—āĻ¨ā§āϤāĻŦā§āϝ⧇ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ āϖ⧁āϞāϤ⧇ āĻ…āĻ¨ā§āϝ āϝ⧇āϕ⧋āύ⧋ PendingIntent āĻŽāϤ⧋ āĻāχ deepLinkPendingIntent āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤

āύ⧇āĻ¸ā§āĻŸā§‡āĻĄ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ

āύ⧇āĻ¸ā§āĻŸā§‡āĻĄ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ—ā§āϰāĻžāĻĢāϗ⧁āϞāĻŋ āϕ⧀āĻ­āĻžāĻŦ⧇ āϤ⧈āϰāĻŋ āĻ•āϰāĻŦ⧇āύ āϏ⧇ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ, āύ⧇āĻ¸ā§āĻŸā§‡āĻĄ āĻ—ā§āϰāĻžāĻĢāϗ⧁āϞāĻŋ āĻĻ⧇āϖ⧁āύāĨ¤

āύāĻŋāĻšā§‡āϰ āĻāύāĻāĻ­āĻŋ āĻŦāĻžāϰ⧇āϰ āϏāĻžāĻĨ⧇ āχāĻ¨ā§āϟāĻŋāĻ—ā§āϰ⧇āĻļāύ

āφāĻĒāύāĻžāϰ āϏāĻ‚āĻŽāĻŋāĻļā§āϰāĻŖāϝ⧋āĻ—ā§āϝ āĻ…āύ⧁āĻ•ā§āϰāĻŽā§‡āϰ āĻāĻ•āϟāĻŋ āωāĻšā§āϚ āĻ¸ā§āϤāϰ⧇ NavController āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰ⧇, āφāĻĒāύāĻŋ āĻ…āĻ¨ā§āϝāĻžāĻ¨ā§āϝ āωāĻĒāĻžāĻĻāĻžāύ āϝ⧇āĻŽāύ āύ⧀āĻšā§‡āϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āωāĻĒāĻžāĻĻāĻžāύāϗ⧁āϞāĻŋāϰ āϏāĻžāĻĨ⧇ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϏāĻ‚āϝ⧋āĻ— āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤ āĻāϟāĻŋ āĻ•āϰāĻžāϰ āĻĢāϞ⧇ āφāĻĒāύāĻŋ āύ⧀āĻšā§‡āϰ āĻŦāĻžāϰ⧇ āφāχāĻ•āύāϗ⧁āϞāĻŋ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧇ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύ⧎

BottomNavigation āĻāĻŦāĻ‚ BottomNavigationItem āωāĻĒāĻžāĻĻāĻžāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇, āφāĻĒāύāĻžāϰ Android āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ⧇ androidx.compose.material āύāĻŋāĻ°ā§āĻ­āϰāϤāĻž āϝ⧋āĻ— āĻ•āϰ⧁āύāĨ¤

āĻ—ā§āϰ⧋āĻ­āĻŋ

dependencies {
    implementation "androidx.compose.material:material:1.9.0"
}

android {
    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.15"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

āϕ⧋āϟāϞāĻŋāύ

dependencies {
    implementation("androidx.compose.material:material:1.9.0")
}

android {
    buildFeatures {
        compose = true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.15"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

āφāĻĒāύāĻžāϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ—ā§āϰāĻžāĻĢ⧇āϰ āϰ⧁āĻŸā§‡āϰ āϏāĻžāĻĨ⧇ āύ⧀āĻšā§‡āϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦāĻžāϰ⧇ āĻĨāĻžāĻ•āĻž āφāχāĻŸā§‡āĻŽāϗ⧁āϞāĻŋāϕ⧇ āϞāĻŋāĻ™ā§āĻ• āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ, āĻāĻ–āĻžāύ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ TopLevelRoute āĻāϰ āĻŽāϤ⧋ āĻāĻ•āϟāĻŋ āĻ•ā§āϞāĻžāϏ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰāĻžāϰ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāĻšā§āϛ⧇, āϝāĻžāϰ āĻāĻ•āϟāĻŋ āϰ⧁āϟ āĻ•ā§āϞāĻžāϏ āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āφāχāĻ•āύ āϰāϝāĻŧ⧇āϛ⧇⧎

data class TopLevelRoute<T : Any>(val name: String, val route: T, val icon: ImageVector)

āϤāĻžāϰāĻĒāϰ āϏ⧇āχ āϰ⧁āϟāϗ⧁āϞāĻŋāϕ⧇ āĻāĻ•āϟāĻŋ āϤāĻžāϞāĻŋāĻ•āĻžāϝāĻŧ āϰāĻžāϖ⧁āύ āϝāĻž BottomNavigationItem āĻĻā§āĻŦāĻžāϰāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

val topLevelRoutes = listOf(
   TopLevelRoute("Profile", Profile, Icons.Profile),
   TopLevelRoute("Friends", Friends, Icons.Friends)
)

āφāĻĒāύāĻžāϰ BottomNavigation composable āĻ, currentBackStackEntryAsState() āĻĢāĻžāĻ‚āĻļāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻŦāĻ°ā§āϤāĻŽāĻžāύ NavBackStackEntry āĻĒāĻžāύāĨ¤ āĻāχ āĻāĻ¨ā§āĻŸā§āϰāĻŋ āφāĻĒāύāĻžāϕ⧇ āĻŦāĻ°ā§āϤāĻŽāĻžāύ NavDestination āĻ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻĻ⧇āϝāĻŧāĨ¤ āĻĒā§āϰāϤāĻŋāϟāĻŋ BottomNavigationItem āĻāϰ āύāĻŋāĻ°ā§āĻŦāĻžāϚāĻŋāϤ āĻ…āĻŦāĻ¸ā§āĻĨāĻž āϤāĻžāϰāĻĒāϰ⧇ āĻŦāĻ°ā§āϤāĻŽāĻžāύ āĻ—āĻ¨ā§āϤāĻŦā§āϝ⧇āϰ āϰ⧁āĻŸā§‡āϰ āϏāĻžāĻĨ⧇ āφāχāĻŸā§‡āĻŽā§‡āϰ āϰ⧁āϟ āĻāĻŦāĻ‚ āĻāϰ āĻŽā§‚āϞ āĻ—āĻ¨ā§āϤāĻŦā§āϝāϗ⧁āϞāĻŋāϰ āϏāĻžāĻĨ⧇ āϤ⧁āϞāύāĻž āĻ•āϰ⧇ āύāĻŋāĻ°ā§āϧāĻžāϰāĻŖ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇ āϝāĻ–āύ āφāĻĒāύāĻŋ NavDestination āĻļā§āϰ⧇āĻŖā§€āĻŦāĻŋāĻ¨ā§āϝāĻžāϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āύ⧇āĻ¸ā§āĻŸā§‡āĻĄ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϛ⧇āύāĨ¤

navigate āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ āĻ•āϞ⧇āϰ āϏāĻžāĻĨ⧇ onClick lambda āϏāĻ‚āϝ⧋āĻ— āĻ•āϰāϤ⧇ āφāχāĻŸā§‡āĻŽā§‡āϰ āϰ⧁āϟāϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧ āϝāĻžāϤ⧇ āφāχāĻŸā§‡āĻŽāϟāĻŋāϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰāĻž āϏ⧇āχ āφāχāĻŸā§‡āĻŽā§‡ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰ⧇āĨ¤ saveState āĻāĻŦāĻ‚ restoreState āĻĒāϤāĻžāĻ•āĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āφāĻĒāύāĻŋ āύ⧀āĻšā§‡āϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āφāχāĻŸā§‡āĻŽāϗ⧁āϞāĻŋāϰ āĻŽāĻ§ā§āϝ⧇ āĻ…āĻĻāϞāĻŦāĻĻāϞ āĻ•āϰāĻžāϰ āϏāĻžāĻĨ⧇ āϏāĻžāĻĨ⧇ āϏ⧇āχ āφāχāĻŸā§‡āĻŽā§‡āϰ āϰāĻžāĻœā§āϝ āĻāĻŦāĻ‚ āĻĒāĻŋāĻ›āύ⧇āϰ āĻ¸ā§āĻŸā§āϝāĻžāĻ• āϏāĻ āĻŋāĻ•āĻ­āĻžāĻŦ⧇ āϏāĻ‚āϰāĻ•ā§āώāĻŋāϤ āĻāĻŦāĻ‚ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤

val navController = rememberNavController()
Scaffold(
  bottomBar = {
    BottomNavigation {
      val navBackStackEntry by navController.currentBackStackEntryAsState()
      val currentDestination = navBackStackEntry?.destination
      topLevelRoutes.forEach { topLevelRoute ->
        BottomNavigationItem(
          icon = { Icon(topLevelRoute.icon, contentDescription = topLevelRoute.name) },
          label = { Text(topLevelRoute.name) },
          selected = currentDestination?.hierarchy?.any { it.hasRoute(topLevelRoute.route::class) } == true,
          onClick = {
            navController.navigate(topLevelRoute.route) {
              // Pop up to the start destination of the graph to
              // avoid building up a large stack of destinations
              // on the back stack as users select items
              popUpTo(navController.graph.findStartDestination().id) {
                saveState = true
              }
              // Avoid multiple copies of the same destination when
              // reselecting the same item
              launchSingleTop = true
              // Restore state when reselecting a previously selected item
              restoreState = true
            }
          }
        )
      }
    }
  }
) { innerPadding ->
  NavHost(navController, startDestination = Profile, Modifier.padding(innerPadding)) {
    composable<Profile> { ProfileScreen(...) }
    composable<Friends> { FriendsScreen(...) }
  }
}

āĻāĻ–āĻžāύ⧇ āφāĻĒāύāĻŋ NavController.currentBackStackEntryAsState() āĻĒāĻĻā§āϧāϤāĻŋāϰ āϏ⧁āĻŦāĻŋāϧāĻž āύ⧇āĻŦ⧇āύ āϝāĻžāϤ⧇ NavHost āĻĢāĻžāĻ‚āĻļāύ āĻĨ⧇āϕ⧇ navController āĻ¸ā§āĻŸā§‡āϟāϟāĻŋ āωāĻ¤ā§āϤ⧋āϞāύ āĻ•āϰāĻž āϝāĻžāϝāĻŧ āĻāĻŦāĻ‚ āĻāϟāĻŋ BottomNavigation āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āĻŸā§‡āϰ āϏāĻžāĻĨ⧇ āĻļ⧇āϝāĻŧāĻžāϰ āĻ•āϰāĻž āϝāĻžāϝāĻŧāĨ¤ āĻāϰ āĻŽāĻžāύ⧇ āĻšāϞ BottomNavigation āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āφāĻĒ-āϟ⧁-āĻĄā§‡āϟ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĨ¤

āχāĻ¨ā§āϟāĻžāϰāĻ…āĻĒāĻžāϰ⧇āĻŦāĻŋāϞāĻŋāϟāĻŋ

āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āϰāϚāύāĻžāϰ āϏāĻžāĻĨ⧇ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āωāĻĒāĻžāĻĻāĻžāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āϚāĻžāύ āϤāĻŦ⧇ āφāĻĒāύāĻžāϰ āĻ•āĻžāϛ⧇ āĻĻ⧁āϟāĻŋ āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āϰāϝāĻŧ⧇āϛ⧇:

  • āĻ–āĻŖā§āĻĄā§‡āϰ āϜāĻ¨ā§āϝ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āωāĻĒāĻžāĻĻāĻžāύ āϏāĻš āĻāĻ•āϟāĻŋ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ—ā§āϰāĻžāĻĢ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰ⧁āύāĨ¤
  • āĻ•āĻŽā§āĻĒā§‹āϜ āĻ—āĻ¨ā§āϤāĻŦā§āϝāϗ⧁āϞāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āϰāϚāύāĻžāϤ⧇ āĻāĻ•āϟāĻŋ NavHost āϏāĻš āĻāĻ•āϟāĻŋ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ—ā§āϰāĻžāĻĢ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰ⧁āύāĨ¤ āĻ¨ā§āϝāĻžāĻ­āĻŋāϗ⧇āĻļāύ āĻ—ā§āϰāĻžāĻĢ⧇āϰ āϏāĻŽāĻ¸ā§āϤ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āĻšāϞ⧇āχ āĻāϟāĻŋ āϏāĻŽā§āĻ­āĻŦāĨ¤

āĻ…āϤāĻāĻŦ, āĻŽāĻŋāĻļā§āϰ āϰāϚāύāĻž āĻāĻŦāĻ‚ āĻ­āĻŋāω āĻ…ā§āϝāĻžāĻĒāϗ⧁āϞāĻŋāϰ āϜāĻ¨ā§āϝ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āĻšāϞ āĻĢā§āĻ°ā§āϝāĻžāĻ—āĻŽā§‡āĻ¨ā§āϟ-āĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ• āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āωāĻĒāĻžāĻĻāĻžāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāĨ¤ āĻĢā§āĻ°ā§āϝāĻžāĻ—āĻŽā§‡āĻ¨ā§āϟāϗ⧁āϞāĻŋ āϤāĻ–āύ āĻ­āĻŋāω-āĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ• āĻ¸ā§āĻ•ā§āϰ⧀āύ, āĻ•āĻŽā§āĻĒā§‹āϜ āĻ¸ā§āĻ•ā§āϰ⧀āύ āĻāĻŦāĻ‚ āĻ¸ā§āĻ•ā§āϰ⧀āύāϗ⧁āϞāĻŋāϕ⧇ āϧāϰ⧇ āϰāĻžāĻ–āĻŦ⧇ āϝāĻž āĻ­āĻŋāω āĻāĻŦāĻ‚ āĻ•āĻŽā§āĻĒā§‹āϜ āωāĻ­āϝāĻŧāχ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇āĨ¤ āĻāĻ•āĻŦāĻžāϰ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻĢā§āĻ°ā§āϝāĻžāĻ—āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āĻŦāĻŋāώāϝāĻŧāĻŦāĻ¸ā§āϤ⧁ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡ āĻšāϝāĻŧ⧇ āϗ⧇āϞ⧇, āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āϧāĻžāĻĒ āĻšāϞ āϏ⧇āχ āϏāĻŽāĻ¸ā§āϤ āĻ¸ā§āĻ•ā§āϰ⧀āύāϗ⧁āϞāĻŋāϕ⧇ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āϰ āϏāĻžāĻĨ⧇ āĻŦ⧇āρāϧ⧇ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻāĻŦāĻ‚ āϏāĻŽāĻ¸ā§āϤ āϟ⧁āĻ•āϰ⧋āϗ⧁āϞāĻŋ āϏāϰāĻŋāϝāĻŧ⧇ āĻĢ⧇āϞāĻžāĨ¤

āĻ•āĻŽā§āĻĒā§‹āϜ āϕ⧋āĻĄā§‡āϰ āĻŽāĻ§ā§āϝ⧇ āĻ—āĻ¨ā§āϤāĻŦā§āϝ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ, āφāĻĒāύāĻŋ āĻāĻŽāύ āχāϭ⧇āĻ¨ā§āϟāϗ⧁āϞāĻŋ āĻĒā§āϰāĻ•āĻžāĻļ āĻ•āϰ⧇āύ āϝāĻž āĻ…āύ⧁āĻ•ā§āϰāĻŽā§‡āϰ āϝ⧇āϕ⧋āύ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āĻĻā§āĻŦāĻžāϰāĻž āĻĒāĻžāϏ āĻ•āϰāĻž āϝāĻžāϝāĻŧ āĻāĻŦāĻ‚ āĻŸā§āϰāĻŋāĻ—āĻžāϰ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

@Composable
fun MyScreen(onNavigate: (Int) -> Unit) {
    Button(onClick = { onNavigate(R.id.nav_profile) } { /* ... */ }
}

āφāĻĒāύāĻžāϰ āĻ–āĻŖā§āĻĄā§‡, āφāĻĒāύāĻŋ NavController āϖ⧁āρāĻœā§‡ āĻāĻŦāĻ‚ āĻ—āĻ¨ā§āϤāĻŦā§āϝ⧇ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāĻžāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āϰāϚāύāĻž āĻāĻŦāĻ‚ āĻ–āĻŖā§āĻĄ-āĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ• āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āωāĻĒāĻžāĻĻāĻžāύ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āϏ⧇āϤ⧁ āϤ⧈āϰāĻŋ āĻ•āϰ⧇āύ:

override fun onCreateView( /* ... */ ) {
    setContent {
        MyScreen(onNavigate = { dest -> findNavController().navigate(dest) })
    }
}

āĻŦāĻŋāĻ•āĻ˛ā§āĻĒāĻ­āĻžāĻŦ⧇, āφāĻĒāύāĻŋ āφāĻĒāύāĻžāϰ āϰāϚāύāĻž āĻ…āύ⧁āĻ•ā§āϰāĻŽā§‡āϰ āύāĻŋāĻšā§‡ NavController āĻĒāĻžāϏ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤ āϝāĻžāχāĻšā§‹āĻ•, āϏāĻžāϧāĻžāϰāĻŖ āĻĢāĻžāĻ‚āĻļāύ āĻĒā§āϰāĻ•āĻžāĻļ āĻ•āϰāĻž āĻ…āύ⧇āĻ• āĻŦ⧇āĻļāĻŋ āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰāϝ⧋āĻ—ā§āϝ āĻāĻŦāĻ‚ āĻĒāϰ⧀āĻ•ā§āώāĻžāϝ⧋āĻ—ā§āϝāĨ¤

āĻŸā§‡āĻ¸ā§āϟāĻŋāĻ‚

āφāĻĒāύāĻžāϰ āϏāĻ‚āĻŽāĻŋāĻļā§āϰāĻŖāϝ⧋āĻ—ā§āϝ āĻ—āĻ¨ā§āϤāĻŦā§āϝāϗ⧁āϞāĻŋ āĻĨ⧇āϕ⧇ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϕ⧋āĻĄāϟāĻŋ āĻĻā§āĻŦāĻŋāϗ⧁āĻŖ āĻ•āϰ⧁āύ āϝāĻžāϤ⧇ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞāϕ⧇ āĻŦāĻŋāĻšā§āĻ›āĻŋāĻ¨ā§āύāĻ­āĻžāĻŦ⧇ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāĻž āϝāĻžāϝāĻŧ, āϝāĻž NavHost āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻžāĨ¤

āĻāϰ āĻŽāĻžāύ⧇ āĻšāϞ āϝ⧇ āφāĻĒāύāĻžāϰ navController āĻ•āϞāĻŦā§āϝāĻžāĻ•āϗ⧁āϞāĻŋāϕ⧇ āĻĒāϰāĻžāĻŽāĻŋāϤāĻŋ āĻšāĻŋāϏāĻžāĻŦ⧇ āϏāϰāĻžāϏāϰāĻŋ āϕ⧋āύāĻ“ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ⧇ āĻĒāĻžāϏ āĻ•āϰāĻž āωāϚāĻŋāϤ āύāϝāĻŧāĨ¤ āĻāϟāĻŋ āφāĻĒāύāĻžāϰ āϏāĻŽāĻ¸ā§āϤ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞāϕ⧇ āĻĒ⧃āĻĨāĻ•āĻ­āĻžāĻŦ⧇ āĻĒāϰ⧀āĻ•ā§āώāĻžāϝ⧋āĻ—ā§āϝ āĻšāϤ⧇ āĻĻ⧇āϝāĻŧ, āĻ•āĻžāϰāĻŖ āϤāĻžāĻĻ⧇āϰ āĻĒāϰ⧀āĻ•ā§āώāĻžāϝāĻŧ navController āĻāĻ•āϟāĻŋ āωāĻĻāĻžāĻšāϰāĻŖ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āĻšāϝāĻŧ āύāĻžāĨ¤

composable āĻ˛ā§āϝāĻžāĻŽā§āĻŦāĻĄāĻž āĻĻā§āĻŦāĻžāϰāĻž āĻĒā§āϰāĻĻāĻ¤ā§āϤ āĻĒāϰ⧋āĻ•ā§āώ āĻ¸ā§āϤāϰ āϝāĻž āφāĻĒāύāĻžāϕ⧇ āφāĻĒāύāĻžāϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϕ⧋āĻĄāϕ⧇ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻž āĻ•āϰāϤ⧇ āĻĻ⧇āϝāĻŧāĨ¤ āĻāϟāĻŋ āĻĻ⧁āϟāĻŋ āĻĻāĻŋāϕ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇:

  • āφāĻĒāύāĻžāϰ āϰāϚāύāĻžāϝ⧋āĻ—ā§āϝ āĻŽāĻ§ā§āϝ⧇ āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āĻĒāĻžāĻ°ā§āϏ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻĒāĻžāϏ
  • āĻĒāĻžāϏ āĻ˛ā§āϝāĻžāĻŽā§āĻŦāĻĄāĻžāϏ āϝāĻž āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āĻĻā§āĻŦāĻžāϰāĻž āĻŸā§āϰāĻŋāĻ—āĻžāϰ āĻ•āϰāĻž āωāϚāĻŋāϤ, āĻŦāϰāĻ‚ NavController āύāĻŋāĻœā§‡āχāĨ¤

āωāĻĻāĻžāĻšāϰāĻŖāĻ¸ā§āĻŦāϰ⧂āĻĒ, āĻāĻ•āϟāĻŋ ProfileScreen āĻ•āĻŽā§āĻĒā§‹āϜāϝ⧋āĻ—ā§āϝ āϝāĻž āĻāĻ•āϟāĻŋ userId āχāύāĻĒ⧁āϟ āĻšāĻŋāϏāĻžāĻŦ⧇ āύ⧇āϝāĻŧ āĻāĻŦāĻ‚ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āĻĻ⧇āϰ āĻŦāĻ¨ā§āϧ⧁āϰ āĻĒā§āϰ⧋āĻĢāĻžāχāϞ āĻĒ⧃āĻˇā§āĻ āĻžāϝāĻŧ āύ⧇āĻ­āĻŋāϗ⧇āϟ āĻ•āϰāĻžāϰ āĻ…āύ⧁āĻŽāϤāĻŋ āĻĻ⧇āϝāĻŧ āĻāϰ āĻ¸ā§āĻŦāĻžāĻ•ā§āώāϰ āĻĨāĻžāĻ•āϤ⧇ āĻĒāĻžāϰ⧇:

@Composable
fun ProfileScreen(
    userId: String,
    navigateToFriendProfile: (friendUserId: String) -> Unit
) {
 â€Ļ
}

āĻāχāĻ­āĻžāĻŦ⧇, ProfileScreen āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻĨ⧇āϕ⧇ āĻ¸ā§āĻŦāĻžāϧ⧀āύāĻ­āĻžāĻŦ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇, āĻāϟāĻŋ āĻ¸ā§āĻŦāĻžāϧ⧀āύāĻ­āĻžāĻŦ⧇ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāĻžāϰ āĻ…āύ⧁āĻŽāϤāĻŋ āĻĻ⧇āϝāĻŧāĨ¤ composable āĻ˛ā§āϝāĻžāĻŽā§āĻŦāĻĄāĻž āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻāĻĒāĻŋāφāχ āĻāĻŦāĻ‚ āφāĻĒāύāĻžāϰ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻŦā§āϝāĻŦāϧāĻžāύ āĻĒā§‚āϰāĻŖ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻĒā§āϰāϝāĻŧā§‹āϜāύ⧀āϝāĻŧ āĻ¨ā§āϝ⧂āύāϤāĻŽ āϞāϜāĻŋāĻ•āϕ⧇ āĻāύāĻ•ā§āϝāĻžāĻĒāϏ⧁āϞ⧇āϟ āĻ•āϰāĻŦ⧇:

@Serializable data class Profile(id: String)

composable<Profile> { backStackEntry ->
    val profile = backStackEntry.toRoute<Profile>()
    ProfileScreen(userId = profile.id) { friendUserId ->
        navController.navigate(route = Profile(id = friendUserId))
    }
}

NavHost āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰ⧇ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻĒā§āϰāϝāĻŧā§‹āϜāύ⧀āϝāĻŧāϤāĻžāϗ⧁āϞāĻŋ āĻ•āĻ­āĻžāϰ āĻ•āϰ⧇ āĻāĻŽāύ āĻĒāϰ⧀āĻ•ā§āώāĻžāϗ⧁āϞāĻŋ āϞāĻŋāĻ–āϤ⧇ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āĻ•āϰāĻž āĻšāϝāĻŧ, āφāĻĒāύāĻžāϰ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ āĻāĻŦāĻ‚ āφāĻĒāύāĻžāϰ āĻĒ⧃āĻĨāĻ• āĻ¸ā§āĻ•ā§āϰ⧀āύ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞāϗ⧁āϞāĻŋāϤ⧇ āĻĒāĻžāϏ āĻ•āϰāĻž āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ…ā§āϝāĻžāĻ•āĻļāύāϗ⧁āϞāĻŋāĨ¤

NavHost āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāĻž āĻšāĻšā§āϛ⧇

āφāĻĒāύāĻžāϰ NavHost āĻĒāϰ⧀āĻ•ā§āώāĻž āĻļ⧁āϰ⧁ āĻ•āϰāϤ⧇, āύāĻŋāĻŽā§āύāϞāĻŋāĻ–āĻŋāϤ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ-āĻĒāϰ⧀āĻ•ā§āώāĻž āύāĻŋāĻ°ā§āĻ­āϰāϤāĻž āϝ⧋āĻ— āĻ•āϰ⧁āύ:

dependencies {
// ...
  androidTestImplementation "androidx.navigation:navigation-testing:$navigationVersion"
  // ...
}

āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ⧇āϰ NavHost āĻāĻ•āϟāĻŋ āĻ•āĻŽā§āĻĒā§‹āĻœā§‡āĻŦāϞ⧇ āĻŽā§‹āĻĄāĻŧāĻžāύ⧋ āϝāĻž āĻāĻ•āϟāĻŋ NavHostController āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāϟāĻžāϰ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻ—ā§āϰāĻšāĻŖ āĻ•āϰ⧇āĨ¤

@Composable
fun AppNavHost(navController: NavHostController){
  NavHost(navController = navController){ ... }
}

āĻ¨ā§āϝāĻžāĻ­āĻŋāϗ⧇āĻļāύ āĻŸā§‡āĻ¸ā§āϟāĻŋāĻ‚ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ TestNavHostController āĻāϰ āĻāĻ•āϟāĻŋ āωāĻĻāĻžāĻšāϰāĻŖ āĻĒāĻžāϏ āĻ•āϰ⧇ āĻāĻ–āύ āφāĻĒāύāĻŋ AppNavHost āĻāĻŦāĻ‚ NavHost āĻ­āĻŋāϤāϰ⧇ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āϏāĻŽāĻ¸ā§āϤ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϞāϜāĻŋāĻ• āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤ āĻāĻ•āϟāĻŋ UI āĻĒāϰ⧀āĻ•ā§āώāĻž āϝāĻž āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻļ⧁āϰ⧁āϰ āĻ—āĻ¨ā§āϤāĻŦā§āϝ āϝāĻžāϚāĻžāχ āĻ•āϰ⧇ āĻāĻŦāĻ‚ NavHost āĻĻ⧇āĻ–āϤ⧇ āĻāχāϰāĻ•āĻŽ āĻšāĻŦ⧇:

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()
    lateinit var navController: TestNavHostController

    @Before
    fun setupAppNavHost() {
        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            AppNavHost(navController = navController)
        }
    }

    // Unit test
    @Test
    fun appNavHost_verifyStartDestination() {
        composeTestRule
            .onNodeWithContentDescription("Start Screen")
            .assertIsDisplayed()
    }
}

āĻ¨ā§āϝāĻžāĻ­āĻŋāϗ⧇āĻļāύ āĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāĻž āĻšāĻšā§āϛ⧇

āφāĻĒāύāĻŋ āĻāĻ•āĻžāϧāĻŋāĻ• āωāĻĒāĻžāϝāĻŧ⧇ āφāĻĒāύāĻžāϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦāĻžāĻ¸ā§āϤāĻŦāĻžāϝāĻŧāύ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ, UI āωāĻĒāĻžāĻĻāĻžāύāϗ⧁āϞāĻŋāϤ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧇ āĻāĻŦāĻ‚ āϤāĻžāϰāĻĒāϰ⧇ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻ—āĻ¨ā§āϤāĻŦā§āϝ āϝāĻžāϚāĻžāχ āĻ•āϰ⧇ āĻŦāĻž āĻŦāĻ°ā§āϤāĻŽāĻžāύ āϰ⧁āĻŸā§‡āϰ āϏāĻžāĻĨ⧇ āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ āϰ⧁āĻŸā§‡āϰ āϤ⧁āϞāύāĻž āĻ•āϰ⧇āĨ¤

āϝ⧇āĻšā§‡āϤ⧁ āφāĻĒāύāĻŋ āφāĻĒāύāĻžāϰ āĻ•āĻ‚āĻ•ā§āϰāĻŋāϟ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻŦāĻžāĻ¸ā§āϤāĻŦāĻžāϝāĻŧāύ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āϚāĻžāύ, āϤāĻžāχ UI-āϤ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰāĻž āĻŦāĻžāĻžā§āĻ›āύ⧀āϝāĻŧāĨ¤ āĻŦāĻŋāĻšā§āĻ›āĻŋāĻ¨ā§āύāĻ­āĻžāĻŦ⧇ āĻĒ⧃āĻĨāĻ• āϏāĻ‚āĻŽāĻŋāĻļā§āϰāĻŖāϝ⧋āĻ—ā§āϝ āĻĢāĻžāĻ‚āĻļāύāϗ⧁āϞāĻŋāϰ āĻĒāĻžāĻļāĻžāĻĒāĻžāĻļāĻŋ āĻāϟāĻŋ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāĻž āϝāĻžāϝāĻŧ āϤāĻž āĻļāĻŋāĻ–āϤ⧇, āĻœā§‡āϟāĻĒā§āϝāĻžāĻ• āĻ•āĻŽā§āĻĒā§‹āϜ āϕ⧋āĻĄāĻ˛ā§āϝāĻžāĻŦ⧇ āĻĒāϰ⧀āĻ•ā§āώāĻžāϟāĻŋ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰ⧇ āĻĻ⧇āϖ⧁āύāĨ¤

āĻāĻ›āĻžāĻĄāĻŧāĻžāĻ“ āφāĻĒāύāĻŋ navController āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻŦāĻ°ā§āϤāĻŽāĻžāύ āϰ⧁āϟāϟāĻŋāϕ⧇ āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ āϰ⧁āĻŸā§‡āϰ āϏāĻžāĻĨ⧇ āϤ⧁āϞāύāĻž āĻ•āϰ⧇, navController āĻāϰ currentBackStackEntry āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āφāĻĒāύāĻžāϰ āĻĻāĻžāĻŦāĻŋāϗ⧁āϞāĻŋ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ:

@Test
fun appNavHost_clickAllProfiles_navigateToProfiles() {
    composeTestRule.onNodeWithContentDescription("All Profiles")
        .performScrollTo()
        .performClick()

    assertTrue(navController.currentBackStackEntry?.destination?.hasRoute<Profile>() ?: false)
}

āĻ•āĻŽā§āĻĒā§‹āϜ āĻŸā§‡āĻ¸ā§āϟāĻŋāĻ‚ āĻŦ⧇āϏāĻŋāĻ• āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āύāĻŋāĻ°ā§āĻĻ⧇āĻļāύāĻžāϰ āϜāĻ¨ā§āϝ, āφāĻĒāύāĻžāϰ āĻ•āĻŽā§āĻĒā§‹āϜ āϞ⧇āφāωāϟ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāĻž āĻāĻŦāĻ‚ āĻœā§‡āϟāĻĒā§āϝāĻžāĻ• āĻ•āĻŽā§āĻĒā§‹āϜ āϕ⧋āĻĄāĻ˛ā§āϝāĻžāĻŦ⧇ āĻŸā§‡āĻ¸ā§āϟāĻŋāĻ‚ āĻĻ⧇āϖ⧁āύāĨ¤ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϕ⧋āĻĄā§‡āϰ āωāĻ¨ā§āύāϤ āĻĒāϰ⧀āĻ•ā§āώāĻžāϰ āĻŦāĻŋāώāϝāĻŧ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇, āĻŸā§‡āĻ¸ā§āϟ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ—āĻžāχāĻĄ āĻĻ⧇āϖ⧁āύāĨ¤

āφāϰāĻ“ āϜāĻžāύ⧁āύ

āĻœā§‡āϟāĻĒā§āϝāĻžāĻ• āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇, āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ āĻĻāĻŋāϝāĻŧ⧇ āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύ āĻĻ⧇āϖ⧁āύ āĻŦāĻž āĻœā§‡āϟāĻĒā§āϝāĻžāĻ• āĻ•āĻŽā§āĻĒā§‹āϜ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϕ⧋āĻĄāĻ˛ā§āϝāĻžāĻŦ āύāĻŋāύāĨ¤

āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻĄāĻŋāϜāĻžāχāύ āĻ•āϰāĻŦ⧇āύ āϤāĻž āĻļāĻŋāĻ–āϤ⧇ āϝāĻžāϤ⧇ āĻāϟāĻŋ āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ āĻ¸ā§āĻ•ā§āϰ⧀āύ⧇āϰ āφāĻ•āĻžāϰ, āĻ…āĻ­āĻŋāϝ⧋āϜāύ āĻāĻŦāĻ‚ āĻĢāĻ°ā§āĻŽ āĻĢā§āϝāĻžāĻ•ā§āϟāϰāϗ⧁āϞāĻŋāϰ āϏāĻžāĻĨ⧇ āĻ–āĻžāĻĒ āĻ–āĻžāϝāĻŧ, āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāĻļā§€āϞ UI āĻāϰ āϜāĻ¨ā§āϝ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻĻ⧇āϖ⧁āύāĨ¤

āύ⧇āĻ¸ā§āĻŸā§‡āĻĄ āĻ—ā§āϰāĻžāĻĢ āĻāĻŦāĻ‚ āύāĻŋāĻšā§‡āϰ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦāĻžāϰ āχāĻ¨ā§āϟāĻŋāĻ—ā§āϰ⧇āĻļāύ⧇āϰ āĻŽāϤ āϧāĻžāϰāĻŖāĻž āϏāĻš āĻāĻ•āϟāĻŋ āĻŽāĻĄā§āϞāĻžāϰāĻžāχāϜāĻĄ āĻ…ā§āϝāĻžāĻĒ⧇ āφāϰāĻ“ āωāĻ¨ā§āύāϤ āĻ•āĻŽā§āĻĒā§‹āϜ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦāĻžāĻ¸ā§āϤāĻŦāĻžāϝāĻŧāύ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āϜāĻžāύāϤ⧇, GitHub-āĻ Now in Android āĻ…ā§āϝāĻžāĻĒāϟāĻŋ āĻĻ⧇āϖ⧁āύāĨ¤

āύāĻŽā§āύāĻž

{% āĻļāĻŦā§āĻĻāĻžāĻ°ā§āĻĨ⧇ %} {% endverbatim %} {% āĻļāĻŦā§āĻĻāĻžāĻ°ā§āĻĨ⧇ %} {% endverbatim %}