- @Composable
- fun <T> StateFlow<T>.collectAsState(
- context: CoroutineContext = EmptyCoroutineContext
- ): State<T> = collectAsState(value, context)
- @Composable
- fun <T : R, R> Flow<T>.collectAsState(
- initial: R,
- context: CoroutineContext = EmptyCoroutineContext
- ): State<R> = produceState(initial, this, context) {
- if (context == EmptyCoroutineContext) {
- collect { value = it }
- } else withContext(context) {
- collect { value = it }
- }
- }
例えばサーバーからデータをとってきて表示する画面があり、画面の状態を表す UiState が次のようになっているとします。
- sealed interface UiState {
- object Initial : UiState
- object Loading : UiState
- data class Error(val e: Exception) : UiState
- data class Success(val profile: Profile) : UiState
- }
(* 私は基本的には ViewModel からは StateFlow ではなく State を公開するようにしています。)
Flow.collectAsState() の場合
- class ProfileViewModel : ViewModel() {
- val uiState: Flow<UiState> = ...
- ...
- }
- @Composable
- fun ProfileScreen(
- viewModel: ProfileViewModel
- ) {
- ProfileContent(
- uiState = viewModel.uiState.collectAsState(initial = UiState.Initial).value
- )
- }
- @Composable
- private fun ProfileContent(
- uiState: UiState
- ) {
- when (uiState) {
- UiState.Initial,
- UiState.Loading -> {
- ...
- }
- is UiState.Error -> {
- ...
- }
- is UiState.Success -> {
- ...
- }
- }
- }
UiState.Initial → (ViewModel でデータ取得開始)→ UiState.Loading → (取得成功)→ UiState.Success
ここで ProfileScreen から Navigation Compose で別の画面に行って戻ってくると、一瞬 UiState.Initial になります。
UiState.Success → (Navigation Compose で別の画面に行って戻ってくる) → UiState.Initial → UiState.Success
StateFlow.collectAsState() の場合
- class ProfileViewModel : ViewModel() {
- val uiState: StateFlow<UiState> = ...
- ...
- }
- @Composable
- fun ProfileScreen(
- viewModel: ProfileViewModel
- ) {
- ProfileContent(
- uiState = viewModel.uiState.collectAsState().value
- )
- }
UiState.Initial → (ViewModel でデータ取得開始)→ UiState.Loading → (取得成功)→ UiState.Success
一方、ここで ProfileScreen から Navigation Compose で別の画面に行って戻ってきても UiState.Initial にはなりません。
UiState.Success → (Navigation Compose で別の画面に行って戻ってくる) → UiState.Success
そのため、ViewModel から StateFlow を公開して StateFlow の collectAsState() を使ったほうがよいです。
stateIn() を使えば Flow を StateFlow に変換することができます。