@Composable: nó là gì, chú thích Compose và phạm vi ứng dụng

Tác giả: IT Sectr Đã đăng: 2026-06-27 Thời gian đọc: 8 phút

Chú thích @Composable là một yếu tố cơ bản của Jetpack Compose, biến một hàm Kotlin thông thường thành một khối xây dựng giao diện người dùng theo khai báo. Không có chú thích này, không thể tạo bất kỳ màn hình nào trong phát triển Android hiện đại. Theo Google Android Developers, 2026, hơn 80% các dự án Kotlin mới sử dụng Compose để xây dựng UI, và @Composable là chú thích được sử dụng nhiều nhất trong hệ sinh thái.

Những điểm chính

  • @Composable — một chú thích Kotlin cho phép hàm mô tả UI một cách khai báo
  • Hàm Composable chỉ có thể gọi các hàm Composable khác, tuân thủ ngữ cảnh hợp thành
  • Khởi động lại hàm Composable xảy ra khi tham số đầu vào hoặc trạng thái thay đổi
  • Thứ tự gọi hàm Composable không được bảo đảm — Compose tối ưu việc xây dựng lại UI
  • Đặt tên hàm Composable tuân theo quy tắc PascalCase, giống như mọi thành phần trong Compose

@Composable trong Jetpack Compose là gì

@Composable là một chú thích của ngôn ngữ Kotlin đánh dấu một hàm dùng để mô tả giao diện người dùng trong framework Jetpack Compose. Khi trình biên dịch Kotlin gặp chú thích này, nó sẽ tạo mã bổ sung cho phép hàm hoạt động trong ngữ cảnh hợp thành — hệ thống quản lý cây UI.

Chú thích @Composable được Google giới thiệu vào năm 2021 cùng với phiên bản ổn định đầu tiên của Jetpack Compose 1.0. Trước khi nó xuất hiện, việc phát triển giao diện Android chỉ được thực hiện thông qua đánh dấu XML và hệ thống View. @Composable đã thay đổi cách tiếp cận một cách triệt để: thay vì mô tả UI trong một tệp đánh dấu riêng, nhà phát triển viết giao diện trực tiếp trong Kotlin.

Sự khác biệt chính giữa @Composable và các hàm Kotlin thông thường là khả năng đọc và phản ứng với các thay đổi trạng thái. Khi một biến mà hàm Composable đọc thay đổi giá trị, hệ thống sẽ tự động lên lịch khởi động lại (tái hợp thành) hàm đó. Điều này giải phóng nhà phát triển khỏi việc cập nhật UI thủ công qua findViewById và setText.

Cơ chế bên trong của @Composable dựa trên khái niệm slot — một vùng nhớ đặc biệt được cấp phát cho mỗi hàm trong quá trình hợp thành. Slot này lưu trữ các giá trị được truyền vào hàm, cũng như thông tin dịch vụ cần thiết cho việc so sánh trong các lần gọi tiếp theo.

Cách khai báo hàm Composable

Để khai báo một hàm Composable, chỉ cần thêm chú thích @Composable trước từ khóa fun. Hàm phải nằm trong một gói import chú thích từ androidx.compose.runtime. Nên viết tên hàm bắt đầu bằng chữ hoa — đây là quy ước được chấp nhận rộng rãi trong cộng đồng Compose, giúp phân biệt trực quan các thành phần UI với các hàm thông thường.

kotlin
import androidx.compose.runtime.Composable

@Composable
fun Greeting(name: String) {
    var count by remember { mutableStateOf(0) }
    Column {
        Text("Xin chào, $name!")
        Button(onClick = { count++ }) {
            Text("Đã nhấp $count lần")
        }
    }
}

Các tham số của hàm Composable có thể là bất kỳ thứ gì — kiểu nguyên thủy, chuỗi, lambda, và thậm chí các hàm Composable khác được truyền qua Slot API. Khuyến nghị nên làm cho các tham số bất biến (val) để tránh các tác dụng phụ trong quá trình tái hợp thành. Tất cả dữ liệu có thể thay đổi phải được quản lý thông qua cơ chế trạng thái của Compose.

Các hàm Composable không thể trả về giá trị tùy ý như các hàm thông thường — nhiệm vụ duy nhất của chúng là xây dựng hoặc cập nhật một phần của cây UI. Tuy nhiên, có các mẫu đặc biệt như State Hoisting, trong đó hàm Compose chấp nhận trạng thái và callback thông qua các tham số, vẫn giữ được tính thuần túy và khả năng tái sử dụng.

Các quy tắc hàm Composable trong Kotlin

Hệ thống Compose áp dụng một số hạn chế nghiêm ngặt về cách hàm Composable trông và hoạt động. Quy tắc đầu tiên: hàm Composable chỉ có thể gọi các hàm Composable khác hoặc hàm thông thường không có tác dụng phụ. Điều này đảm bảo tính dự đoán được của quá trình hợp thành và hoạt động chính xác của các tối ưu hóa Compose.

