Skip to Content

Modifiers trong Jetpack Compose

Modifiers cho phép bạn trang trí hoặc thay đổi hành vi của một composable. Đây là một trong những khái niệm quan trọng nhất trong Compose mà bạn sẽ sử dụng hàng ngày.

1. Modifier là gì?

Modifier là một chuỗi các “decorators” được áp dụng lên composable để:

  • Thay đổi kích thướcvị trí
  • Thêm background, border, shadow
  • Xử lý sự kiện (click, scroll, drag)
  • Thay đổi accessibility properties
@Composable fun GreetingCard() { Text( text = "Hello Compose", modifier = Modifier .fillMaxWidth() // Chiếm toàn bộ chiều rộng .padding(16.dp) // Thêm padding .background(Color.LightGray) // Nền xám .clickable { } // Có thể click ) }

2. Thứ tự Modifiers RẤT QUAN TRỌNG

Modifiers được áp dụng từ trái sang phải (hoặc từ trên xuống dưới). Thứ tự khác nhau → kết quả khác nhau!

Ví dụ: padding trước vs sau background

// padding TRƯỚC background Modifier .padding(16.dp) // 1. Padding bên ngoài .background(Color.Blue) // 2. Background sau padding // → Background chỉ bao phủ phần trong // padding SAU background Modifier .background(Color.Blue) // 1. Background trước .padding(16.dp) // 2. Padding bên trong // → Background bao phủ cả padding

Ví dụ trực quan

@Composable fun ModifierOrderDemo() { Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { // Padding trước background Box( modifier = Modifier .padding(8.dp) .background(Color.Blue) .size(50.dp) ) // Padding sau background Box( modifier = Modifier .background(Color.Red) .padding(8.dp) .size(50.dp) ) } }

Quy tắc nhớ

  1. Padding ngoài → dùng padding() TRƯỚC background()
  2. Padding trong → dùng padding() SAU background()
  3. Clickable areaclickable() SAU padding() để click cả padding

3. Các Modifiers phổ biến

3.1. Size Modifiers

Modifier .size(100.dp) // Width = Height = 100dp .size(width = 150.dp, height = 100.dp) // Width ≠ Height .width(200.dp) // Chỉ set width .height(100.dp) // Chỉ set height .fillMaxWidth() // 100% parent width .fillMaxHeight() // 100% parent height .fillMaxSize() // 100% cả width và height .fillMaxWidth(0.5f) // 50% parent width .wrapContentSize() // Fit content .requiredSize(100.dp) // Bắt buộc size, ignore constraints

3.2. Padding và Spacing

Modifier .padding(16.dp) // Tất cả các cạnh .padding(horizontal = 16.dp, vertical = 8.dp) .padding(start = 8.dp, end = 16.dp, top = 4.dp, bottom = 12.dp)

3.3. Background và Border

Modifier .background(Color.Blue) // Solid color .background( // Gradient brush = Brush.horizontalGradient( colors = listOf(Color.Blue, Color.Purple) ) ) .background(Color.Blue, RoundedCornerShape(8.dp)) // Với corner .border(2.dp, Color.Black) // Border .border(2.dp, Color.Black, CircleShape) // Border với shape

3.4. Shape và Clip

Modifier .clip(RoundedCornerShape(16.dp)) // Bo góc .clip(CircleShape) // Hình tròn .clip(CutCornerShape(8.dp)) // Cắt góc .shadow(8.dp, RoundedCornerShape(16.dp)) // Shadow

3.5. Click và Touch

Modifier .clickable { /* handle click */ } .clickable( enabled = true, onClick = { } ) .combinedClickable( onClick = { }, onLongClick = { }, onDoubleClick = { } ) .selectable(selected = isSelected, onClick = { }) .toggleable(value = isOn, onValueChange = { })

3.6. Scroll

Modifier .verticalScroll(rememberScrollState()) .horizontalScroll(rememberScrollState()) .scrollable( state = rememberScrollableState { delta -> delta }, orientation = Orientation.Vertical )

3.7. Layout và Alignment

Modifier .offset(x = 10.dp, y = 20.dp) // Di chuyển vị trí .offset { IntOffset(10.dp.roundToPx(), 0) } // Dynamic offset .align(Alignment.Center) // Trong Box .align(Alignment.CenterVertically) // Trong Row .align(Alignment.CenterHorizontally) // Trong Column .weight(1f) // Trong Row/Column

3.8. Drawing

Modifier .alpha(0.5f) // Trong suốt .rotate(45f) // Xoay 45 độ .scale(1.5f) // Scale up 150% .graphicsLayer { // Advanced transformations rotationZ = 45f scaleX = 1.5f translationX = 10f }

4. Scope Safety - Modifiers chỉ dùng trong context

Một số Modifiers chỉ available trong scope nhất định:

4.1. matchParentSize trong Box

Box(modifier = Modifier.size(200.dp)) { // ✅ Chỉ dùng được trong BoxScope Box( modifier = Modifier .matchParentSize() // Khớp với parent, không ảnh hưởng parent size .background(Color.Blue) ) // Nếu dùng fillMaxSize(), nó sẽ yêu cầu parent lớn nhất có thể }

4.2. weight trong Row/Column

Row(modifier = Modifier.fillMaxWidth()) { // ✅ Chỉ dùng được trong RowScope Text("Left", modifier = Modifier.weight(1f)) Text("Right", modifier = Modifier.weight(2f)) } Column(modifier = Modifier.fillMaxHeight()) { // ✅ Chỉ dùng được trong ColumnScope Text("Top", modifier = Modifier.weight(1f)) Text("Bottom", modifier = Modifier.weight(1f)) }

4.3. align trong Box/Row/Column

Box(modifier = Modifier.fillMaxSize()) { // BoxScope.align() Text("Center", modifier = Modifier.align(Alignment.Center)) } Row(modifier = Modifier.height(100.dp)) { // RowScope.align() - chỉ vertical Text("Bottom", modifier = Modifier.align(Alignment.Bottom)) } Column(modifier = Modifier.width(200.dp)) { // ColumnScope.align() - chỉ horizontal Text("End", modifier = Modifier.align(Alignment.End)) }

5. Tái sử dụng Modifiers

5.1. Extract ra biến

// Tạo modifier chung val cardModifier = Modifier .fillMaxWidth() .padding(16.dp) .clip(RoundedCornerShape(8.dp)) .background(Color.White) .shadow(4.dp) @Composable fun ProductCard(product: Product) { Card(modifier = cardModifier) { // ... } }

5.2. Cho phép customize từ bên ngoài

@Composable fun PrimaryButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier // Luôn có parameter này ) { Button( onClick = onClick, modifier = modifier // Apply modifier từ caller trước .height(48.dp) ) { Text(text) } } // Sử dụng PrimaryButton( text = "Submit", onClick = { }, modifier = Modifier.fillMaxWidth() // Caller customize )

5.3. Best Practices

// ✅ ĐÚNG: Modifier parameter ở đầu (sau required params) @Composable fun MyComponent( title: String, // Required modifier: Modifier = Modifier, // Modifier subtitle: String = "" // Optional ) { } // ✅ ĐÚNG: Chain modifier từ caller với internal modifiers @Composable fun CustomCard( modifier: Modifier = Modifier ) { Card( modifier = modifier .fillMaxWidth() // Internal modifiers sau caller's .padding(16.dp) ) { } }

6. Tạo Custom Modifier

6.1. Extension function đơn giản

// Custom modifier để làm round corners với background fun Modifier.roundedBackground( color: Color, cornerRadius: Dp = 8.dp ): Modifier = this .clip(RoundedCornerShape(cornerRadius)) .background(color) // Sử dụng Text( "Custom", modifier = Modifier.roundedBackground(Color.Blue) )

6.2. Modifier với composed

Khi cần tạo state hoặc side effects trong modifier:

fun Modifier.shake(enabled: Boolean): Modifier = composed { val offset = remember { Animatable(0f) } LaunchedEffect(enabled) { if (enabled) { offset.animateTo( targetValue = 10f, animationSpec = spring(stiffness = Spring.StiffnessHigh) ) offset.animateTo(0f) } } this.offset { IntOffset(offset.value.roundToInt(), 0) } }

📝 Tóm tắt

CategoryModifiers
Sizesize, width, height, fillMax*, wrapContent
Spacingpadding, offset
Appearancebackground, border, clip, shadow, alpha
Drawingrotate, scale, graphicsLayer
Interactionclickable, scrollable, draggable
Layoutalign, weight, matchParentSize

Checklist khi dùng Modifiers

  • Thứ tự modifiers có đúng không? (padding vs background)
  • Có truyền modifier parameter cho custom composables không?
  • Modifier từ caller được áp dụng trước internal modifiers?
  • Scope-specific modifiers chỉ dùng trong đúng scope?
Last updated on