Composable Function là một đơn vị cơ bản của giao diện người dùng trong Jetpack Compose, xác định một phần của màn hình sẽ trông như thế nào và hoạt động ra sao. Mỗi hàm như vậy được đánh dấu bằng chú thích @Composable và thực thi trong một ngữ cảnh đặc biệt cho phép Compose theo dõi các phụ thuộc và tự động xây dựng lại UI khi dữ liệu thay đổi. Theo Google Android Developers, 2026, việc xây dựng đúng các hàm Composable ảnh hưởng trực tiếp đến hiệu suất ứng dụng và hiệu quả tái tổ hợp.
Điểm chính
Composable Function là một hàm trong ngôn ngữ Kotlin, được đánh dấu bằng chú thích @Composable, mô tả một phần của giao diện người dùng theo cách khai báo. Thay vì tạo và cấu hình các đối tượng View qua mã Java hoặc đánh dấu XML, nhà phát triển chỉ cần viết UI sẽ trông như thế nào cho mỗi trạng thái dữ liệu.
Sự khác biệt chính giữa hàm Composable và hệ thống View truyền thống của Android nằm ở mô hình cập nhật. Trong cách tiếp cận cổ điển, nhà phát triển tự gọi findViewById, thay đổi văn bản qua setText và quản lý khả năng hiển thị qua setVisibility. Composable Function giải phóng bạn khỏi thói quen này: khi dữ liệu thay đổi, hệ thống tự xác định hàm nào cần tái tổ hợp và chỉ thực thi những hàm đó.
Trình biên dịch Kotlin, khi xử lý chú thích @Composable, tạo ra mã bổ sung tích hợp hàm vào cơ chế tổ hợp. Mã này bao gồm đọc và ghi vào các slot — ô nhớ đặc biệt lưu trữ trạng thái và tham số của mỗi hàm Composable trong cây UI hiện tại. Nhờ sự tích hợp này, Compose biết hàm nào phụ thuộc dữ liệu nào.
Cú pháp của hàm Composable rất ngắn gọn: chỉ cần thêm @Composable trước từ khóa fun. Hàm có thể chấp nhận bất kỳ tham số nào, bao gồm các lời gọi Composable khác trong thân hàm và sử dụng các cấu trúc Kotlin — điều kiện, vòng lặp, biểu thức when — để hiển thị UI có điều kiện.
@Composable
fun ProductItem(
product: Product,
modifier: Modifier = Modifier,
onAddToCart: () -> Unit
) {
Card(modifier = modifier.padding(8.dp)) {
Row(modifier = Modifier.fillMaxWidth().padding(12.dp),
verticalAlignment = Alignment.CenterVertically) {
Column(modifier = Modifier.weight(1f)) {
Text(text = product.name, style = MaterialTheme.typography.titleMedium)
Text(text = "${product.price}", color = MaterialTheme.colorScheme.primary)
}
Button(onClick = onAddToCart) {
Text("Thêm vào giỏ")
}
}
}
}
Trong ví dụ này, hàm Composable ProductItem chấp nhận một đối tượng Product, một bộ sửa đổi và một callback. Cả ba tham số đều bất biến, đảm bảo hành vi có thể dự đoán trong quá trình tái tổ hợp. Bộ sửa đổi được truyền dưới dạng tham số với giá trị mặc định — đây là thực hành tiêu chuẩn cho phép người gọi tùy chỉnh phần đệm và kích thước.
Bên trong hàm Composable, các thành phần Material Design tích hợp sẵn (Text, Button, Card, TextField) hoặc các nguyên thủy cơ bản (Canvas, Layout) được sử dụng. Mỗi thành phần chấp nhận tham số để cấu hình giao diện và hành vi, cũng như một hoặc nhiều bộ sửa đổi qua tham số modifier.
Bộ sửa đổi là một chuỗi các hàm thay đổi kích thước, vị trí, xử lý sự kiện và giao diện của thành phần. Thứ tự của các bộ sửa đổi trong chuỗi rất quan trọng: clickable.semantics hoạt động khác với semantics.clickable, và padding.background tô màu cả khu vực lẫn nền bao gồm cả phần đệm, điều này rất quan trọng khi thiết kế.
Bên trong hàm Composable, bạn có thể sử dụng điều kiện if và when để hiển thị có điều kiện các phần UI, cũng như vòng lặp for cho danh sách động. Tất cả các cấu trúc này hoạt động tự nhiên vì Kotlin là ngôn ngữ lập trình hoàn chỉnh. Tuy nhiên, điều quan trọng cần nhớ là: nếu một điều kiện hoặc vòng lặp chứa lời gọi hàm Composable, chúng cũng tham gia vào quá trình tái tổ hợp.
@Composable
fun ProductList(
products: List<Product>,
modifier: Modifier = Modifier
) {
LazyColumn(modifier = modifier) {
items(products, key = { it.id }) { product ->
ProductItem(
product = product,
onAddToCart = { /* add to cart */ }
)
}
}
}
Hãy xem xét ví dụ về màn hình tìm kiếm sản phẩm sử dụng nhiều hàm Composable. Các mẫu điển hình được hiển thị ở đây: trường nhập liệu có trạng thái, lọc danh sách, xử lý kết quả trống và tải.
data class Product(
val id: String,
val name: String,
val price: Double,
val category: String
)
@Composable
fun SearchScreen() {
var query by remember { mutableStateOf("") }
val products = remember(query) { getFilteredProducts(query) }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
OutlinedTextField(
value = query,
onValueChange = { query = it },
label = { Text("Tìm kiếm sản phẩm") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
when (products) {
is Loading -> CircularProgressIndicator()
is Empty -> Text("Không tìm thấy kết quả")
is Result -> LazyColumn {
items(products.items, key = { it.id }) { product ->
ProductItem(product = product, onAddToCart = {})
}
}
}
}
}
Ví dụ này thể hiện một số idiom cùng một lúc: remember để giữ trạng thái truy vấn tìm kiếm, remember(query) để lọc với khóa, when cho ba trạng thái UI và LazyColumn để hiển thị danh sách hiệu quả. Mỗi idiom này là kết quả của kinh nghiệm thực tế trong phát triển ứng dụng Compose.
Hàm Composable chấp nhận tham số giống như hàm Kotlin thông thường, nhưng có một điểm khác biệt quan trọng: một tham số có thể là một hàm Composable khác được truyền qua lambda với chú thích @Composable. Cơ chế này được gọi là Slot API và là mẫu chính để tạo các container có thể tái sử dụng.
Slot API giải quyết vấn đề mà trong hệ thống View truyền thống được giải quyết qua ViewGroup và thêm View con theo chương trình. Thay vì các phương thức addView, Compose sử dụng lambda content — tham số cuối cùng có kiểu @Composable () -> Unit. Người gọi truyền bất kỳ UI nào vào lambda này và container chỉ định nghĩa bố cục của nó.
Các tham số của hàm Composable có thể có giá trị mặc định, giúp đơn giản hóa việc sử dụng chúng trong các ngữ cảnh khác nhau. Khuyến nghị chỉ bắt buộc các tham số mà không có chúng hàm không thể thực hiện nhiệm vụ và cung cấp giá trị mặc định hợp lý cho phần còn lại.
| Tham số | Kiểu | Ví dụ |
|---|---|---|
| Bắt buộc | Bất kỳ kiểu | name: String |
| Tùy chọn | Có giá trị mặc định | modifier: Modifier = Modifier |
| Content | @Composable () -> Unit | content: @Composable () -> Unit |
| Callback | Lambda không @Composable | onClick: () -> Unit |
Một số idiom đã được thiết lập trong cộng đồng Compose giúp làm cho hàm Composable dễ đọc hơn và có thể dự đoán được. Đầu tiên là State Hoisting: trạng thái được nâng lên mức cao hơn và hàm Composable nhận nó qua tham số. Điều này làm cho hàm trở nên thuần khiết và có thể tái sử dụng trong các ngữ cảnh khác nhau.
Idiom thứ hai là tham số Event-driven. Thay vì truyền ViewModel hoặc useCase vào hàm Composable, chỉ các callback cụ thể được truyền: onSave, onDelete, onNavigateToDetail. Điều này giảm sự kết nối và đơn giản hóa việc kiểm thử.
Idiom thứ ba là CompositionLocal để truyền dữ liệu dùng chung qua cây tổ hợp. Chủ đề, mật độ màn hình, Route hiện tại — tất cả đều được truyền qua CompositionLocal, tránh các chuỗi tham số qua hàng chục hàm Composable. Tuy nhiên, không nên lạm dụng CompositionLocal: tham số rõ ràng luôn được ưu tiên hơn phụ thuộc ngầm.
// State Hoisting: trạng thái được nâng lên hàm cha
@Composable
fun CounterDisplay(
count: Int,
onIncrement: () -> Unit
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Đếm: $count", style = MaterialTheme.typography.headlineLarge)
Button(onClick = onIncrement) {
Text("+1")
}
}
}
// Sử dụng với State Hoisting
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
CounterDisplay(
count = count,
onIncrement = { count++ }
)
}
Câu hỏi thường gặp
Có, return được phép nhưng cẩn thận. Compose tối ưu hóa việc tái tổ hợp ở mức hàm riêng lẻ và return sớm có thể phá vỡ tối ưu hóa này. Tốt hơn nên sử dụng toán tử điều kiện if hoặc when trong thân hàm.
Trong Kotlin, Unit là một đối tượng singleton, không phải kiểu rỗng. Hàm Composable trả về Unit, có nghĩa kỹ thuật là chúng trả về chính đối tượng Unit. Tuy nhiên, trong thực tế điều này không quan trọng.
Truyền bộ sưu tập có thể thay đổi là có thể nhưng là thực hành tồi. Nếu bộ sưu tập thay đổi, Compose sẽ không biết vì tham chiếu đối tượng vẫn giữ nguyên. Sử dụng danh sách bất biến hoặc mutableStateListOf cho các thay đổi được theo dõi.
Để gỡ lỗi, sử dụng Android Studio với Layout Inspector, hiển thị cây hàm Composable hiện tại, giá trị tham số và lý do tái tổ hợp. Trình gỡ lỗi Kotlin thông thường cũng hoạt động.
Hàm Composable luôn trả về Unit, vì vậy kiểu trả về không được chỉ định. Cố gắng trả về kiểu khác sẽ gây lỗi biên dịch vì chú thích @Composable không tương thích với kiểu trả về không phải Unit.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm