Android Compose UI 狀態管理 (State, MutableState, remember)

在宣告式 UI 中,UI 是狀態 (State) 的函數UI = f(State)

如果狀態不改變,UI 就不會改變。因此,學習 Compose 的關鍵就在於學習如何管理狀態。

什麼是 State?

在 Compose 中,State<T> 是一個持有值並能通知 UI 更新的介面。最常用的是 MutableState<T>

MutableState 的值改變時,讀取該值的 Composable 函式會自動觸發 Recomposition

remember 的作用

Composable 函式可能會被頻繁地重新執行 (Recomposition)。如果你直接在函式裡定義變數:

@Composable
fun Counter() {
    // ❌ 錯誤:每次重組時,count 都會被重置為 0
    var count = mutableStateOf(0) 

    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

你需要使用 remember 來告訴 Compose:「請幫我在記憶體中記住這個值,即使發生重組也不要遺忘」。

@Composable
fun Counter() {
    // ✅ 正確:使用 remember 保存狀態
    val count = remember { mutableStateOf(0) }

    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

Property Delegate (屬性委派)

為了讓語法更簡潔,我們通常使用 Kotlin 的 by 關鍵字:

// 需要 import androidx.compose.runtime.*
var count by remember { mutableStateOf(0) }

Button(onClick = { count++ }) {
    Text("Count: $count") // 直接使用 Int,不需要 .value
}

rememberSaveable (跨越組態變更)

remember 只能在重組 (Recomposition) 期間保存狀態。但是當發生 組態變更 (Configuration Change),例如螢幕旋轉時,Activity 會被重建,remember 的值會遺失。

如果你希望狀態在螢幕旋轉後依然保留,請使用 rememberSaveable

var count by rememberSaveable { mutableStateOf(0) }

它會自動將資料儲存到 Bundle 中,並在重建後恢復。注意:儲存的資料必須是可序列化的 (Serializable/Parcelable) 或是基本型別。

Stateless vs Stateful

  • Stateful Composable:內部持有並管理自己的狀態(使用 remember)。優點是好用,缺點是難以測試與重用。
  • Stateless Composable:不持有狀態,狀態由外部參數傳入,事件由外部 Lambda 處理。優點是易於測試與重用。

最佳實踐:State Hoisting (狀態提升)。 盡量讓底層的 Composable 保持 Stateless,將狀態「提升」到上層的 Composable 或 ViewModel 中管理。

小結

  • State 驅動 UI 更新。
  • remember 防止重組時狀態遺失。
  • rememberSaveable 防止旋轉螢幕時狀態遺失。