StateFlow の View への公開

以下のような ViewModel があったとして、バッキングプロパティ部分。

どう書いてますか。


class CounterModel {
  private val _counter = MutableStateFlow(0)

  ??? counter ??? = _counter ???

  fun inc() {
    _counter.update { count -> count + 1 } 
  }
}

以下の登場時の開発の様子を参考に。

Introduce StateFlow
👉 Introduce StateFlow · Issue #1973 · Kotlin/kotlinx.coroutines 

StateFlow は、状態を表す更新可能な値の Flow です。

- StateFlow インターフェイスは、現在の値にアクセスするための読み取り専用で、値の更新を collect するための Flow を実装しています。

- MutabaleStateFlow インターフェースは、値を変更する操作を追加しています。

- MutableStateFlow(x) のコンストラクタ関数が用意されています。この関数は、与えられた初期値を持つ MutableStateFlow の実装を返します。値への高速で非リアクティブなアクセスが必要な場合は StateFlow として、値への更新のリアクティブな表示のみが必要な場合は Flow として、外部に公開することができます。

次のようにまとめることができます。


package kotlinx.coroutines.flow

interface StateFlow<T> : Flow<T> {
  val value: T // always availabe, reading it never fails
}

interface MutableStateFlow<T> : StateFlow<T> {
  override var value: T // can read & write value
}

fun <T> MutableStateFlow(value: T): MutableStateFlow<T> // constructor fun

よって、以下、ありがちな記述。(ないか。)


var counter = _counter // NG


val counter = _counter // NG


val counter: MutableStateFlow<Int> = _counter // NG


val counter: StateFlow<Int> = _counter // NG


val counter get() = _counter // NG


val counter: MutableStateFlow<Int> get() = _counter // NG

開発者の間でも、好き嫌いはあるようですが、以下の2パターンが良さげ。Read Only であることが大事。


val counter: StateFlow<Int> get() = _counter  // OK


val counter = _counter.asStateFlow()  // OK

よって、


class CounterModel {
  private val _counter = MutableStateFlow(0)

  val counter = _counter.asStateFlow()

  fun inc() {
    _counter.update { count -> count + 1 } 
  }
}

最近の言語の仕様は、オフィシャルドキュメントでは分かりずらいポリシーが多くあるように思います。

👉 【MVVM】 ViewModel の_プロパティ記述 hatena-bookmark

👉 【MVVM】 Kotlin Flow で使える5つの利用パターン 
👉 StateFlow は distinctUtilChanged 不要