Quy tắc thứ hai liên quan đến thứ tự thực thi. Compose có quyền gọi các hàm Composable theo bất kỳ thứ tự nào, vì vậy mã trong thân của hàm như vậy không được phụ thuộc vào trình tự gọi của các hàm lân cận. Mỗi hàm Composable phải tự đảm bảo ở cấp độ vị trí của nó trong cây UI.

Quy tắc thứ ba — cấm các tác dụng phụ bên trong thân hàm Composable. Các hoạt động như ghi vào cơ sở dữ liệu, gửi yêu cầu mạng hoặc sửa đổi biến bên ngoài chỉ được thực hiện trong các hiệu ứng đặc biệt: LaunchedEffect, DisposableEffect hoặc SideEffect. Vi phạm quy tắc này dẫn đến hành vi không thể đoán trước trong quá trình tái hợp thành.

Quy tắc thứ tư: các hàm Composable phải có tính idempotent. Gọi lại chúng với cùng các đối số sẽ tạo ra cùng một UI. Yêu cầu này cần thiết cho hoạt động chính xác của tối ưu hóa bỏ qua, trong đó Compose bỏ qua việc vẽ lại các hàm mà dữ liệu đầu vào không thay đổi.

kotlin
// Đúng: hàm Composable thuần túy không có tác dụng phụ
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
    Card(modifier = Modifier.clickable { onClick() }) {
        Text(text = user.name)
    }
}

// Sai: tác dụng phụ bên trong thân hàm
@Composable
fun WrongCard(userId: String) {
    // val result = viewModel.loadUser(userId)  // KHÔNG ĐƯỢC PHÉP
    Text("Đang tải...")
}

Ví dụ sử dụng @Composable

Hãy xem một ví dụ thực tế về cách tạo màn hình hồ sơ sử dụng chú thích @Composable. Ở đây chúng tôi minh họa việc kết hợp nhiều hàm Composable, làm việc với trạng thái và các bộ sửa đổi — các yếu tố chính của bất kỳ bố cục Compose nào.

kotlin
@Composable
fun ProfileScreen(userId: String) {
    var isFollowed by remember { mutableStateOf(false) }

    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        ProfileHeader(userId = userId)
        Spacer(modifier = Modifier.height(16.dp))
        StatsRow(posts = 42, followers = 1280)
        Spacer(modifier = Modifier.height(24.dp))
        FollowButton(
            isFollowed = isFollowed,
            onToggle = { isFollowed = !isFollowed }
        )
    }
}

@Composable
fun ProfileHeader(userId: String) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        AsyncImage(model = "https://example.com/avatars/$userId",
            contentDescription = "User avatar")
        Spacer(modifier = Modifier.width(12.dp))
        Text(text = "Người dùng #$userId", style = MaterialTheme.typography.headlineMedium)
    }
}

@Composable
fun StatsRow(posts: Int, followers: Int) {
    Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
        StatItem("Posts", posts)
        StatItem("Followers", followers)
    }
}

@Composable
fun StatItem(label: String, value: Int) {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(text = "$value", style = MaterialTheme.typography.headlineSmall)
        Text(text = label, style = MaterialTheme.typography.bodySmall)
    }
}

Trong ví dụ, mỗi hàm Composable chịu trách nhiệm cho phần màn hình của nó: ProfileScreen quản lý trạng thái tổng thể và sự hợp thành của các hàm con, ProfileHeader hiển thị ảnh đại diện và tên, và StatsRow hiển thị một khối thống kê. Cách tiếp cận này tuân theo nguyên tắc trách nhiệm duy nhất và đơn giản hóa việc tái sử dụng thành phần.

Các loại hàm Composable và mục đích của chúng

Trong Jetpack Compose, có ba loại hàm Composable chính. Loại đầu tiên — container (Row, Column, Box, LazyColumn) — xác định bố trí của các phần tử con. Loại thứ hai — phần tử hiển thị (Text, Image, Icon, Button) — kết xuất các thành phần UI cụ thể. Loại thứ ba — hàm Composable tùy chỉnh — kết hợp các thành phần tích hợp sẵn thành các khối có thể tái sử dụng.

Container khác với các phần tử thông thường ở chỗ chúng chấp nhận một lambda content — tham số cuối cùng có kiểu @Composable () -> Unit. Cơ chế này cho phép xây dựng các cây UI lồng nhau: mỗi container tạo ra một quá trình hợp thành con với ngữ cảnh và vùng nhớ riêng.

Các hàm Composable tùy chỉnh được chia thành hai loại phụ: thông minh (smart) và đơn giản (dumb). Hàm thông minh quản lý trạng thái và logic — chúng chứa các lời gọi đến remember, LaunchedEffect và các API Compose khác. Hàm đơn giản nhận tất cả dữ liệu qua các tham số và chỉ hiển thị chúng. Việc phân chia thành các thành phần thông minh và đơn giản cải thiện khả năng kiểm thử và tái sử dụng mã.

