Navigation در کامپوز Jetpack Compose

Navigation در کامپوز Jetpack Compose
در این پست می‌خوانید:

سلام خدمت دوستان برنامه نویس ام ، در این مقاله میخوایم بررسی کنیم که Navigation در کامپوز به چه صورت است و چطوری استفاده کنیم ، این ابزار به شما کمک می‌کنه تا راحت و حرفه‌ای ناوبری (Navigation) بین صفحات یا همون کامپوزبل‌هاتون رو مدیریت کنین.

navigation in compose

سلام خدمت دوستان برنامه نویس ام ، در این مقاله میخوایم بررسی کنیم که Navigation در کامپوز به چه صورت است و چطوری استفاده کنیم ، این ابزار به شما کمک می‌کنه تا راحت و حرفه‌ای ناوبری (Navigation) بین صفحات یا همون کامپوزبل‌هاتون رو مدیریت کنین.

اگه قبلاً با Navigation Component در روش سنتی اندروید (با XML و Viewها) آشنا بودین، احتمالاً اینجا هم سریع راه میفتین. ولی خبر خوب اینه که دیگه خبری از XML نیست و همه چی توی همون کاتلین خلاصه شده! 😎

در ابتدای کار ، من بخش های مهم این نویگشین رو معرفی میکنم ، در پایان یه مثال کلی میزنم که مطلب جا بی افته !

بخش اول: تغییرات اصلی در Navigation Component

حذف فایل‌های XML

یادتونه که توی روش سنتی برای مدیریت نویگیشن باید یه فایل XML برای تعریف Navigation Graph می‌ساختیم؟ خب، حالا دیگه نیازی به اون نیست. توی Jetpack Compose همه‌ی این کارها توی کد کاتلین انجام می‌شه.

شباهت‌ها با روش سنتی

بعضی چیزا تغییر نکردن، مثل:

  • استفاده از NavController برای مدیریت ناوبری.
  • نیاز به NavHost و تعریف مقصدها (Destinations).
  • ارسال داده بین صفحات با آرگومان‌ها.

محدودیت‌های جدید

یه نکته مهم اینه که توی Compose توصیه شده اشیاء کامل (Objects) رو بین صفحات نفرستین. فقط مقادیر ساده مثل String یا Int رو ارسال کنین. اگه نیاز به ارسال داده‌های پیچیده‌تر دارین، بهتره شناسه (ID) اون داده رو بفرستین و توی مقصد، داده کامل رو از دیتابیس یا یه منبع دیگه بگیرین.

بخش دوم: استفاده از NavController

اضافه کردن کتابخونه نویگیشن

اول از همه باید کتابخونه مورد نیاز رو اضافه کنیم. مطمئن بشید آخرین نسخه رو توی فایل build.gradle اضافه کردین:

dependencies { implementation "androidx.navigation:navigation-compose:2.8.4" }

ایجاد NavController

برای مدیریت ناوبری، باید یه NavController داشته باشیم. این کار رو با تابع rememberNavController انجام می‌دیم. مثلاً:

val navController = rememberNavController()

این navController رو باید به همه‌ی کامپوزبل‌هایی که نیاز به ناوبری دارن، برسونیم.

اتصال NavController به NavHost

نوهاست NavHost که مقصدها (Destinations) و مسیرها (Routes) رو تعریف می‌کنیم. همچنین باید یه صفحه‌ی شروع (Start Destination) هم مشخص کنیم:

  
      
NavHost(
    navController = navController,
    startDestination = "home"
) {
    composable("home") {
        HomeScreen(navController)
    }
    composable("details") {
        DetailsScreen(navController)
    }
}

    
    
  

بخش سوم: تعریف مقصدها و مسیریابی

تعریف مقصدها (Destinations)

هر صفحه یا کامپوزبل یه مقصد محسوب می‌شه. مقصدها رو با استفاده از تابع composable توی NavHost تعریف می‌کنیم. هر مقصد باید یه مسیر (Route) داشته باشه:


      
composable("home") {
    HomeScreen(navController)
}
composable("details") {
    DetailsScreen(navController)
}

    
    
  

مسیریابی بین مقصدها

برای ناوبری به یه مقصد دیگه، از تابع navigate در NavController استفاده می‌کنیم. مثلاً:

Button(onClick = {
    navController.navigate("details")
}) {
    Text("Go to Details")
}

    

نکته مهم: تابع navigate رو فقط توی onClick یا Callbackها صدا بزنین. اگه توی بدنه کامپوزبل این کار رو انجام بدین، توی هر Recomposition دوباره فراخوانی می‌شه و ممکنه مشکل ایجاد کنه.

بخش چهارم: ارسال آرگومان‌ها

گاهی لازم می‌شه که داده‌هایی رو بین صفحات ارسال کنیم. توی Compose این کار رو با آرگومان‌ها انجام می‌دیم.

اگه فرق بین آرگومان و پارامتر رو نمیدونی پیشنهاد میدم این مطلب رو بخونی  :تفاوت بین آرگومان و پارامتر :Argument or Parameter

تعریف آرگومان‌ها

آرگومان‌ها دو نوع دارن:

  1. آرگومان‌های اجباری (Required): اینا همیشه باید مقدار داشته باشن.
  2. آرگومان‌های اختیاری (Optional): می‌تونین مقدار پیش‌فرض براشون تعریف کنین.

ارسال آرگومان‌ها

