首页 > 解决方案 > 如何正确使用 StateFlow 和 Jetpack compose?

问题描述

我正在 ViewModel 中进行 API 调用,并在可组合物中观察它,如下所示:

class FancyViewModel(): ViewModel(){
 private val _someUIState =
     MutableStateFlow<FancyWrapper>(FancyWrapper.Nothing)
 val someUIState: StateFlow<FancyWrapper> =
     _someUIState

 fun attemptAPICall() = viewModelScope.launch {
  _someUIState.value = FancyWrapper.Loading
  when(val res = doAPICall()){
   is APIWrapper.Success -> _someUIState.value = FancyWrapper.Loading(res.vaue.data)
   is APIWrapper.Error -> _someUIState.value = FancyWrapper.Error("Error!")
  }
 }
}

在可组合中,我正在听这样的“someUIState”:

@Composable
fun FancyUI(viewModel: FancyViewModel){

 val showProgress by remember {
    mutableStateOf(false)
 }
 val openDialog = remember { mutableStateOf(false) }

 val someUIState =
    viewModel.someUIState.collectAsState()
 
 when(val res = someUIState.value){
  is FancyWrapper.Loading-> showProgress = true
  is FancyWrapper.Success-> {
     showProgress = false
     if(res.value.error)
      openDialog.value = true
     else
     navController.navigate(Screen.OtherScreen.route)
    }
  is FancyWrapper.Error-> showProgress = false
 }

 if (openDialog.value){
  AlertDialog(
   ..
  )
 }

 Scaffold(
  topBar = {
   Button(onClick={viewModel.attemptAPICall()}){
    if(showProgress)
     CircularProgressIndicator()
    else
     Text("Click")
    }
   }
 ){
  SomeUI()
 }

}

我面临的问题是 FancyUI 可组合中 someUIState 的“何时”块代码在可组合重组期间被多次触发,即使没有单击 Scaffold 中的按钮(例如:当 AlertDialog 出现时)。我在哪里做错了?在 Composable 中使用 StateFlow 观察数据的正确更好方法是什么?

标签: androidkotlinandroid-jetpack-composeandroid-jetpackkotlin-stateflow

解决方案


如果您只想处理每个someUIState值一次,则应将其放在 a 中LaunchedEffect并作为键传递someUIState,以便在它更改时重新触发块。

val someUIState by viewModel.someUIState.collectAsState()
LaunchedEffect(someUiState) {
    when(someUiState) {
        // Same as in the question
    }
}

或者,您可以只在LaunchedEffect.

LaunchedEffect(Unit) {
    viewModel.someUIState.collect { uiState -> 
        when(uiState) {
            // Same as in the question
        }
    }
}

推荐阅读