Modifier — زنجیره تغییردهنده‌ها و عملکرد در Compose

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

Modifier — یک شیء تغییرناپذیر در Jetpack Compose است که ویژگی‌های کامپوننت UI را تعریف می‌کند: اندازه، حاشیه‌ها، پس‌زمینه، پردازش حرکات و رفتار. تغییردهنده‌ها از طریق فراخوانی متوالی در یک زنجیره ترکیب می‌شوند و ترتیب اعمال آنها به طور بحرانی بر نتیجه تأثیر می‌گذارد. به گفته Google Android Developers, 2026، استفاده صحیح از Modifier اساس ساخت رابط کاربری منعطف و کارآمد در UI اعلانی است.

نکات اصلی

  • Modifier — شیء تغییرناپذیر توصیف‌کننده ظاهر و رفتار کامپوننت UI
  • زنجیره تغییردهنده‌ها به صورت متوالی ساخته می‌شود، ترتیب بر نمایش تأثیر می‌گذارد
  • ترتیب مهم است: padding → size با size → padding متفاوت است
  • Modifier.composed امکان ایجاد تغییردهنده‌های ترکیبی سفارشی را فراهم می‌کند
  • بهینه‌سازی: از بازآفرینی Modifier در هر بازترکیب خودداری کنید

Modifier در Jetpack Compose چیست

Modifier — یک رابط از بسته androidx.compose.ui است که الگوی Composite را پیاده‌سازی می‌کند. هر تغییردهنده یک عنصر زنجیره است که عنصر قبلی را می‌پوشاند و رفتار خود را اضافه می‌کند. Modifier تغییرناپذیر است — هر تغییری با کپی کردن و افزودن عنصر جدید به زنجیره یک شیء جدید ایجاد می‌کند. این امکان اشتراک‌گذاری ایمن یک Modifier بین چندین کامپوننت را فراهم می‌کند.

توابع-تغییردهنده پایه از طریق شیء همراه Modifier فراخوانی می‌شوند (مثلاً Modifier.padding(), Modifier.fillMaxWidth()). هر تابع یک Modifier جدید با عنصر اضافه شده برمی‌گرداند. اگر چند تغییردهنده وجود داشته باشد، در یک زنجیره ترکیب می‌شوند: Modifier.padding(16.dp).fillMaxWidth().background(Color.Blue). ترتیب جهت از بیرون به داخل نسبت به عنصر UI است.

برخلاف Viewهای سنتی که ویژگی‌ها از طریق setterها (view.setPadding(...), view.setBackground(...)) تنظیم می‌شدند، در Compose Modifier یک توصیف اعلانی است. کامپوننت تغییردهنده‌ها را در حین کار "اعمال" نمی‌کند — LayoutNode در مرحله ترکیب از زنجیره Modifier عبور می‌کند و لیستی از Modifier.Element را جمع‌آوری می‌کند که سپس در مرحله اندازه‌گیری و چیدمان پردازش می‌شوند.

زنجیره تغییردهنده‌ها و ترتیب اعمال

ترتیب تغییردهنده‌ها — یکی از رایج‌ترین اشتباهات در Compose. هر تغییردهنده عنصر قبلی را می‌پوشاند و عملیات از بیرون به داخل اجرا می‌شوند. مثلاً padding(16.dp).clickable { }: ابتدا حاشیه دور عنصر اضافه می‌شود، سپس ناحیه کلیک حاشیه را نیز شامل می‌شود. clickable { }.padding(16.dp): ابتدا ناحیه کلیک برابر با اندازه عنصر است، سپس حاشیه دور آن اضافه می‌شود — کلیک روی حاشیه کار نخواهد کرد.

