![](/img/trans.png)
[英]Collecting StateFlow in compose as lifecycle aware first emit initial value then the original value
[英]How to safely (lifecycle aware) .collectAsState() a StateFlow?
根據這些文章,我正在嘗試遵循官方指南,使用 Compose 從 LiveData 遷移到 Flow/StateFlow:
我正在嘗試遵循第一篇文章中的建議,在接近結尾的 Jetpack Compose 部分的安全流集合中。
在 Compose 中,副作用必須在受控環境中執行。 為此,使用 LaunchedEffect 創建一個遵循可組合項生命周期的協程。 在其塊中,如果您需要它在主機生命周期處於某個 State 時重新啟動代碼塊,則可以在其塊中調用暫停 Lifecycle.repeatOnLifecycle。
我設法以這種方式使用.flowWithLifecycle()來確保當應用程序進入后台時流不會發出:
@Composable
fun MyScreen() {
val lifecycleOwner = LocalLifecycleOwner.current
val someState = remember(viewModel.someFlow, lifecycleOwner) {
viewModel.someFlow
.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.stateIn(
scope = viewModel.viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = null
)
}.collectAsState()
}
我覺得這很“樣板”——一定有更好的東西。 我想在 ViewModel 中使用 StateFlow,而不是在 @Composable 中轉換為 StateFLow 的 Flow,並使用.repeatOnLifeCycle() ,因此我可以使用多個.collectAsState()和更少的樣板。
當我嘗試在協程 (LaunchedEffect) 中使用.collectAsState() 時,我顯然收到了一個錯誤 about.collectAsState() 必須從 @Composable function 的上下文中調用。
我怎樣才能實現與 with.collectAsState() 類似的功能,但在.repeatOnLifecycle() 內部。 我是否必須在 StateFlow 上使用.collect(),然后用 State 包裝該值? 沒有比這更少的樣板嗎?
基於 OP 的回答,如果您不關心WhileSubscribed(5000)
行為,則內部不通過StateFlow
可能會更輕量級。
@Composable
fun <T> Flow<T>.toStateWhenStarted(initialValue: T): State<T> {
val lifecycleOwner = LocalLifecycleOwner.current
return produceState(initialValue = initialValue, this, lifecycleOwner) {
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
collect { value = it }
}
}
}
在閱讀了幾篇文章后,包括
Flow 的 shareIn 和 stateIn 操作符須知
我越來越相信以這種方式使用.flowWithLifecycle() 而不是repeatOnLifecycle() +produceState()。 我剛剛做了這個擴展 function 以在從@Composable 收集流為 State 時減輕樣板:
@Composable
fun <T> Flow<T>.flowWithLifecycleStateInAndCollectAsState(
scope: CoroutineScope,
initial: T? = null,
context: CoroutineContext = EmptyCoroutineContext,
): State<T?> {
val lifecycleOwner = LocalLifecycleOwner.current
return remember(this, lifecycleOwner) {
this
.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = initial
)
}.collectAsState(context)
}
這將在 @Composable 中像這樣使用:
@Composable
fun SomeScreen() {
//...
val someState = viewModel.someFlow
.flowWithLifecycleStateInAndCollectAsState(
scope = viewModel.viewModelScope //or the composable's scope
)
//...
}
我會將其標記為已接受的解決方案,以防它對其他人有所幫助,除非有人在接下來的幾天內讓我知道更好的方法。
從“androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha01”中,您可以使用collectAsStateWithLifecycle()擴展 function 從流/狀態流中收集並以生命周期感知方式將其最新值表示為 Compose State。
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@Composable
fun MyScreen() {
val state by viewModel.state.collectAsStateWithLifecycle()
}
來源: Android 生命周期發布
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.