LoạiVí dụMục đích
ContainerColumn, Row, BoxQuản lý bố trí các phần tử con
Phần tửText, Image, ButtonHiển thị nội dung và xử lý đầu vào
Tùy chỉnhProfileCard, UserListKết hợp các thành phần tiêu chuẩn

@Composable và tái sử dụng thành phần

Ưu điểm chính của chú thích @Composable là khả năng tạo các thành phần UI có thể tái sử dụng mà không cần kế thừa và hệ thống phân cấp lớp phức tạp. Không giống hệ thống View, nơi mỗi phần tử tùy chỉnh yêu cầu tạo một lớp Java với các hàm tạo, một thành phần Composable chỉ là một hàm Kotlin với các tham số.

Để đảm bảo tái sử dụng, mẫu Slot API được sử dụng, trong đó hàm Composable chấp nhận các lambda content cho các khu vực khác nhau của bố cục. Ví dụ, thành phần Card có thể chấp nhận nội dung riêng cho tiêu đề, thân và chân trang, khiến nó trở nên phổ quát cho mọi màn hình ứng dụng.

Các bộ sửa đổi (Modifier) đóng vai trò quan trọng trong việc tái sử dụng: chúng cho phép cấu hình khoảng đệm, kích thước, nhấp chuột và hoạt ảnh mà không cần thay đổi thành phần. Nên luôn truyền Modifier như một tham số của hàm Composable với giá trị mặc định: Modifier = Modifier — đây là thực hành tiêu chuẩn được áp dụng trong các thư viện chính thức của Google.

kotlin
@Composable
fun SectionCard(
    modifier: Modifier = Modifier,
    title: String,
    content: @Composable () -> Unit
) {
    Card(modifier = modifier) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(text = title, style = MaterialTheme.typography.titleMedium)
            Spacer(modifier = Modifier.height(8.dp))
            content()
        }
    }
}

Nhờ Slot API, thành phần SectionCard có thể được sử dụng trên các màn hình khác nhau với các nội dung khác nhau — biểu mẫu, danh sách, khối văn bản. Sự kết hợp giữa các bộ sửa đổi và Slot API làm cho các thành phần Compose trở nên cực kỳ linh hoạt mà không mất đi tính an toàn kiểu mà Kotlin cung cấp.

Câu hỏi thường gặp

@Composable khác gì so với hàm Kotlin thông thường?

Một hàm @Composable được thực thi trong ngữ cảnh hợp thành và có thể đọc trạng thái, tự động khởi động lại khi trạng thái thay đổi. Các hàm Kotlin thông thường không có quyền truy cập vào cơ chế theo dõi trạng thái và không tham gia vào việc xây dựng cây UI.

Có thể gọi hàm Composable từ hàm thông thường không?

Không, hàm Composable chỉ có thể được gọi từ các hàm Composable khác, vì cần một ngữ cảnh hợp thành đặc biệt. Để tích hợp mã Compose với Kotlin thông thường, sử dụng phương thức setContent { } trong Activity hoặc ComposeView trong hệ thống View.

Tại sao hàm Composable được viết bằng chữ hoa?

Đây là quy ước đặt tên được chấp nhận trong cộng đồng Compose. Chữ hoa phân biệt trực quan các thành phần UI với các hàm thông thường, tuân theo các quy tắc đặt tên lớp. Đây không phải yêu cầu của trình biên dịch, mà là thực hành được khuyến nghị trong tài liệu của Google.

Có thể có bao nhiêu hàm Composable trên một màn hình?

Không có giới hạn về số lượng. Trong thực tế, một màn hình lớn có thể chứa 50–100 hàm Composable, bao gồm các thành phần tích hợp sẵn (Text, Button) và tùy chỉnh. Compose tối ưu cây hàm và chỉ thực thi những hàm có dữ liệu đầu vào thay đổi.

Hàm Composable có thể trả về giá trị không?

Thông thường, hàm Composable trả về Unit, vì nhiệm vụ của chúng là xây dựng UI. Tuy nhiên, có các hàm chuyên biệt như rememberderivedStateOf được đánh dấu @Composable và trả về giá trị. Đây là một ngoại lệ, không phải quy tắc.

Tổng kết

  • @Composable — chú thích cho mô tả UI khai báo trong Jetpack Compose
  • Hàm Composable chỉ được gọi bên trong các hàm Composable khác trong ngữ cảnh đặc biệt
  • Tính idempotent — mỗi lần thực thi lặp lại với cùng đối số tạo ra cùng một UI
  • Tác dụng phụ bị cấm trong thân hàm — chỉ thông qua LaunchedEffect và SideEffect
  • Slot API và Modifier đảm bảo tái sử dụng thành phần mà không cần kế thừa
  • Hàm container (Row, Column, LazyColumn) chấp nhận lambda content cho các phần tử lồng nhau
  • Khuyến nghị: truyền Modifier như tham số của mỗi hàm Composable tùy chỉnh với giá trị mặc định

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.

Thảo luận dự án

Đọc thêm