قانون به خاطر سپردن: زنجیره را از چپ به راست بخوانید و از بیرون به داخل اعمال کنید. اولین تغییردهنده — بیرونی‌ترین، به ناحیه دور عنصر اعمال می‌شود. آخرین — درونی‌ترین، مستقیماً به محتوا اعمال می‌شود. تغییردهنده‌های اندازه (size, fillMaxWidth) باید بعد از حاشیه‌ها بیایند اگر حاشیه از والد لازم است، یا قبل از حاشیه‌ها اگر محتوا ابتدا باید محدود شود و سپس وسط‌چین شود.

مثال: size(100.dp).padding(10.dp) — عنصر با اندازه ثابت 100dp، سپس padding 10dp در بیرون (اندازه نهایی 120dp). padding(10.dp).size(100.dp) — padding 10dp فضای موجود را به (والد - 20dp) کاهش می‌دهد، سپس size(100dp) ممکن است والد را سرریز کند. همیشه ترتیب را آگاهانه طراحی کنید و از تست‌های نمایش برای تأیید نتیجه استفاده کنید.

ترتیبنتیجه
padding → clickableکلیک در ناحیه حاشیه نیز کار می‌کند
clickable → paddingکلیک فقط روی محتوا کار می‌کند، حاشیه منطقه مرده است
size → paddingعنصر size(100)، padding در بیرون → 100+2*pad
padding → sizepadding فضا را کاهش می‌دهد، size ممکن است از مرزها خارج شود
background → paddingپس‌زمینه کل عنصر شامل ناحیه بیرونی را پر می‌کند
padding → backgroundپس‌زمینه فقط داخل حاشیه (ناحیه بیرونی شفاف است)

انواع تغییردهنده‌ها: اندازه، حاشیه، تزئین و رفتار

کتابخانه استاندارد Compose شامل ~50+ تغییردهنده است که به دسته‌هایی تقسیم می‌شوند. اندازه و موقعیت‌یابی: Modifier.size(), width(), height(), fillMaxSize(), fillMaxWidth(), fillMaxHeight(), defaultMinSize(), requiredSize(). حاشیه‌ها و مرزها: padding(), offset(), margin (از طریق padding والد یا Layout قرار می‌گیرد). تزئین: background(), border(), clip(), alpha(), shadow(), blur().

رفتار و حرکات: clickable(), combinedClickable(), pointerInput(), draggable(), swipeable(). چیدمان در ظرف: weight() (برای Row/Column), align(), alignBy(), matchParentSize(). معناشناسی و دسترسی‌پذیری: semantics(), testTag(), clearAndSetSemantics(). نقاشی: drawBehind(), drawWithContent(), drawModifier() — تغییردهنده‌هایی که نقاشی سفارشی روی بوم را امکان‌پذیر می‌کنند.

تغییردهنده‌های معنایی — یک دسته خاص. Modifier.semantics {} مشخص می‌کند که عنصر در درخت Accessibility چگونه نمایش داده شود. Compose به طور خودکار معناشناسی را از متن پر می‌کند، اما برای کامپوننت‌های سفارشی باید نقش‌ها، وضعیت‌ها و اقدامات را به صورت دستی تعیین کنید. این برای مطابقت با WCAG 2.2 و عملکرد صحیح TalkBack (Android) و VoiceOver (iOS) حیاتی است.

kotlin
@Composable
fun ModifierDemo() {
    // زنجیره تغییردهنده با ترتیب صحیح
    Box(
        modifier = Modifier
            .size(150.dp)
            .padding(8.dp)
            .border(2.dp, Color.Gray)
            .background(Color(0xFFE3F2FD))
            .clickable { /* handle click */ }
            .semantics {
                contentDescription = "Demo card with click action"
                role = Role.Button
            }
    ) {
        Text("مرا لمس کن")
    }
}

ایجاد تغییردهنده‌های سفارشی با Modifier.composed

Modifier.composed — یک روش کارخانه‌ای است که امکان ایجاد تغییردهنده‌های ترکیبی را فراهم می‌کند که می‌توانند از سایر تغییردهنده‌ها، LocalComposition و وضعیت محلی استفاده کنند. برخلاف تابع توسعه معمولی، composed هر بار هنگام اعمال یک نمونه ایجاد می‌کند که امکان داشتن وضعیت خاص خود را در داخل تغییردهنده فراهم می‌کند.