برای ارسال آرگومان، مقدار رو همراه با مسیر مقصد ارسال می‌کنیم:

  
      
Button(onClick = {
    navController.navigate("details/123")
}) {
    Text("Go to Details with ID")
}

    

دریافت آرگومان‌ها

برای خوندن آرگومان‌ها توی صفحه مقصد، از NavBackStackEntry استفاده می‌کنیم:

val taskId = navController.previousBackStackEntry?.arguments?.getString("taskId")

نکته: حتماً مطمئن بشین که آرگومان‌هاتون درست تعریف شدن و مقدار دارن.

بخش پنجم: مثال کامل

حالا بیاین یه مثال واقعی بزنیم که همه چیز رو کنار هم ببینیم.

در این مثال ما سه تا فایل خواهیم داشت ،

  1. MainActivity()
  2. Screens
  3. navigationStack()

MainActivity : 

این کلاس که مشخصاً کلاس اصلی ماست ، داخلش دوتا اسکرین تعریف کردیم ، MainScreen و DetailScreen حالا در این ها میشه ، صفحه های ما ، در صفحه ای MainScreen ما یه دکمه داریم و یه جای نوشته که بشه اون رو به صفحه DetailScreen ارسال کنیم

class MainActivity : ComponentActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            TrainNavigationTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    NavigationStack()
                }
            }
        }
    }
}


@Composable
fun MainScreen(navController: NavController) {
    val text = remember { mutableStateOf("") }

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        OutlinedTextField(value = text.value, onValueChange = {
            text.value = it
        })

        Button(onClick = {
            navController.navigate(route = Screens.Detail.route + "?text=${text.value}")
        }) {
            Text("Next Screen")
        }
    }

}

@Composable
fun DetailScreen(text: String?) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        Text("Detail Screen")
        Text("$text")
    }
}

Screens:

فایل Screens مثل یه نقشه ناوبری برای اپلیکیشن عمل می‌کنه که توش آدرس‌ها و اسم صفحه‌ها رو مشخص می‌کنیم. اینجوری همه مسیرها یه جا جمع می‌شن و مدیریت‌شون خیلی راحت‌تر می‌شه. به جای اینکه توی پروژه از رشته‌های پراکنده استفاده کنیم، مسیرها رو اینجا تعریف می‌کنیم تا هم کد تمیزتر باشه و هم اگه بخوایم چیزی رو تغییر بدیم، سریع پیداش کنیم. خلاصه، این فایل کارمون رو برای مدیریت صفحه‌ها و مسیرهای ناوبری حسابی ساده می‌کنه!

sealed class Screens(val route:String){
    data object Main:Screens("main_screen")
    data object Detail:Screens("detail_screen")
}

navigationStack() :

«بار کلی ، این کار ناوبری بر دوش این فایل عه» ، این فایل در واقع مسئول مدیریت و اجرای ناوبری بین صفحه‌های اپلیکیشن شماست. توش یه تابع کامپوزبل به اسم NavigationStack داریم که کارش اینه که یه NavController بسازه و همه مقصدهای ناوبری رو داخل یه NavHost تعریف کنه. اینجا مشخص می‌کنیم کدوم صفحه نقطه شروع باشه (مثل صفحه اصلی یا همون MainScreen) و چطوری بین صفحه‌ها جابه‌جا بشیم.

برای هر صفحه (مثل MainScreen و DetailScreen) یه مسیر (Route) تعریف کردیم که از فایل Screens گرفته شده. توی مسیر DetailScreen حتی یه آرگومان به اسم text هم داریم که می‌تونیم مقدارش رو ارسال کنیم. این باعث می‌شه هر وقت بخوایم چیزی رو به صفحه جزئیات بفرستیم، خیلی راحت این کار رو انجام بدیم. خلاصه، این فایل تمام مسیرها و ارتباط بین صفحه‌ها رو مدیریت می‌کنه و کار ناوبری رو حسابی ساده و مرتب کرده!

package ir.brazandeh.trainnavigation

import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument

@Composable
fun NavigationStack() {

    val navController = rememberNavController()

    NavHost(navController = navController, startDestination = Screens.Main.route) {
        composable(route = Screens.Main.route) {
            MainScreen(navController = navController)
        }

        composable(
            route = Screens.Detail.route + "?text={text}",
            arguments = listOf(
                navArgument("text") {
                    type = NavType.StringType
                    nullable = true
                }
            )
        ) {
            DetailScreen(text = it.arguments?.getString("text"))
        }
    }


}

بخش ششم: نکات کاربردی

استفاده از popUpTo: اگه می‌خواین یه صفحه رو از استک حذف کنین، از popUpTo استفاده کنین:

      
navController.navigate("home") {
    popUpTo("home") { inclusive = true }
}

مدیریت آرگومان‌ها: برای ارسال داده‌های پیچیده، فقط شناسه (ID) اون داده رو بفرستین و توی صفحه مقصد، داده کامل رو از دیتابیس بگیرین.

جمع‌بندی

توی این جزوه با Navigation Component در Jetpack Compose آشنا شدین. یاد گرفتین چطوری مقصدها رو تعریف کنین، داده‌ها رو بین صفحات جابه‌جا کنین و از NavController استفاده کنین. امیدوارم این آموزش براتون مفید بوده باشه. اگه سوالی دارین، حتماً بپرسین. موفق باشین! 🚀

دیدگاه‌ها ۰
ارسال دیدگاه جدید