নেভিগেশন কম্পোনেন্টটি জেটপ্যাক কম্পোজ অ্যাপ্লিকেশনগুলোকে সমর্থন করে। আপনি নেভিগেশন কম্পোনেন্টের পরিকাঠামো এবং বৈশিষ্ট্যগুলোর সুবিধা নিয়ে কম্পোজেবলগুলোর মধ্যে নেভিগেট করতে পারেন।
বিশেষভাবে Compose-এর জন্য নির্মিত সর্বশেষ প্রি-রিলিজ নেভিগেশন লাইব্রেরির জন্য, Navigation 3 ডকুমেন্টেশন দেখুন।
সেটআপ
Compose সমর্থন করার জন্য, আপনার অ্যাপ মডিউলের build.gradle ফাইলে নিম্নলিখিত ডিপেন্ডেন্সিটি ব্যবহার করুন:
গ্রুভি
dependencies { def nav_version = "2.9.8" implementation "androidx.navigation:navigation-compose:$nav_version" }
কোটলিন
dependencies { val nav_version = "2.9.8" implementation("androidx.navigation:navigation-compose:$nav_version") }
শুরু করুন
অ্যাপে নেভিগেশন প্রয়োগ করার সময়, একটি নেভিগেশন হোস্ট, গ্রাফ এবং কন্ট্রোলার বাস্তবায়ন করুন। আরও তথ্যের জন্য, নেভিগেশন ওভারভিউ দেখুন।
একটি নেভকন্ট্রোলার তৈরি করুন
Compose-এ কীভাবে একটি NavController তৈরি করতে হয়, সে সম্পর্কে তথ্যের জন্য, "Create a navigation controller" -এর Compose বিভাগটি দেখুন।
একটি নেভহোস্ট তৈরি করুন
Compose-এ কীভাবে একটি NavHost তৈরি করতে হয় সে সম্পর্কে তথ্যের জন্য, "আপনার নেভিগেশন গ্রাফ ডিজাইন করুন" এর "Compose" বিভাগটি দেখুন।
একটি রচনাযোগ্য স্থানে নেভিগেট করুন
একটি কম্পোজেবল-এ নেভিগেট করার তথ্যের জন্য, আর্কিটেকচার ডকুমেন্টেশনের ' নেভিগেট টু এ ডেস্টিনেশন' অংশটি দেখুন।
আর্গুমেন্ট দিয়ে নেভিগেট করুন
কম্পোজেবল ডেস্টিনেশনগুলোর মধ্যে আর্গুমেন্ট আদান-প্রদান করার তথ্যের জন্য, “আপনার নেভিগেশন গ্রাফ ডিজাইন করুন” -এর “কম্পোজ” অংশটি দেখুন।
নেভিগেট করার সময় জটিল ডেটা পুনরুদ্ধার করুন
নেভিগেট করার সময় জটিল ডেটা অবজেক্ট ব্যবহার না করার জন্য দৃঢ়ভাবে পরামর্শ দেওয়া হচ্ছে; এর পরিবর্তে, নেভিগেশন অ্যাকশন সম্পাদন করার সময় আর্গুমেন্ট হিসেবে ন্যূনতম প্রয়োজনীয় তথ্য, যেমন একটি অনন্য শনাক্তকারী বা অন্য কোনো ধরনের আইডি, ব্যবহার করুন।
// 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)
}
এই ডিপ লিঙ্কগুলি আপনাকে একটি কম্পোজেবলের সাথে একটি নির্দিষ্ট URL, অ্যাকশন বা মাইম টাইপ যুক্ত করতে দেয়। ডিফল্টরূপে, এই ডিপ লিঙ্কগুলি বাহ্যিক অ্যাপের কাছে উন্মুক্ত থাকে না। এই ডিপ লিঙ্কগুলিকে বাহ্যিকভাবে উপলব্ধ করতে, আপনাকে আপনার অ্যাপের 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)
}
এরপর আপনি এই deepLinkPendingIntent অন্য যেকোনো PendingIntent মতোই ব্যবহার করে আপনার অ্যাপটিকে ডিপ লিঙ্কের গন্তব্যে খুলতে পারবেন।
নেস্টেড নেভিগেশন
নেস্টেড নেভিগেশন গ্রাফ কীভাবে তৈরি করতে হয় সে সম্পর্কে তথ্যের জন্য, নেস্টেড গ্রাফ দেখুন।
একটি অভিযোজিত নেভিগেশন বার এবং নেভিগেশন রেল তৈরি করুন
NavigationSuiteScaffold আপনার অ্যাপের বর্তমান WindowSizeClass উপর ভিত্তি করে উপযুক্ত শীর্ষ-স্তরের নেভিগেশন UI প্রদর্শন করে। ছোট স্ক্রিনের জন্য, স্ক্যাফোল্ডটি একটি বটম নেভিগেশন বার দেখায়; আর মাঝারি এবং বড় স্ক্রিনের জন্য, একটি নেভিগেশন রেইল দেখায়।
NavigationSuiteScaffold প্রাথমিক নেভিগেশন পরিচালনা করে; তবে, অ্যাডাপ্টিভ লেআউটগুলিতে প্রায়শই অন্যান্য বিশেষায়িত কম্পোজেবল অন্তর্ভুক্ত থাকে। লিস্ট-ডিটেইল এবং সাপোর্টিং পেইন ক্যানোনিকাল লেআউটের জন্য, যা অ্যাডাপ্টিভ ডিজাইনে সাধারণ, যথাক্রমে ListDetailPaneScaffold এবং SupportingPaneScaffold ব্যবহার করুন। আরও তথ্যের জন্য, "Build adaptive layouts" দেখুন।
আন্তঃকার্যক্ষমতা
আপনি যদি Compose-এর সাথে Navigation কম্পোনেন্টটি ব্যবহার করতে চান, তাহলে আপনার কাছে দুটি বিকল্প রয়েছে:
- ফ্র্যাগমেন্টগুলির জন্য নেভিগেশন কম্পোনেন্ট ব্যবহার করে একটি নেভিগেশন গ্রাফ নির্ধারণ করুন।
- Compose ডেস্টিনেশন ব্যবহার করে Compose-এ একটি
NavHostসহ একটি নেভিগেশন গ্রাফ সংজ্ঞায়িত করুন। এটি কেবল তখনই সম্ভব, যদি নেভিগেশন গ্রাফের সমস্ত স্ক্রিন কম্পোজেবল হয়।
অতএব, Compose এবং Views উভয়ই ব্যবহার করা হয় এমন অ্যাপের জন্য Fragment-based Navigation কম্পোনেন্ট ব্যবহার করার পরামর্শ দেওয়া হয়। এক্ষেত্রে Fragment-গুলো View-based স্ক্রিন, Compose স্ক্রিন এবং Views ও Compose উভয়ই ব্যবহার করা স্ক্রিনগুলোকে ধারণ করবে। প্রতিটি Fragment-এর কন্টেন্ট Compose-এ চলে আসার পর, পরবর্তী ধাপ হলো Navigation Compose ব্যবহার করে সেই সমস্ত স্ক্রিনকে একত্রিত করা এবং সমস্ত Fragment মুছে ফেলা।
ফ্র্যাগমেন্টের জন্য নেভিগেশন ব্যবহার করে কম্পোজ থেকে নেভিগেট করুন
Compose কোডের ভিতরে গন্তব্য পরিবর্তন করার জন্য, আপনাকে এমন ইভেন্টগুলো প্রকাশ করতে হবে যা হায়ারার্কির যেকোনো কম্পোজেবলের কাছে পাঠানো এবং তার দ্বারা ট্রিগার করা যেতে পারে:
@Composable
fun MyScreen(onNavigate: (Int) -> Unit) {
Button(onClick = { onNavigate(R.id.nav_profile) } { /* ... */ }
}
আপনার ফ্র্যাগমেন্টে, আপনি NavController খুঁজে বের করে এবং গন্তব্যে নেভিগেট করার মাধ্যমে Compose এবং ফ্র্যাগমেন্ট-ভিত্তিক নেভিগেশন কম্পোনেন্টের মধ্যে সংযোগ স্থাপন করেন:
override fun onCreateView( /* ... */ ) {
setContent {
MyScreen(onNavigate = { dest -> findNavController().navigate(dest) })
}
}
বিকল্পভাবে, আপনি আপনার Compose হায়ারার্কিতে NavController পাস করতে পারেন। তবে, ফাংশন এক্সপোজ করা অনেক বেশি পুনঃব্যবহারযোগ্য এবং পরীক্ষাযোগ্য।
পরীক্ষা
আপনার কম্পোজেবল ডেস্টিনেশনগুলো থেকে ন্যাভিগেশন কোডকে বিচ্ছিন্ন করুন, যাতে প্রতিটি কম্পোজেবলকে NavHost কম্পোজেবল থেকে আলাদাভাবে পরীক্ষা করা যায়।
এর মানে হলো, আপনার কোনো কম্পোজেবলে সরাসরি navController পাস করা উচিত নয়, বরং নেভিগেশন কলব্যাকগুলোকে প্যারামিটার হিসেবে পাস করতে হবে। এর ফলে আপনার সমস্ত কম্পোজেবল আলাদাভাবে পরীক্ষাযোগ্য হয়, কারণ টেস্টের জন্য সেগুলোর navController এর কোনো ইনস্ট্যান্সের প্রয়োজন হয় না।
composable ল্যাম্বডা দ্বারা প্রদত্ত ইনডিরেকশনের স্তরই আপনাকে আপনার নেভিগেশন কোডকে কম্পোজেবলটি থেকে আলাদা করতে দেয়। এটি দুই দিকে কাজ করে:
- আপনার কম্পোজেবলে শুধুমাত্র পার্স করা আর্গুমেন্টগুলো পাস করুন।
- এমন ল্যাম্বডা পাস করুন যা নেভিগেট করার জন্য সরাসরি
NavControllerপরিবর্তে কম্পোজেবল দ্বারা ট্রিগার হবে।
উদাহরণস্বরূপ, একটি ProfileScreen কম্পোজেবল যা ইনপুট হিসেবে একটি userId গ্রহণ করে এবং ব্যবহারকারীদের বন্ধুর প্রোফাইল পৃষ্ঠায় যেতে দেয়, তার সিগনেচারটি নিম্নরূপ হতে পারে:
@Composable
fun ProfileScreen(
userId: String,
navigateToFriendProfile: (friendUserId: String) -> Unit
) {
…
}
এইভাবে, ProfileScreen কম্পোজেবলটি Navigation থেকে স্বাধীনভাবে কাজ করে, ফলে এটিকে স্বাধীনভাবে পরীক্ষা করা যায়। composable ল্যাম্বডাটি Navigation API এবং আপনার কম্পোজেবলের মধ্যেকার ব্যবধান পূরণের জন্য প্রয়োজনীয় ন্যূনতম লজিককে এনক্যাপসুলেট করবে:
@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 একটি composable-এর মধ্যে রাখুন, যা প্যারামিটার হিসেবে একটি NavHostController গ্রহণ করে।
@Composable
fun AppNavHost(navController: NavHostController){
NavHost(navController = navController){ ... }
}
এখন আপনি নেভিগেশন টেস্টিং আর্টিফ্যাক্ট TestNavHostController এর একটি ইনস্ট্যান্স পাস করে AppNavHost এবং NavHost ভেতরে সংজ্ঞায়িত সমস্ত নেভিগেশন লজিক পরীক্ষা করতে পারেন। আপনার অ্যাপ এবং NavHost এর শুরুর গন্তব্য যাচাই করে এমন একটি UI টেস্ট দেখতে এইরকম হবে:
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-তে ক্লিক করাই শ্রেয়। স্বতন্ত্র কম্পোজেবল ফাংশনগুলোর পাশাপাশি এটি কীভাবে পরীক্ষা করতে হয় তা শিখতে, অবশ্যই “ Testing in Jetpack Compose” কোডল্যাবটি দেখুন।
এছাড়াও আপনি navController এর navController ব্যবহার করে বর্তমান রুটের সাথে প্রত্যাশিত রুটের তুলনা করে আপনার অ্যাসারশনগুলো যাচাই করতে পারেন currentBackStackEntry
@Test
fun appNavHost_clickAllProfiles_navigateToProfiles() {
composeTestRule.onNodeWithContentDescription("All Profiles")
.performScrollTo()
.performClick()
assertTrue(navController.currentBackStackEntry?.destination?.hasRoute<Profile>() ?: false)
}
Compose টেস্টিং-এর প্রাথমিক বিষয়গুলো সম্পর্কে আরও নির্দেশনার জন্য, “Testing your Compose layout” এবং “ Testing in Jetpack Compose” কোডল্যাবটি দেখুন। নেভিগেশন কোডের উন্নত টেস্টিং সম্পর্কে আরও জানতে, “Test Navigation” গাইডটি দেখুন।
আরও জানুন
Jetpack Navigation সম্পর্কে আরও জানতে, "Get started with the Navigation component" দেখুন অথবা Jetpack Compose Navigation কোডল্যাবটি করুন।
আপনার অ্যাপের নেভিগেশন কীভাবে ডিজাইন করবেন যাতে এটি বিভিন্ন স্ক্রিনের আকার, ওরিয়েন্টেশন এবং ফর্ম ফ্যাক্টরের সাথে খাপ খাইয়ে নিতে পারে, তা জানতে “রেসপন্সিভ UI-এর জন্য নেভিগেশন” দেখুন।
একটি মডিউলারাইজড অ্যাপে আরও উন্নত কম্পোজ নেভিগেশন ইমপ্লিমেন্টেশন, যার মধ্যে নেস্টেড গ্রাফ এবং বটম নেভিগেশন বার ইন্টিগ্রেশনের মতো ধারণাগুলো অন্তর্ভুক্ত, সে সম্পর্কে জানতে গিটহাবে থাকা ‘Now in Android’ অ্যাপটি দেখুন।
নমুনা
আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলেও লিঙ্কের লেখা প্রদর্শিত হয়।
- কম্পোজে ম্যাটেরিয়াল ডিজাইন ২
- Jetpack Navigation-কে Navigation Compose-এ স্থানান্তর করুন
- কোথায় রাজ্য উত্তোলন করতে হবে