چه زمانی از composed استفاده کنیم: ترکیبات تکراری تغییردهنده‌ها (مثلاً سبک استاندارد کارت: padding + background + border + clickable)؛ تغییردهنده‌های با وضعیت (تغییر پس‌زمینه متحرک هنگام فشار)؛ دسترسی به CompositionLocals (طرح رنگ MaterialTheme، تراکم پیکسل). در موارد معمولی، یک تابع توسعه معمولی بدون composed کافی است.

عملکرد composed: هر فراخوانی یک شیء تغییردهنده جدید ایجاد می‌کند که می‌تواند منجر به تخصیص‌های اضافی در بازترکیب شود. برای جلوگیری از این، composed را در remember بپیچید. Google توصیه می‌کند فقط زمانی از composed استفاده کنید که واقعاً به وضعیت یا CompositionLocal در داخل نیاز باشد. برای ترکیبات ایستا از توابع توسعه معمولی استفاده کنید.

kotlin
// تغییردهنده سفارشی از طریق composed با وضعیت
fun Modifier.cardStyle(
    elevation: Dp = 4.dp,
    isSelected: Boolean = false
): Modifier = this.composed {
    val backgroundColor = if (isSelected)
        MaterialTheme.colorScheme.primaryContainer
    else
        MaterialTheme.colorScheme.surface

    this
        .fillMaxWidth()
        .padding(12.dp)
        .background(backgroundColor, RoundedCornerShape(8.dp))
        .shadow(elevation, RoundedCornerShape(8.dp))
}

// مثال استفاده
@Composable
fun CardList() {
    Column {
        Box(Modifier.cardStyle()) { Text("مورد ۱") }
        Box(Modifier.cardStyle(isSelected = true)) { Text("انتخاب شده") }
    }
}

// نسخه ایستا (بدون composed) — سریع‌تر
fun Modifier.simpleCardStyle(): Modifier =
    this.fillMaxWidth().padding(8.dp).clip(RoundedCornerShape(4.dp))

عملکرد Modifier و بهترین روش‌ها

از بازآفرینی Modifier در هر بازترکیب خودداری کنید. اگر تغییردهنده به داده‌های متغیر وابسته نیست — آن را به یک ثابت یا remember منتقل کنید. هر بار که Modifier.padding().background() فراخوانی می‌شود، اشیاء جدید Modifier.Element ایجاد می‌شوند. در یک کامپوننت ایزوله این نامحسوس است، اما در LazyColumn با صدها عنصر، تخصیص‌های اضافی باعث تأخیر قابل توجه در اسکرول می‌شود.

قانون: اگر زنجیره تغییردهنده‌ها به پارامترهای تابع Composable وابسته نیست — آن را به عنوان val خارج از تابع (در سطح فایل یا Companion) اعلام کنید. اگر وابسته است — از remember(وابستگی) { ... } استفاده کنید. برای تغییردهنده‌هایی که همیشه یکسان هستند، کارآمدترین راه val خارج از Composable است: چنین اشیائی یک بار در طول عمر برنامه ایجاد می‌شوند.

بهترین روش‌های ترتیب Modifier: تغییردهنده‌ها را به ترتیب منطقی قرار دهید: ابتدا اندازه/حاشیه (layout)، سپس تزئین (background, border)، سپس رفتار (clickable, pointerInput). این نه تنها خوانایی را بهبود می‌بخشد، بلکه به Compose Runtime کمک می‌کند تا زنجیره را در مرحله اندازه‌گیری بهینه‌سازی کند. همچنین از Boxهای تودرتوی اضافی با Modifierهای مختلف خودداری کنید — اغلب یک Modifier روی ظرف والد می‌تواند جایگزین 2-3 مورد تودرتو شود.

