NavHost یک کانتینر composable است که به عنوان نقطه ورود برای گراف ناوبری در Jetpack Compose عمل میکند. این کامپوننت NavController را به مجموعهای از مسیرها متصل میکند و صفحه فعلی را بر اساس وضعیت back stack نمایش میدهد. بر اساس Android Developers (2025)، NavHost یک کامپوننت اجباری برای هر برنامه Compose با ناوبری است. در داخل NavHost، مسیرهای composable با آرگومانهای اختیاری، deep links و انیمیشن ثبت میشوند. هر مسیر یک تابع composable معمولی است که NavBackStackEntry با دادههای انتقال دریافت میکند. NavHost به طور خودکار back press، ذخیره وضعیت و بازیابی در بازپیکربندی را مدیریت میکند.
نکات اصلی
NavHost یک تابع composable است که کانتینری برای نمایش صفحه ناوبری فعلی فراهم میکند. NavHost یک NavController، startDestination و گراف مسیرهای ساخته شده از طریق Kotlin DSL را میپذیرد. هنگام تغییر مسیر فعلی، NavHost composable نمایش داده شده را با انیمیشن مشخص شده تغییر میدهد.
NavHost مانند یک تغییردهنده صفحه کار میکند: NavBackStackEntry فعلی را از NavController ردیابی کرده و بلوک composable مربوطه را نمایش میدهد. هر صفحه یک تابع composable مستقل است که NavBackStackEntry را با آرگومانهای مسیر دریافت میکند. همه صفحهها در یک درخت ترکیب واحد وجود دارند، اما NavHost فقط یکی را در یک زمان نشان میدهد و بقیه را از طریق انیمیشن پنهان میکند.
برخلاف FragmentManager، NavHost برای هر صفحه Fragment ایجاد نمیکند. تمام چرخه حیات از طریق CompositionLifecycle مدیریت میشود — توابع composable دارای onStart/onResume نیستند، بنابراین برای عوارض جانبی از LaunchedEffect و DisposableEffect استفاده میشود. NavHost به طور خودکار در NavController مشترک شده و UI را هنگام تغییر مسیر بازترکیب میکند.
بر اساس Google، NavHost از نسخه Navigation 2.4.0 یک API پایدار است. از نسخه 2.8.0، NavHost از Type-Safe Navigation از طریق Kotlin Serialization پشتیبانی میکند که routeهای رشتهای را با data-کلاسها جایگزین میکند. NavHost همچنین از گرافهای تودرتو پشتیبانی میکند که امکان سازماندهی ناوبری بر اساس ماژولها را فراهم میکند.
NavHost با دو پارامتر اجباری ایجاد میشود: navController (نمونه NavHostController) و startDestination (رشته مسیر صفحه اول). پارامتر سوم — بلوک builder است که در آن همه مسیرها از طریق composable()، navigation() و dialog() ثبت میشوند.
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") { HomeScreen(navController) }
composable("settings") { SettingsScreen(navController) }
}
}
startDestination مسیری است که هنگام راهاندازی اولیه NavHost باز میشود. اگر back stack خالی باشد، NavHost به طور خودکار startDestination را به پشته اضافه میکند. در بازپیکربندی (چرخش صفحه)، NavHost آخرین مسیر را از savedState بازیابی میکند، نه startDestination.
برای BottomNavigation، startDestination یکی از مسیرهای پنل پایینی است. سایر مسیرهای پنل به عنوان ورودیهای composable جداگانه اضافه میشوند. NavHost باید در داخل Scaffold.content قرار گیرد — جایی که محتوای اصلی برنامه نمایش داده میشود. NavHost تمام ارتفاع موجود پس از کسر TopAppBar و BottomNavigation را اشغال میکند.
تابع composable(route, arguments, deepLinks, enterTransition, exitTransition, content) یک مسیر را در گراف NavHost ثبت میکند. پارامتر route رشتهای است که مسیر را با placeholderهای اختیاری به فرم {paramName} توصیف میکند. placeholder هنگام ناوبری با مقدار مشخص جایگزین میشود.
بلوک content NavBackStackEntry دریافت میکند که آرگومانها از آن استخراج میشوند. تابع composable صفحه فقط زمانی نمایش داده میشود که مسیر فعلی NavController با route مطابقت داشته باشد. در صورت عدم تطابق، composable از ترکیب حذف میشود، اما وضعیت آن میتواند از طریق rememberSaveable یا ViewModel با SavedStateHandle حفظ شود.
composable(
route = "article/{articleId}",
arguments = listOf(navArgument("articleId") {
type = NavType.IntType
defaultValue = 0
}),
deepLinks = listOf(navDeepLink { uriPattern = "https://app.example/article/{articleId}" })
) { backStackEntry ->
val articleId = backStackEntry.arguments?.getInt("articleId") ?: 0
ArticleScreen(articleId = articleId)
}
تعداد ورودیهای composable در داخل NavHost میتواند هر چیزی باشد — از چند تا صدها. برای برنامههای بزرگ، مسیرها بر اساس ماژولها تقسیم و از طریق گرافهای تودرتو متصل میشوند. هر composable میتواند تنظیمات انیمیشن، deep links و آرگومانهای خاص خود را داشته باشد.
آرگومانهای مسیر از طریق پارامتر arguments: List<NamedNavArgument> در composable() تعریف میشوند. هر آرگومان از طریق navArgument(name) { type; defaultValue } مشخص میشود. NavType نوع آرگومان را تعیین میکند: StringType، IntType، LongType، FloatType، BoolType، ParcelableType و ReferenceType.
| پارامتر مسیر | مثال route | NavType |
|---|---|---|
| مسیر (path) | «user/{id}» | NavType.IntType |
| پرس و جو (query) | «search?q={query}» | NavType.StringType |
| اختیاری | «details/{id}?tab={tab}» | StringType + defaultValue=«» |
| Parcelable | «checkout/{order}» | NavType.ParcelableType |
آرگومانها از NavBackStackEntry از طریق arguments?.getInt("id") استخراج میشوند. برای آرگومانهای اجباری، defaultValue میتواند حذف شود — NavType از null استفاده خواهد کرد. برای آرگومانهای اختیاری، defaultValue اجباری است، در غیر این صورت ناوبری در صورت عدم وجود پارامتر استثنا ایجاد میکند.
از نسخه Navigation 2.8.0، Type-Safe Navigation توصیه میشود: یک sealed class یا data class برای مسیرها با Kotlin Serialization تعریف کنید. به جای route رشتهای از composable<RouteType> { backStackEntry -> } استفاده کنید. این کار اشتباهات تایپی در route را حذف کرده و به طور خودکار NavType را برای آرگومانها تولید میکند. برای مهاجرت، وابستگی navigation-compose-typesafe و پلاگین Kotlin Serialization را اضافه کنید.
nested graphs — مکانیزم گروهبندی مسیرها در داخل NavHost از طریق تابع navigation(route, startDestination) است. یک گراف تودرتو پیشوند route و startDestination خاص خود را دارد و همه مسیرهای آن از طریق پیشوند در دسترس هستند. nested graphs برای معماری ماژولار استفاده میشوند، جایی که هر ماژول ویژگی زیرگراف خود را ثبت میکند.
مزایای گرافهای تودرتو: ایزولهسازی مسیرها در داخل ماژول، back stack واحد برای گروهی از صفحهها، امکان ناوبری با پیشوند بدون افشای ساختار داخلی. به عنوان مثال، گراف «auth» شامل «auth/login» و «auth/register» است. ناوبری هم از طریق مسیر کامل و هم از طریق پیشوند با تغییرمسیر به startDestination امکانپذیر است.
NavHost(navController = navController, startDestination = "main") {
composable("main") { MainScreen(navController) }
navigation(
route = "auth",
startDestination = "auth/login"
) {
composable("auth/login") { LoginScreen(navController) }
composable("auth/register") { RegisterScreen(navController) }
}
}
nested graphs از انتقال آرگومانها در سطح گراف پشتیبانی میکند: پارامترهای اعلامشده در route گراف به همه مسیرهای داخلی منتقل میشوند. برای پاک کردن گراف تودرتو از popBackStack(route) استفاده کنید — همه ورودیهای داخلی را حذف میکند. گرافهای تودرتو محدودیت عمق ندارند، اما برای خوانایی بیش از ۳ سطح توصیه نمیشود.
NavHost از انیمیشن انتقال بین مسیرهای composable از طریق پارامترهای enterTransition، exitTransition، popEnterTransition و popExitTransition پشتیبانی میکند. انیمیشنها یک بار برای NavHost تعریف شده و برای همه مسیرها اعمال میشوند، یا به صورت فردی برای هر composable. به طور پیشفرض انیمیشن غیرفعال است.
پیکربندی معمول: enterTransition = slideInHorizontally(initialOffsetX = { it }) — صفحه از راست وارد میشود؛ exitTransition = slideOutHorizontally(targetOffsetX = { -it }) — صفحه به چپ خارج میشود. برای انیمیشن pop جهتها معکوس هستند: صفحه از چپ وارد و به راست خارج میشود. برای BottomNavigation از fadeIn/fadeOut بدون slide استفاده میشود.
NavHost(
navController = navController,
startDestination = "home",
enterTransition = { slideInHorizontally(initialOffsetX = { it }) + fadeIn() },
exitTransition = { slideOutHorizontally(targetOffsetX = { -it }) + fadeOut() },
popEnterTransition = { slideInHorizontally(initialOffsetX = { -it }) + fadeIn() },
popExitTransition = { slideOutHorizontally(targetOffsetX = { it }) + fadeOut() }
) { /* composable routes */ }
انیمیشنهای سفارشی از طریق Compose Animation API ایجاد میشوند: AnimatedContentTransitionScope دسترسی به اندازههای کانتینر، پیشرفت انیمیشن و direction را فراهم میکند. برای shared element transition (یک element به آرامی به صفحه دیگر منتقل میشود) به کتابخانه Accompanist Navigation Animation یا پیادهسازی سفارشی از طریق sharedElement Modifier نیاز است. بر اساس Android Developers (2025)، انیمیشن slide پیشفرض (ورود از راست، خروج به چپ) در ۸۰٪ از برنامههای Android با ناوبری استفاده میشود.
سوالات متداول
از نظر فنی بله، اما توصیه نمیشود. هر NavHost یک back stack مستقل ایجاد میکند که ناوبری یکپارچه را مختل میکند. استثنا — مناطق جداگانه، مانند NavHost برای محتوای اصلی و NavHost برای BottomSheet با ناوبری خاص خود.
NavHost — کانتینر ناوبری که صفحهها را تغییر میدهد. Scaffold — طرحبندی کل صفحه (TopAppBar، BottomNavigation، FloatingActionButton). معمولاً NavHost در داخل Scaffold.content قرار میگیرد. Scaffold ناوبری را مدیریت نمیکند، بلکه فقط slotهایی برای کامپوننتهای UI فراهم میکند.
ViewModel در چارچوب NavBackStackEntry از طریق viewModel() ایجاد میشود. برای به اشتراکگذاری ViewModel بین صفحهها از parentNavController استفاده کنید: ViewModel مشترک را به entry والد متصل کنید. جایگزین — DI (Hilt/Koin) با scope روی NavGraph.
این رفتار عادی است — NavHost composable را هنگام خروج از مسیر از ترکیب حذف میکند. برای حفظ وضعیت از rememberSaveable برای وضعیت UI و ViewModel با SavedStateHandle برای منطق تجاری استفاده کنید.
آخرین مسیر composable("404") را اضافه کنید و در صورت deep link ناشناخته به آن ناوبری کنید. NavHost مسیر catch-all ندارد — مسیر را در intent-handler Deep Link قبل از navigate() بررسی کنید. اگر route پیدا نشد — به ۴۰۴ navigate کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید