imn*_*imn 6 android kotlin android-jetpack android-jetpack-compose kotlin-stateflow
我正在 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!")
}
}
}
Run Code Online (Sandbox Code Playgroud)
在可组合项中,我正在听“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()
}
}
Run Code Online (Sandbox Code Playgroud)
我面临的问题是 FancyUI 可组合中的某些 UIState 的“何时”块代码在可组合重组期间被多次触发,即使没有单击 Scaffold 中的按钮(例如:当 AlertDialog 显示时)。我哪里做错了?在 Composable 中使用 StateFlow 观察数据的正确更好方法是什么?
如果您只想处理每个someUIState
值一次,则应该将其放入 a 中并作为键LaunchedEffect
传递,以便每当它更改时都会重新触发块。someUIState
val someUIState by viewModel.someUIState.collectAsState()
LaunchedEffect(someUiState) {
when(someUiState) {
// Same as in the question
}
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以只收集LaunchedEffect
.
LaunchedEffect(Unit) {
viewModel.someUIState.collect { uiState ->
when(uiState) {
// Same as in the question
}
}
}
Run Code Online (Sandbox Code Playgroud)
我对零食栏所做的其他解决方案是通知视图模型数据已被消耗:在您的 FancyUI 中:
...
when(val res = someUIState.value){
is FancyWrapper.Loading-> showProgress = true
is FancyWrapper.Success-> {
...
viewModel.onResultConsumed()
}
is FancyWrapper.Error-> showProgress = false
}
...
Run Code Online (Sandbox Code Playgroud)
在你的视图模型中:
class FancyViewModel() : ViewModel() {
private val _someUIState = MutableStateFlow<FancyWrapper>(FancyWrapper.Nothing)
...
fun onResultConsumed() {
_someUIState.tryEmit(FancyWrapper.Nothing)
}
}
Run Code Online (Sandbox Code Playgroud)
编辑
如果有人仍在寻找这个,这是另一个解决方案:
创建事件类:
/*
* Copyright 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
Run Code Online (Sandbox Code Playgroud)
Event 类最初是为 LiveData 创建的,但在 Flow 中工作得很好,如果已经被消耗,事件的值将是 bull,这样您就可以保存对视图模型的调用。
在您的屏幕中使用它:
...
when(val res = someUIState.value){
is FancyWrapper.Loading-> showProgress = true
is FancyWrapper.Success-> {
res.event.getContentIfNotHandled?.let {
//do stuff here
...
}
}
is FancyWrapper.Error-> showProgress = false
}
...
Run Code Online (Sandbox Code Playgroud)
要在视图模型中使用,您只需为要显示的状态创建一个事件,例如:
_someUIState.tryEmit(FancyWrapper.Success(event = Event(data)))
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6797 次 |
最近记录: |