NavController — ماهیت، روش‌ها و مدیریت ناوبری در Jetpack Compose

نویسنده: IT Sectr منتشر شده: 2026-06-29 زمان مطالعه: 7 دقیقه

NavController مؤلفه مرکزی کتابخانه Navigation Compose است که پیمایش پشته و وضعیت back stack را در برنامه‌های Android مدیریت می‌کند. از طریق NavController انتقال بین صفحه‌ها، بازگشت به صفحات قبلی و انتقال داده بین مسیرها انجام می‌شود. به گفته Android Developers (2025)، NavController یک عنصر اجباری برای هر برنامه Compose با بیش از یک صفحه است. کنترلر از طریق rememberNavController() ایجاد می‌شود، به NavHost منتقل می‌شود و برای فراخوانی navigate() از هر نقطه ترکیب در دسترس است. پشتیبانی داخلی SavedStateHandle به طور خودکار وضعیت ViewModel را هنگام بازپیکربندی ذخیره می‌کند.

نکات اصلی

  • NavController — کنترلر مرکزی ناوبری Compose که back stack و انتقال بین صفحه‌ها را مدیریت می‌کند
  • navigate() — روش اصلی برای انتقال به مسیر با پشتیبانی NavOptions برای مدیریت پشته
  • popBackStack() — بازگشت به صفحه قبلی با پاکسازی اختیاری تا مسیر مشخص
  • SavedStateHandle — ادغام با ViewModel برای ذخیره وضعیت صفحه هنگام ناوبری
  • currentBackStackEntryAsState() — مشاهده مسیر فعلی برای همگام‌سازی UI

NavController در Jetpack Compose چیست؟

NavController — کلاسی از کتابخانه Navigation Compose است که کنترلر ناوبری را برای برنامه‌های Compose پیاده‌سازی می‌کند. NavController پشته NavBackStackEntry را مدیریت می‌کند، جایی که هر ورودی شامل مسیر، آرگومان‌ها و وضعیت صفحه است. کنترلر از عملیات اصلی ناوبری پشتیبانی می‌کند: انتقال، بازگشت، جایگزینی و پاکسازی.

برخلاف سیستم View که در آن ناوبری از طریق FragmentManager یا Intent انجام می‌شد، NavController منحصراً در زمینه Compose کار می‌کند. back stack به صورت گراف NavDestination ذخیره می‌شود، نه پشته Fragment. این کار سربار ایجاد و نابودی Fragment را حذف می‌کند و همچنین آزمایش را ساده می‌کند — NavController را می‌توان از طریق TestNavHostController شبیه‌سازی کرد.

NavController ارتباط نزدیکی با NavHost دارد — ظرفی که صفحه فعلی را از گراف رندر می‌کند. بدون NavHost، NavController نمی‌تواند توابع composable را نمایش دهد، اما توانایی مدیریت پشته را حفظ می‌کند. در معماری معمولی، NavController در سطح Activity یا composable اصلی ایجاد می‌شود و از طریق پارامترها به پایین درخت ترکیب منتقل می‌شود.

به گفته Google، NavController چندین انتشار اصلی داشته است. نسخه 2.8.0 ناوبری Type-Safe را اضافه کرد، نسخه 2.9.0 — پشتیبانی از predictive back gesture (Android 14+). کنترلر با Material3 Scaffold و BottomNavigation سازگار است. برای پروژه‌های چندماژولی، NavController از طریق DI (Hilt/Koin) یا پارامترهای سازنده منتقل می‌شود.

NavController از طریق تابع composable rememberNavController() ایجاد می‌شود. تابع یک نمونه NavHostController (وارث NavController) مرتبط با چرخه حیات composable فعلی برمی‌گرداند. هنگام خروج از ترکیب، کنترلر پاک می‌شود. برای ذخیره کنترلر در بازپیکربندی، از rememberSaveable یا ViewModel استفاده کنید.

kotlin
@Composable
fun MyApp() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "main"
    ) {
        composable("main") { MainScreen(navController) }
        composable("details") { DetailsScreen(navController) }
    }
}

پیکربندی NavController شامل است: NavHostController (اصلی)، TestNavHostController (آزمایش) و ScopedNavController (فرعی برای گراف‌های تو در تو). برای BottomNavigation، NavController باید برای کل برنامه واحد باشد — ایجاد کنترلر جدید در هر زبانه منجر به از دست رفتن پشته می‌شود. برای انتقال کنترلر به صفحات تو در تو، از پارامتر تابع استفاده کنید، نه CompositionLocalProvider، تا خوانایی حفظ شود.

برای آزمایش ناوبری، از TestNavHostController با compose-test-rule استفاده کنید. کنترلر امکان تنظیم مسیر اولیه و بررسی اینکه navigate() انتقال مورد انتظار را فراخوانی کرده است. آزمایش NavController به شبیه‌ساز نیاز ندارد — با Semantics-matcherهای Compose Test کار می‌کند.