kotlin
// ✅ خوب: ثابت خارج از Composable
private val cardModifier = Modifier
    .fillMaxWidth()
    .padding(16.dp)
    .clip(RoundedCornerShape(8.dp))

@Composable
fun CardContent() {
    Box(cardModifier.background(Color.White)) { ... }
}

// ❌ بد: بازآفرینی در هر بازترکیب
@Composable
fun BadCard() {
    Box(Modifier.fillMaxWidth().padding(16.dp)) { ... }
}

// ✅ خوب: remember برای Modifier پویا
@Composable
fun DynamicCard(color: Color) {
    val modifier = remember(color) {
        Modifier.fillMaxWidth().background(color)
    }
    Box(modifier) { ... }
}

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

آیا می‌توان از یک Modifier برای چندین Composable استفاده کرد؟

بله، Modifier تغییرناپذیر است، بنابراین یک شیء را می‌توان به طور ایمن در چندین مکان استفاده کرد. با این حال اگر از composed-modifier استفاده می‌کنید، هر فراخوانی یک نمونه جدید ایجاد می‌کند. برای زنجیره‌های ایستا، ثابت یا val خارج از Composable راه حل بهینه است.

چگونه زنجیره تغییردهنده‌ها را اشکال‌زدایی کنیم؟

از Layout Inspector در Android Studio استفاده کنید — مرزهای هر Modifier را به صورت بصری نشان می‌دهد. برای اشکال‌زدایی برنامه‌ای، Modifier.border() را با رنگ‌های مختلف در هر مرحله از زنجیره اضافه کنید تا مرزهای اعمال هر تغییردهنده را ببینید.

Modifier.then() چیست و چه تفاوتی با فراخوانی متوالی دارد؟

Modifier.then(other) زنجیره other را به this متصل می‌کند. فراخوانی متوالی (Modifier.a().b()) معادل Modifier.then(a()).then(b()) است. تفاوتی ندارد — این همان مکانیسم زنجیره است. then() زمانی مفید است که نیاز به اتصال یک زنجیره آماده از یک متغیر دارید.

Modifier چگونه بر معناشناسی Accessibility تأثیر می‌گذارد؟

Modifier.semantics {} مشخص می‌کند که عنصر چگونه برای صفحه‌خوان توصیف شود. Modifier.clickable() به طور خودکار نقش Button و Action(OnClick) را اضافه می‌کند. برای حرکات سفارشی باید semantics را به صراحت مشخص کنید. بدون تغییردهنده‌های معنایی، کاربران TalkBack نمی‌توانند با کامپوننت‌های سفارشی تعامل کنند.

چرا background در Modifier با گوشه‌های گرد کار نمی‌کند؟

Modifier.background(color, shape) با گوشه‌ها کار می‌کند، اما clip() باید قبل از background بیاید تا گوشه‌ها بریده شوند. ترتیب صحیح: clip(shape).background(color). اگر نیاز به بریدن محتوای داخل نیز دارید، از clipToBounds() روی والد استفاده کنید.

خلاصه

  • Modifier — شیء تغییرناپذیر برای توصیف اعلانی ظاهر و رفتار
  • ترتیب تغییردهنده‌ها نتیجه را تعیین می‌کند: padding → clickable در مقابل clickable → padding
  • زنجیره به صورت متوالی ساخته می‌شود، هر عنصر عنصر قبلی را می‌پوشاند
  • Modifier.composed امکان ایجاد تغییردهنده‌های با وضعیت و CompositionLocal
  • عملکرد: زنجیره‌های ایستا را به ثابت‌ها منتقل کنید، برای پویا از remember استفاده کنید
  • معناشناسی: Modifier.semantics برای Accessibility کامپوننت‌های سفارشی الزامی است
  • توصیه: تغییردهنده‌ها را از layout به تزئین، سپس به رفتار مرتب کنید

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

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

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

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