Issue
uiState
is MutableStateFlow
in ViewModel()
, Code A and Code B can update UI automatically based latest value of the flow.
1: What are differents between handleMeter.uiState.collectAsState()
and remember { handleMeter.uiState }
in Kotlin ?
2:Code C is wrong, I can't wrapped handleMeter.uiState.collectAsState()
with remember
, why? How can I remember the latest value of the flow?
Code A
@Composable
fun Greeting(handleMeter: HandleMeter,lifecycleScope: LifecycleCoroutineScope) {
Column(
modifier = Modifier.fillMaxSize()
) {
var dataInfo = handleMeter.uiState.collectAsState()
Text(text = "My ${dataInfo.value}")
}
..
}
class HandleMeter: ViewModel() {
val uiState = MutableStateFlow<Int>(10)
...
}
Code B
@Composable
fun Greeting(handleMeter: HandleMeter,lifecycleScope: LifecycleCoroutineScope) {
Column(
modifier = Modifier.fillMaxSize()
) {
var dataInfo = remember { handleMeter.uiState }
Text(text = "My ${dataInfo.value}")
}
..
}
//The same
Code C (It's wrong)
@Composable
fun Greeting(handleMeter: HandleMeter,lifecycleScope: LifecycleCoroutineScope) {
Column(
modifier = Modifier.fillMaxSize()
) {
var dataInfo = remember { handleMeter.uiState.collectAsState() }
Text(text = "My ${dataInfo.value}")
}
..
}
//The same
Solution
Attempting to retrieve the state flow value directly from Composable should cause the following error:
StateFlow.value
should not be called within compose.
You should not ignore this error. The reason is that Compose will not be able to recompose when a new value comes from the flow. The only thing that can trigger recomposition in Compose(not counting calling setContent
) is updating a state holder.
collectAsState
function under the hood collects the flow and updates the underlying state holder, which causes recomposition when the flow value is updated.
The basic implementation of collectAsState
can look like this. It actually more optimized, but this code should let you understand what's going on under the hood.
@Composable
fun<T> StateFlow<T>.collectAsState() : State<T> {
val state = remember { mutableStateOf(value) }
LaunchedEffect(Unit) {
collect {
state.value = it
}
}
return state
}
The most clear way is to use it with delegation, so you don't need to add .value
:
val dataInfo by handleMeter.uiState.collectAsState()
Text("My $dataInfo")
There is no need to remember it because it remembers all the necessary data holders under the hood.
Suppose you need to build some logic that should be updated when some action occurs - in this case collect
produces a new value. You must use remember
to remember the mutable state holder, so your function must be annotated with @Composable
. This means you can't call it from another remember
, but you don't need to, because everything you need to remember is already inside your remember
function.
Answered By - Philip Dukhov
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.