روش navigate(route: String) — روش اصلی ناوبری در NavController است. یک رشته مسیر، NavOptions اختیاری و Navigator.Extras را دریافت می‌کند. NavOptions رفتار انتقال را مدیریت می‌کند: launchSingleTop (مسیر را در پشته تکرار نکن)، popUpTo (پشته را تا مسیر پاک کن)، restoreState (وضعیت قبلی را بازیابی کن).

NavOptions از طریق نحو builder تنظیم می‌شوند: NavOptionsBuilder. پارامترهای اصلی: popUpTo (route + inclusive/saveState)، launchSingleTop (Boolean، true — تکرار ایجاد نکن)، restoreState (وضعیت را در بازگشت بازیابی کن). بدون popUpTo، هر navigate() یک ورودی به پشته اضافه می‌کند که منجر به انباشت back stack و رفتار نادرست دکمه Back می‌شود.

kotlin
navController.navigate("profile/42") {
    popUpTo("main") { saveState = true }
    launchSingleTop = true
    restoreState = true
}

Navigator.Extras امکان انتقال داده‌های اضافی که بخشی از مسیر نیستند را فراهم می‌کند: shared element برای انیمیشن، پرچم‌های Intent، Pac-Man bundle. Extras به ندرت استفاده می‌شود — عمدتاً برای ادغام با Accompanist Animation یا Navigator سفارشی. برای اکثر سناریوها، رشته مسیر و NavOptions کافی است.

popBackStack: مدیریت بازگشت و پاکسازی پشته

popBackStack() — روش بازگشت به صفحه قبلی. بدون آرگومان ورودی بالای پشته را حذف می‌کند و در صورت موفقیت true برمی‌گرداند. اگر پشته خالی باشد — روش false برمی‌گرداند و Activity بسته می‌شود (مشابه super.onBackPressed()).

نسخه بارگیری شده popBackStack(route: String, inclusive: Boolean) تمام ورودی‌ها را تا مسیر مشخص حذف می‌کند. اگر inclusive = true باشد — خود مسیر مشخص نیز حذف می‌شود. روش Boolean برمی‌گرداند — true اگر ورودی‌ها پیدا و حذف شوند. نسخه با inclusive برای سناریوهای «خروج به صفحه اصلی» پس از مجوز یا تکمیل سفارش مفید است.

روششرحمثال
popBackStack()بازگشت یک صفحه به عقبnavController.popBackStack()
popBackStack(route, false)پاکسازی تا مسیر (مسیر باقی می‌ماند)popBackStack("home", false)
popBackStack(route, true)پاکسازی تا و شامل مسیرpopBackStack("home", true)
navigate(route) { popUpTo(route) { inclusive = true } }انتقال با پاکسازی کاملnavigate("login") { popUpTo(0) { inclusive = true } }

برای مدیریت دکمه Back سیستمی (hardware back button)، از BackHandler از Compose استفاده کنید. BackHandler enabled و onBack — callback فراخوانی شده در فشار را دریافت می‌کند. برای Android 14+ از PredictiveBackGesture استفاده می‌شود که از طریق NavController نسخه 2.9.0 ادغام شده است. Predictive back یک انیمیشن پیش‌نمایش بازگشت اضافه می‌کند.

SavedStateHandle: ذخیره وضعیت صفحه

SavedStateHandle — مکانیزمی برای ذخیره وضعیت ViewModel هنگام ناوبری و بازپیکربندی است. NavController به طور خودکار SavedStateHandle را برای هر NavBackStackEntry فراهم می‌کند. از طریق SavedStateHandle، ViewModel وضعیت صفحه را ذخیره می‌کند و در بازگشت آن را بازیابی می‌کند (restoreState = true).

در Navigation Compose، SavedStateHandle همراه با ViewModel استفاده می‌شود: ViewModel از طریق SavedStateHandle که از backStackEntry منتقل می‌شود مقداردهی اولیه می‌شود. هنگام انتقال به صفحه دیگر و بازگشت (با restoreState)، ViewModel وضعیت ذخیره شده را دریافت می‌کند، دوباره ایجاد نمی‌شود. این برای صفحات با ورود داده، فیلترها یا پیمایش حیاتی است.

kotlin
class ProfileViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    val userId: String = savedStateHandle.get<String>("userId") ?: ""
    var searchQuery by savedStateHandle.getStateFlow("search", "")
        .collectAsState()
}

SavedStateHandle از انواع اولیه، String، Bundle و Parcelable پشتیبانی می‌کند. برای اشیاء پیچیده، فقط ID را ذخیره کنید و داده‌های کامل را از مخزن بارگیری کنید. محدودیت SavedStateHandle حدود 1 MB است، تجاوز از آن باعث TransactionTooLargeException می‌شود. برای حجم‌های بزرگ، به جای ذخیره در handle از Room یا DataStore استفاده کنید.

