为什么我使用 MutableStateFlow 时需要在 Compose UI 中调用 collect?

Why do I need invoke collect in Compose UI when I use MutableStateFlow?

我看过Android官方artical.

我看到 MutableStateFlow 很热 Flow 并且被 Compose 观察到在它们发生变化时触发重组。

代码A来自Android官方文章,没关系。

我很奇怪为什么作者需要调用 collect 来获取代码 A 中 Compose UI 的最新值。

我认为 Compose UI 总能得到 latestNewsViewModel.uiState 的最新值,为什么我不能使用代码 B 做同样的工作?

代码A

class LatestNewsActivity : AppCompatActivity() {
    private val latestNewsViewModel = // getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
     
        lifecycleScope.launch {          
            repeatOnLifecycle(Lifecycle.State.STARTED) {               
                latestNewsViewModel.uiState.collect { uiState ->                
                    when (uiState) {
                        is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
                        is LatestNewsUiState.Error -> showError(uiState.exception)
                    }
                }
            }
        }
    }
}
    
class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {
  
    private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList())) 
    val uiState: StateFlow<LatestNewsUiState> = _uiState
    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews            
                .collect { favoriteNews ->
                    _uiState.value = LatestNewsUiState.Success(favoriteNews)
                }
        }
    }
}

代码B

class LatestNewsActivity : ComponentActivity() {
    private val latestNewsViewModel = // getViewModel()
    override fun onCreate(savedInstanceState: Bundle?) {       
        super.onCreate(savedInstanceState)
        setContent {
            SoundMeterTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Greeting(latestNewsViewModel)
                }
            }
        }    
    }
}

@Composable
fun Greeting(latestNewsViewModel: LatestNewsViewModel) {
    val myUIState by remember{ latestNewsViewModel.uiState }

    when (myUIState) {
         is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
         is LatestNewsUiState.Error -> showError(uiState.exception)
    }
}

//The same

添加内容

致 RaBaKa 78:谢谢!

根据您的意见,我可以使用代码 C 代替代码 A 吗?

代码C

class LatestNewsActivity : ComponentActivity() {
    private val latestNewsViewModel = // getViewModel()
    override fun onCreate(savedInstanceState: Bundle?) {       
        super.onCreate(savedInstanceState)
        setContent {
            SoundMeterTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Greeting(latestNewsViewModel)
                }
            }
        }    
    }
}

@Composable
fun Greeting(latestNewsViewModel: LatestNewsViewModel) {
    val myUIState by remember{ latestNewsViewModel.uiState.collectAsState() }

    when (myUIState) {
         is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
         is LatestNewsUiState.Error -> showError(uiState.exception)
    }
}

//The same

Compose 需要 State 而不是 StateFlow 进行相应的重组,

您可以在 compose

中轻松地将 StateFlow 转换为 State
val myUiState = latestNewsViewModel.uiState.collectAsState()

不需要使用 remember {} 因为你的 StateFlow 来自你的 viewModel,所以它可以在没有 remember

的情况下管理重组

因此,与代码 B 一样,您可以手动 检查 StateFLow 的状态或自动转换为 State[=38] =] 当状态改变时重组。

代码 A 是 XML 做事的方式,您可以在其中调用其他函数,但在 Compose 中,您应该在 viewModel

中执行该步骤

代码 D

class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {


private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList())) 
val uiState: StateFlow<LatestNewsUiState> = _uiState
init {
    viewModelScope.launch {
        newsRepository.favoriteLatestNews            
            .collect { favoriteNews ->
                _uiState.value = LatestNewsUiState.Success(favoriteNews)
            }
      }
   }
}


@Composable
fun Greeting(latestNewsViewModel: LatestNewsViewModel) {
    val myUIState = latestNewsViewModel.uiState.collectAsState()

    Column(modifier = Modifier.fillMaxSIze()) {
         when(myUIState) {
             is LatestNewsUiState.Success -> SuccessComposable(uiState.news)
             is LatestNewsUiState.Error -> showError(uiState.exception) -> ErrorComposable(uiState.exception)
         }
    }

}