Modifier — یک شیء تغییرناپذیر در Jetpack Compose است که ویژگیهای کامپوننت UI را تعریف میکند: اندازه، حاشیهها، پسزمینه، پردازش حرکات و رفتار. تغییردهندهها از طریق فراخوانی متوالی در یک زنجیره ترکیب میشوند و ترتیب اعمال آنها به طور بحرانی بر نتیجه تأثیر میگذارد. به گفته Google Android Developers, 2026، استفاده صحیح از Modifier اساس ساخت رابط کاربری منعطف و کارآمد در UI اعلانی است.
نکات اصلی
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 → size | padding فضا را کاهش میدهد، 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) حیاتی است.
@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 — یک روش کارخانهای است که امکان ایجاد تغییردهندههای ترکیبی را فراهم میکند که میتوانند از سایر تغییردهندهها، LocalComposition و وضعیت محلی استفاده کنند. برخلاف تابع توسعه معمولی، composed هر بار هنگام اعمال یک نمونه ایجاد میکند که امکان داشتن وضعیت خاص خود را در داخل تغییردهنده فراهم میکند.
چه زمانی از composed استفاده کنیم: ترکیبات تکراری تغییردهندهها (مثلاً سبک استاندارد کارت: padding + background + border + clickable)؛ تغییردهندههای با وضعیت (تغییر پسزمینه متحرک هنگام فشار)؛ دسترسی به CompositionLocals (طرح رنگ MaterialTheme، تراکم پیکسل). در موارد معمولی، یک تابع توسعه معمولی بدون composed کافی است.
عملکرد composed: هر فراخوانی یک شیء تغییردهنده جدید ایجاد میکند که میتواند منجر به تخصیصهای اضافی در بازترکیب شود. برای جلوگیری از این، composed را در remember بپیچید. Google توصیه میکند فقط زمانی از composed استفاده کنید که واقعاً به وضعیت یا CompositionLocal در داخل نیاز باشد. برای ترکیبات ایستا از توابع توسعه معمولی استفاده کنید.
// تغییردهنده سفارشی از طریق 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 در هر بازترکیب خودداری کنید. اگر تغییردهنده به دادههای متغیر وابسته نیست — آن را به یک ثابت یا 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 مورد تودرتو شود.
// ✅ خوب: ثابت خارج از 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 تغییرناپذیر است، بنابراین یک شیء را میتوان به طور ایمن در چندین مکان استفاده کرد. با این حال اگر از composed-modifier استفاده میکنید، هر فراخوانی یک نمونه جدید ایجاد میکند. برای زنجیرههای ایستا، ثابت یا val خارج از Composable راه حل بهینه است.
از Layout Inspector در Android Studio استفاده کنید — مرزهای هر Modifier را به صورت بصری نشان میدهد. برای اشکالزدایی برنامهای، Modifier.border() را با رنگهای مختلف در هر مرحله از زنجیره اضافه کنید تا مرزهای اعمال هر تغییردهنده را ببینید.
Modifier.then(other) زنجیره other را به this متصل میکند. فراخوانی متوالی (Modifier.a().b()) معادل Modifier.then(a()).then(b()) است. تفاوتی ندارد — این همان مکانیسم زنجیره است. then() زمانی مفید است که نیاز به اتصال یک زنجیره آماده از یک متغیر دارید.
Modifier.semantics {} مشخص میکند که عنصر چگونه برای صفحهخوان توصیف شود. Modifier.clickable() به طور خودکار نقش Button و Action(OnClick) را اضافه میکند. برای حرکات سفارشی باید semantics را به صراحت مشخص کنید. بدون تغییردهندههای معنایی، کاربران TalkBack نمیتوانند با کامپوننتهای سفارشی تعامل کنند.
Modifier.background(color, shape) با گوشهها کار میکند، اما clip() باید قبل از background بیاید تا گوشهها بریده شوند. ترتیب صحیح: clip(shape).background(color). اگر نیاز به بریدن محتوای داخل نیز دارید، از clipToBounds() روی والد استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید