রচনা সহ নেভিগেশন

নেভিগেশন কম্পোনেন্ট জেটপ্যাক কম্পোজ অ্যাপ্লিকেশনের জন্য সমর্থন প্রদান করে। নেভিগেশন উপাদানের পরিকাঠামো এবং বৈশিষ্ট্যগুলির সুবিধা গ্রহণ করার সময় আপনি কম্পোজেবলগুলির মধ্যে নেভিগেট করতে পারেন।

সেটআপ

রচনা সমর্থন করতে, আপনার অ্যাপ মডিউলের build.gradle ফাইলে নিম্নলিখিত নির্ভরতা ব্যবহার করুন:

গ্রোভি

dependencies {
    def nav_version = "2.8.2"

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

কোটলিন

dependencies {
    val nav_version = "2.8.2"

    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.7.3"
}

android {
    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.15"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

কোটলিন

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

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 %}