Tất Tần Tật Về Các Loại State Trong Jetpack Compose

Jetpack Compose là thư viện UI hiện đại của Android, giúp xây dựng giao diện một cách declarative (). Thay vì “vẽ” UI từng bước, bạn chỉ cần mô tả state hiện tại, và Compose sẽ tự động cập nhật UI tương ứng khi state thay đổi.

🔄 So sánh Imperative vs Declarative

Tiêu chíImperativeDeclarative
Mô tả hành viBạn viết từng bước xử lýBạn mô tả kết quả mong muốn
UI cập nhậtBạn cập nhật thủ côngUI tự cập nhật theo state
Phong cáchLập trình kiểu truyền thốngLập trình hiện đại (React, Compose, SwiftUI)
Quản lý stateThường nằm ngoài UIState tích hợp trong UI
Dễ bảo trìKhó khi UI phức tạpDễ mở rộng và test
Công nghệ AndroidXML + Java/KotlinJetpack Compose

Chính vì vậy, quản lý state là một phần cốt lõi và quan trọng trong mọi ứng dụng Compose.


🚀 Có những loại state nào trong Compose?

Dưới đây là các loại state phổ biến mà bạn sẽ thường xuyên gặp:

  1. remember – Lưu state trong vòng đời của composable.
  2. rememberSaveable – Lưu state qua cả thay đổi cấu hình (rotation, backstack).
  3. ViewModel + StateFlow – Quản lý state ở cấp màn hình / module lớn.
  4. CompositionLocal – Chia sẻ giá trị toàn cục mà không truyền thủ công.
  5. derivedStateOf – Tạo state dẫn xuất để tối ưu recomposition.
  6. rememberUpdatedState – Giữ giá trị cập nhật mới nhất trong coroutine hoặc callback.

🔍 Phân tích chi tiết từng loại state

1. ✅ remember

Giữ giá trị trong lần gọi composable hiện tại. Khi recomposition xảy ra, giá trị vẫn được giữ nguyên.

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Button(onClick = { count++ }) {
        Text("Clicked $count times")
    }
}

➡️ Dùng khi: Bạn chỉ cần giữ state trong vòng đời composable hiện tại (không cần survive qua rotation). Giá trị count sẽ bị reset khi mở màn hình mới, khi rotation .


2. 💾 rememberSaveable

Giống remember, nhưng dùng được với các kiểu primitive và Serializable – giá trị vẫn giữ được sau khi xoay màn hình.

@Composable
fun RememberSaveableDemo() {
    var name by rememberSaveable { mutableStateOf("") }

    TextField(value = name, onValueChange = { name = it })
}

➡️ Dùng khi: Bạn muốn giữ lại dữ liệu đầu vào của người dùng khi thiết bị xoay, khi mở màn hình khác và quay lại.


3. 🧠 ViewModel + StateFlow

Sử dụng ViewModel để tách logic ra khỏi UI và giữ state qua nhiều recomposition.

class CounterViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count.asStateFlow()

    fun increment() {
        _count.value++
    }
}

@Composable
fun CounterWithViewModel(viewModel: CounterViewModel = viewModel()) {
    val count by viewModel.count.collectAsState()

    Button(onClick = { viewModel.increment() }) {
        Text("Clicked $count times")
    }
}

➡️ Dùng khi: Bạn cần quản lý logic, xử lý dữ liệu, hoặc chia sẻ state giữa nhiều composable.


4. 🌍 CompositionLocal

Cho phép bạn chia sẻ giá trị giữa các composable mà không cần truyền trực tiếp qua props.

val LocalUserName = compositionLocalOf { "Guest" }

@Composable
fun AppContent() {
    CompositionLocalProvider(LocalUserName provides "John") {
        WelcomeUser()
    }
}

@Composable
fun WelcomeUser() {
    Text("Hello, ${LocalUserName.current}")
}

➡️ Dùng khi: Cần chia sẻ giá trị toàn cục như theme, locale, session, user info…


5. ⚙️ derivedStateOf

Tạo state dẫn xuất từ state gốc, chỉ recompute khi giá trị phụ thuộc thay đổi. Giúp tối ưu hiệu năng.

@Composable
fun DerivedStateExample() {
    var text by remember { mutableStateOf("") }

    val isValid by remember {
        derivedStateOf { text.length > = 5 }
    }

    Column {
        TextField(value = text, onValueChange = { text = it })
        Text(if (isValid) "Valid" else "Too short")
    }
}

➡️ Dùng khi: Bạn có logic tính toán phụ thuộc vào state khác và muốn tránh recomposition không cần thiết.


6. 🔄 rememberUpdatedState

Giúp callback hoặc coroutine luôn sử dụng giá trị mới nhất thay vì bị giữ lại giá trị cũ khi khởi tạo.

@Composable
fun TimerWithMessage(message: String) {
    val latestMessage by rememberUpdatedState(message)

    LaunchedEffect(Unit) {
        delay(5000)
        Log.d("TAG", "After delay: $latestMessage")
    }
}

➡️ Dùng khi: Bạn dùng coroutine hoặc listener dài hạn và muốn truy cập giá trị mới nhất từ state.


✅ Kết luận

Việc chọn đúng loại state sẽ giúp ứng dụng Compose của bạn mượt mà, dễ bảo trì và tránh bug không đáng có.

State TypeKhi nào nên dùng
rememberState đơn giản, không cần giữ khi config thay đổi
rememberSaveableCần giữ state sau khi xoay màn hình
ViewModel + StateFlowQuản lý logic, xử lý và chia sẻ state
CompositionLocalGiá trị toàn cục (theme, user…)
derivedStateOfTối ưu tính toán state dẫn xuất
rememberUpdatedStateĐảm bảo callback hoặc coroutine luôn dùng dữ liệu mới nhất

Nguyễn Linh

Chia sẻ để cùng tiến bộ...