مهم: SavedStateHandle فقط هنگام استفاده از restoreState = true در NavOptions وضعیت را ذخیره می‌کند. اگر restoreState مشخص نشده باشد، در بازگشت ViewModel با مقادیر پیش‌فرض دوباره ایجاد می‌شود. برای تغییر BottomNavigation با restoreState، NavController وضعیت هر زبانه را ذخیره می‌کند و در انتخاب مجدد بازیابی می‌کند.

مشاهده مسیر فعلی از طریق currentBackStackEntryAsState

currentBackStackEntryAsState() — تابعی که State<NavBackStackEntry?> را برمی‌گرداند که با هر تغییر مسیر فعلی به‌روز می‌شود. این مکانیزم اصلی همگام‌سازی UI با ناوبری است: BottomNavigation عنصر فعال را برجسته می‌کند، Toolbar عنوان را به‌روز می‌کند، Drawer در انتقال بسته می‌شود.

تابع از طریق snapshotFlow و collectAsState کار می‌کند: هنگام تغییر back stack، Compose عناصر مشترک شده را دوباره ترکیب می‌کند. مهم: currentBackStackEntryAsState() فقط پس از اتمام انیمیشن انتقال به‌روز می‌شود. برای به‌روزرسانی فوری، از currentDestination استفاده کنید که با navigate() به طور همزمان تغییر می‌کند اما از وضعیت پشتیبانی نمی‌کند.

kotlin
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

Text(
    text = when (currentRoute) {
        "home" -> "Home"
        "profile" -> "Profile"
        else -> ""
    }
)

برای دسترسی به آرگومان‌های مسیر فعلی، از navBackStackEntry?.arguments استفاده کنید. این در BottomNavigation راحت است: selectedItem بر اساس currentRoute محاسبه می‌شود. برای اشکال‌زدایی ناوبری، از NavController.addOnDestinationChangedListener() استفاده کنید که هر انتقال را ثبت می‌کند. در production، از اشتراک‌ها در داخل تعداد زیادی composable خودداری کنید — یک منبع واحد در ViewModel ایجاد کنید و State را به UI منتقل کنید.

سوالات متداول

آیا می‌توان چند NavController در یک Activity ایجاد کرد؟

از نظر فنی بله، اما توصیه نمی‌شود. یک NavController واحد back stack منسجم را تضمین می‌کند و اشکال‌زدایی را ساده می‌کند. چندین کنترلر فقط برای گراف‌های تو در تو با ناوبری جداگانه موجه است (مثلاً، modal bottom sheet با پشته خود).

چگونه NavController را از طریق ViewModel منتقل کنیم؟

NavController را از طریق سازنده یا DI به ViewModel منتقل کنید. اما بهتر است فقط توابع callback (onNavigate، onBack) را منتقل کنید، نه خود NavController را — این آزمایش را ساده می‌کند. برای رویدادها، از Channel<NavEvent> در ViewModel استفاده کنید و در UI جمع‌آوری کنید.

چرا navigate بعد از عملیات ناهمگام کار نمی‌کند؟

مشکل در چرخه حیات است: اگر NavController هنوز مقداردهی اولیه نشده باشد (NavHost ساخته نشده)، navigate() نادیده گرفته می‌شود. از LaunchedEffect برای فراخوانی ناوبری پس از بارگیری داده استفاده کنید، نه داخل کوروتین با lifecycle دلخواه.

چگونه کل back stack را پاک کرده و به صفحه جدید برویم؟

navController.navigate("target") { popUpTo(0) { inclusive = true } } را فراخوانی کنید. پارامتر popUpTo(0) پشته را کاملاً پاک می‌کند، inclusive = true ورودی اولیه را نیز حذف می‌کند. پرچم launchSingleTop = true از تکرار مسیر جدید جلوگیری می‌کند.

تفاوت NavHostController با NavController چیست؟

NavHostController — وارث NavController با روش‌های اضافی برای NavHost (مثلاً setOnBackStackChangedListener). NavController — کلاس پایه که می‌توان خارج از NavHost برای مدیریت برنامه‌ای پشته استفاده کرد. در اکثر موارد، از NavHostController استفاده می‌شود.

خلاصه

  • NavController — مؤلفه مرکزی Navigation Compose، مدیریت پشته مسیرها و انتقال بین صفحه‌ها
  • navigate() انتقال را با تنظیمات popUpTo، launchSingleTop و restoreState از طریق NavOptions انجام می‌دهد
  • popBackStack() بازگشت را مدیریت می‌کند: گام تکی یا پاکسازی انبوه تا مسیر مشخص با inclusive
  • SavedStateHandle با ViewModel برای ذخیره خودکار وضعیت صفحه در ناوبری ادغام می‌شود
  • currentBackStackEntryAsState() مشاهده واکنشی مسیر فعلی را برای سینکرونیزاسیون UI فراهم می‌کند
  • BackHandler دکمه Back سیستم را مدیریت می‌کند، PredictiveBackGesture از NavController 2.9.0 پشتیبانی می‌شود
  • برای آزمایش از TestNavHostController با compose-test-rule و Semantics-matcherها استفاده کنید

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید