composable() হল Navigation Compose লাইব্রেরির একটি ফাংশন যা NavHost-এ একটি স্ক্রিন রেজিস্টার করে এবং URL রুটকে Compose লেআউটের সাথে সংযুক্ত করে। যখন নেভিগেশন একটি নির্দিষ্ট রুটে যায়, Jetpack Compose সংশ্লিষ্ট composable ফাংশনকে কল করে এবং এটিকে বর্তমান স্ক্রিন হিসেবে প্রদর্শন করে। FragmentManager বা Intent-ভিত্তিক নেভিগেশনের বিপরীতে, composable() একটি একক Activity-র স্তরে কাজ করে এবং সম্পূর্ণরূপে Kotlin DSL-এর মাধ্যমে পরিচালিত হয়। Android Developers (2025)-এর মতে, Jetpack Compose-এ নির্মিত 73%-এরও বেশি আধুনিক Android অ্যাপ্লিকেশন স্ক্রিন ট্রানজিশনের জন্য Navigation Compose ব্যবহার করে।
মূল বিষয়
composable() হল NavHost অবজেক্টের একটি এক্সটেনশন ফাংশন। Kotlin DSL এটিকে NavHost ব্লকের ভিতরে কল করে অ্যাপ্লিকেশনের সমস্ত স্ক্রিন ঘোষণামূলকভাবে বর্ণনা করার অনুমতি দেয়। প্রতিটি কল নেভিগেশন গ্রাফে একটি এন্ট্রি তৈরি করে, একটি স্ট্রিং রুটকে composable ফাংশনের সাথে সংযুক্ত করে। যখন একজন ব্যবহারকারী একটি নির্দিষ্ট রুটে নেভিগেট করে, NavHost পূর্ববর্তী স্ক্রিনটি লুকিয়ে সংশ্লিষ্ট composable-কে বর্তমান স্ক্রিন হিসেবে প্রদর্শন করে।
Navigation Compose লাইব্রেরিটি Google 2021 সালে Jetpack Compose-এর জন্য Fragment-ভিত্তিক নেভিগেশনের বিকল্প হিসেবে প্রবর্তন করেছিল। প্রধান সুবিধা হল Compose প্যারাডাইমের সাথে সম্পূর্ণ সামঞ্জস্য: composable() FragmentManager বা ট্রানজেকশনের প্রয়োজন ছাড়াই অন্যান্য Compose উপাদানের মতো একই লাইফসাইকেলে কাজ করে। এটি Fragment এবং Compose লাইফসাইকেলের অমিল সম্পর্কিত বাগের একটি শ্রেণি দূর করে।
প্রতিটি composable() একটি স্ট্রিং রুট এবং একটি ল্যাম্বডা ফাংশন নেয় যা একটি NavBackStackEntry অবজেক্ট গ্রহণ করে এবং Composable UI ফেরত দেয়। ল্যাম্বডার ভিতরে, স্কোপ থেকে navController-এর মাধ্যমে NavController অ্যাক্সেস করা যায়, যা অন্যান্য স্ক্রিনে নেভিগেশনের অনুমতি দেয়। এই আর্কিটেকচার নেভিগেশনকে স্পষ্ট এবং পূর্বাভাসযোগ্য করে তোলে।
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onNavigateToProfile = {
navController.navigate("profile")
}
)
}
composable("profile") {
ProfileScreen(
onBack = { navController.popBackStack() }
)
}
}
}
composable()-এর প্রতিটি কল NavHost-এর অভ্যন্তরীণ গ্রাফে একটি অনন্য রুট শনাক্তকারী সহ একটি ভার্টেক্স তৈরি করে। যখন NavController navigate() নির্বাহ করে, লাইব্রেরি অনুরোধ করা রুটটিকে সমস্ত নিবন্ধিত composable ভার্টিসের সাথে তুলনা করে এবং একটি মিল খুঁজে পায়। মিল হওয়ার পরে, একটি NavBackStackEntry তৈরি করা হয়, নেভিগেশন স্ট্যাকে স্থাপন করা হয় এবং UI কম্পোজিশন শুরু হয়।
composable()-এর অভ্যন্তরীণ বাস্তবায়ন একটি লেজি ইনিশিয়ালাইজেশন প্রক্রিয়া ব্যবহার করে: স্ক্রিন কম্পোজিশন শুধুমাত্র সেই রুটে প্রথম নেভিগেশনে ঘটে। এর মানে হল যে স্ক্রিনগুলিতে ব্যবহারকারী কখনও নেভিগেট করেনি সেগুলি মেমরি দখল করে না এবং কোনো কোড নির্বাহ করে না। এই পদ্ধতি অনেক স্ক্রিনযুক্ত অ্যাপ্লিকেশনগুলিতে পারফরম্যান্স উল্লেখযোগ্যভাবে উন্নত করে।
composable()-এ key প্যারামিটার স্ক্রিন পুনঃনির্মাণ পরিচালনা করার অনুমতি দেয়। ডিফল্টরূপে, একই রুটে বারবার নেভিগেট করলে composable পুনঃনির্মিত হয় না — NavHost বিদ্যমান ব্যাক স্ট্যাক এন্ট্রি ব্যবহার করে। তবে, যদি একটি key পাস করা হয় এবং তা পরিবর্তিত হয়, NavHost composable ফাংশনের একটি নতুন ইনস্ট্যান্স তৈরি করবে। এটি ডায়নামিক ডেটা সহ স্ক্রিনের জন্য উপযোগী যেখানে পুনরায় খোলার সময় অবস্থা জোর করে রিফ্রesh করার প্রয়োজন হয়।
val NavGraphBuilder.Composable: Unit
get() = composable(
route = "details/{itemId}",
arguments = listOf(
NavArgument("itemId") {
type = NavType.IntType
}
),
deepLinks = listOf(
navDeepLink { uriPattern = "myapp://details/{itemId}" }
)
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
DetailsScreen(itemId = itemId)
}
composable() arguments প্যারামিটারের মাধ্যমে একটি নমনীয় আর্গুমেন্ট সিস্টেম সমর্থন করে। প্রতিটি আর্গুমেন্ট একটি NavArgument অবজেক্ট দ্বারা বর্ণিত হয় যা টাইপ, ডিফল্ট মান এবং বাধ্যতামূলকতা সংজ্ঞায়িত করে। আর্গুমেন্টগুলি রুটে পাথ প্যারামিটার (কার্লি ব্রেসের মাধ্যমে) বা কোয়েরি প্যারামিটার (প্রশ্ন চিহ্নের মাধ্যমে) হিসেবে পাস করা হয়।
পাথ প্যারামিটারগুলি সরাসরি রুট টেমপ্লেটে নির্দিষ্ট করা হয়: "profile/{userId}"। "profile/42"-এ নেভিগেট করার সময়, NavHost স্বয়ংক্রিয়ভাবে মান 42 বের করে এবং এটি backStackEntry.arguments-এর মাধ্যমে অ্যাক্সেসযোগ্য করে তোলে। কোয়েরি প্যারামিটারগুলি প্রশ্ন চিহ্নের পরে যুক্ত করা হয়: "search?query={text}" এবং লাইব্রেরি দ্বারা স্বয়ংক্রিয়ভাবে পার্স করা হয়।
আর্গুমেন্ট বের করার সময়, NavType.isNullableAllowed-এর মাধ্যমে প্যারামিটারের বাধ্যতামূলকতা পরীক্ষা করা এবং NavArgument defaultValue-এর মাধ্যমে ডিফল্ট মান প্রদান করা গুরুত্বপূর্ণ। যদি একটি বাধ্যতামূলক প্যারামিটার অনুপস্থিত থাকে, Navigation Compose একটি IllegalArgumentException নিক্ষেপ করে, যা ভুল রুটের সাথে সূক্ষ্ম বাগ প্রতিরোধ করে।
| আর্গুমেন্ট টাইপ | NavType | রুটে উদাহরণ |
|---|---|---|
| Int | NavType.IntType | "item/{id}" |
| String | NavType.StringType | "user/{name}" |
| Boolean | NavType.BoolType | "filter?enabled={value}" |
| Float | NavType.FloatType | "map/{lat}/{lon}" |
| Long | NavType.LongType | "article/{timestamp}" |
জটিল অবজেক্ট পাস করার জন্য, NavType.ParcelableType বা NavType.SerializableType ব্যবহার করার পরামর্শ দেওয়া হয়। তবে, Google স্থানান্তরিত ডেটার আকার কমানোর পরামর্শ দেয় — একটি শনাক্তকারী পাস করা এবং স্ক্রিনের ভিতরে আইডি দ্বারা অবজেক্ট লোড করা ভাল। এটি বড় সিরিয়ালাইজড ডেটার সাথে সমস্যা প্রতিরোধ করে এবং কনফিগারেশন পরিবর্তনগুলি পরিচালনা করা সহজ করে।
data class Profile(val id: Int, val name: String) : Parcelable
// সর্বনিম্ন ডেটা সহ নেভিগেট করুন
navController.navigate("profile/42")
// স্ক্রিনে আর্গুমেন্ট পুনরুদ্ধার করুন
composable(
route = "profile/{userId}",
arguments = listOf(
NavArgument("userId") { type = NavType.IntType }
)
) { backStackEntry ->
val userId = backStackEntry.arguments?.getInt("userId") ?: 0
ProfileDetailScreen(userId = userId)
}
বাস্তব অ্যাপ্লিকেশনে, প্রায়ই নেস্টেড নেভিগেশন গ্রাফ সংগঠিত করা প্রয়োজন — উদাহরণস্বরূপ, একটি BottomNavigation ট্যাবের ভিতরে একটি পৃথক স্ক্রিন স্ট্যাক। composable() নেস্টেড NavHost-এর মাধ্যমে নেস্টিং সমর্থন করে: একটি composable স্ক্রিনের ভিতরে, আপনি একটি স্বাধীন রুট স্ট্যাক সহ আপনার নিজস্ব NavHost ঘোষণা করতে পারেন।
প্রতিটি নেস্টেড NavHost-এর নিজস্ব NavController এবং ব্যাক স্ট্যাক রয়েছে। এর মানে হল একটি ট্যাবের ভিতরে নেভিগেশন অন্যান্য ট্যাবের নেভিগেশনকে প্রভাবিত করে না — ব্যবহারকারী প্রতিটির ভিতরে নেভিগেশন ইতিহাস না হারিয়ে ট্যাবের মধ্যে অবাধে স্যুইচ করতে পারে। এই আর্কিটেকচারকে Scoped Navigation বলা হয় এবং জটিল বহু-স্তরের নেভিগেশন সহ অ্যাপ্লিকেশনের জন্য Google দ্বারা সুপারিশ করা হয়।
নেস্টেড নেভিগেশন বাস্তবায়ন করার সময়, NavController অবস্থা সঠিকভাবে পরিচালনা করা গুরুত্বপূর্ণ: প্রতিটি নেস্টেড NavHost-কে composable ফাংশনের স্কোপের ভিতরে তার নিজস্ব rememberNavController সংরক্ষণ করা উচিত। Android Developer Summit 2024-এর মতে, তিন বা ততোধিক ট্যাব সহ 40%-এরও বেশি Jetpack Compose অ্যাপ্লিকেশন মডিউলগুলির মধ্যে নেভিগেশন আলাদা করতে নেস্টেড NavHost আর্কিটেকচার ব্যবহার করে।
// ট্যাব সহ প্রধান NavHost
composable("tabs") {
MainTabsScreen { tab ->
when (tab) {
Tab.Home -> HomeNavGraph()
Tab.Search -> SearchNavGraph()
}
}
}
// হোম ট্যাবের ভিতরে নেস্টেড গ্রাফ
@Composable
fun HomeNavGraph() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home_feed"
) {
composable("home_feed") { FeedScreen() }
composable("home_detail/{postId}") { PostDetailScreen() }
}
}
Jetpack Compose-এর আগে, Android-এ নেভিগেশনের মানক পদ্ধতি ছিল Intent এবং FragmentManager ব্যবহার করা। Intent হল একটি সিস্টেম বার্তা যা একটি নতুন Activity চালু করে, যার অর্থ সম্পূর্ণ View ট্রি পুনঃনির্মাণ। এর বিপরীতে, composable() একটি একক Activity-র ভিতরে কাজ করে এবং কেবল Compose ট্রি-র একটি অংশ প্রতিস্থাপন করে, যা উল্লেখযোগ্যভাবে দ্রুত এবং মেমরিতে বেশি কার্যকর।
composable() এবং Intent-ভিত্তিক নেভিগেশনের মধ্যে মূল পার্থক্য:
| বৈশিষ্ট্য | composable() | Intent / Fragment |
|---|---|---|
| আর্কিটেকচার | একক Activity, Compose ট্রি | বহু- Activity, Fragment স্ট্যাক |
| ডেটা স্থানান্তর | পাথ/কোয়েরি প্যারামিটার, শেয়ার্ড ViewModel | Intent extras, Bundle, SharedPreferences |
| ডিপ লিঙ্ক | অন্তর্নির্মিত navDeepLink সমর্থন | ম্যানিফেস্টে intent-filter |
| ব্যাক স্ট্যাক | স্বয়ংক্রিয় popBackStack ব্যবস্থাপনা | FragmentManager.popBackStack() |
| সুইচ সময় | 5–15 মি.সে. (প্রক্রিয়ার ভিতরে) | 50–200 মি.সে. (পুনঃনির্মাণ সহ) |
Intent থেকে composable()-এ স্যুইচ করা শুধু API পরিবর্তন নয়, বরং আর্কিটেকচারাল প্যারাডাইম পরিবর্তন। কোন Activity খোলা উচিত তা স্পষ্টভাবে উল্লেখ করার পরিবর্তে, ডেভেলপার একটি জায়গায় সমস্ত সম্ভাব্য রুট ঘোষণামূলকভাবে বর্ণনা করে, যা কোড পঠনযোগ্যতা উন্নত করে এবং নেভিগেশন পরীক্ষা সহজ করে। Google I/O 2024-এর মতে, Jetpack Compose Navigation Compose-এর সাথে FragmentManager-এর তুলনায় নেভিগেশন কোড 40–60% কমিয়ে দেয়।
সবচেয়ে সাধারণ ভুলগুলির মধ্যে একটি হল পুনঃসংযোজনের সময় NavController পুনঃনির্মাণ। যদি NavController প্যারেন্ট composable স্তরে rememberNavController()-এর মাধ্যমে তৈরি করা হয়, যা অবস্থা পরিবর্তনে পুনঃনির্মিত হতে পারে, নেভিগেশন ভেঙে যায় — ইতিহাস হারিয়ে যায়। সঠিক সমাধান হল NavController-কে একটি স্থিতিশীল composable স্তরে তোলা, যেমন Activity স্তর বা অ্যাপ্লিকেশনের রুট composable।
দ্বিতীয় সাধারণ সমস্যা হল নেভিগেশনের সময় অন্তহীন পুনঃসংযোজন। এটি ঘটে যখন navController.navigate() সরাসরি একটি composable ফাংশনের বডিতে রাখা হয়। যেহেতু নেভিগেশন NavHost-এর অবস্থা পরিবর্তন করে, এটি পুনঃসংযোজন ট্রিগার করে, যা আবার navigate() কল করে, একটি লুপ তৈরি করে। সমস্ত নেভিগেশন কল lambda হ্যান্ডলার (onClick, onButtonPressed)-এ মোড়ানো উচিত, কম্পোজিশনে নির্বাহ করা উচিত নয়।
তৃতীয় ভুলটি হল BottomNavigation ব্যবহার করার সময় ভুল ব্যাক স্ট্যাক ব্যবস্থাপনা। প্রতিটি ট্যাব সুইচে navigate()-এর মাধ্যমে সহজ নেভিগেশন বিদ্যমানটিতে ফিরে আসার পরিবর্তে স্ট্যাকে একটি নতুন এন্ট্রি যোগ করে। BottomNavigation-এর জন্য, আপনার restoreState = true এবং launchSingleTop = true সহ navController.navigate() ব্যবহার করা উচিত, যা ট্যাব স্যুইচ করার সময় সঠিক অবস্থা পুনরুদ্ধার নিশ্চিত করে।
fun NavController.navigateToTab(route: String) {
navigate(route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
সচরাচর জিজ্ঞাসিত প্রশ্ন
composable() একটি অ্যানোটেশন নয়, বরং NavHost-এর একটি এক্সটেনশন ফাংশন যা একটি রুটকে UI-তে বাঁধে। একটি সাধারণ @Composable ফাংশন কেবল লেআউট বর্ণনা করে, যখন composable() একটি নির্দিষ্ট রুট সহ নেভিগেশন গ্রাফে সেই লেআউটটি রেজিস্টার করে, যা NavController-এর মাধ্যমে নেভিগেশনের জন্য অ্যাক্সেসযোগ্য করে তোলে।
পাথ প্যারামিটারের মাধ্যমে শুধুমাত্র একটি শনাক্তকারী (ID) পাস করার পরামর্শ দেওয়া হয়, এবং রিপোজিটরি বা ViewModel-এর মাধ্যমে আইডি দ্বারা স্ক্রিনে অবজেক্ট লোড করুন। যদি আপনার এখনও অবজেক্ট পাস করার প্রয়োজন হয়, NavType.ParcelableType ব্যবহার করুন, তবে 1 KB-র বেশি বড় অবজেক্ট পাস করা এড়িয়ে চলুন — এতে TransactionTooLargeException হতে পারে।
স্ক্রিন ঘোরানো একটি কনফিগারেশন পরিবর্তন ঘটায়, যা ডিফল্টরূপে Activity পুনঃনির্মাণ করে। composable স্ক্রিনের অবস্থা সংরক্ষণ করতে, সাধারণ ডেটার জন্য rememberSaveable বা সেই স্ক্রিনের স্কোপ সহ ViewModel ব্যবহার করুন। Navigation Compose পুনঃনির্মাণের পরে ব্যাক স্ট্যাক পুনরুদ্ধার করে, কিন্তু composable() ফাংশনের ভিতরের অবস্থা rememberSaveable ছাড়া রিসেট হয়ে যায়।
না, composable() হল NavGraphBuilder-এর একটি এক্সটেনশন ফাংশন, যা শুধুমাত্র NavHost ব্লকের ভিতরে উপলব্ধ। নেভিগেশন ছাড়া সাধারণ UI প্রতিস্থাপনের জন্য, শর্তসাপেক্ষ রেন্ডারিং (when, if) বা AnimatedContent ব্যবহার করুন। composable() বিশেষভাবে ব্যাক স্ট্যাক এবং ডিপ লিঙ্ক সমর্থন সহ রাউটিং-এর জন্য ডিজাইন করা হয়েছে।
ViewModel-এর ভিতরে SavedStateHandle ব্যবহার করুন: প্রথম নেভিগেশনে, handle.get("initialized") null ফেরত দেয়; ব্যাক নেভিগেশনে, এটি সংরক্ষিত মান ফেরত দেয়। বিকল্পভাবে, navController.previousBackStackEntry-এর মাধ্যমে ব্যাক স্ট্যাকে বর্তমান অবস্থান বিশ্লেষণ করুন — যদি এটি null হয়, তাহলে এটি নেভিগেশন স্ট্যাকের প্রথম স্ক্রিন।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন