I'm new to Compose framework and StateFlows. I want LinearProgressIndicator to show progress that will be emitted from downloadProgress StateFlow. How can I achieve that?
@Composable
fun DownloadProgressDialog(
downloadProgress: StateFlow<Float>,
onDismissRequest: () -> Unit = {},
) {
// how to pass downloadProgress to LinearProgressIndicator?
var progress by remember { mutableStateOf(0f) }
val animatedProgress = animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
).value
Dialog(onDismissRequest = { onDismissRequest() }) {
Surface {
Column {
LinearProgressIndicator(progress = animatedProgress)
}
}
}
}
You need a State
or MutableState
to schedule recomposition.
https://stackoverflow.com/a/70217911/5457853
And you can have a stateless Composable by changing DownloadProgressDialog input with Float as
@Composable
fun DownloadProgressDialog(
downloadProgress: Float,
onDismissRequest: () -> Unit = {},
) {
val animatedProgress by animateFloatAsState(
targetValue = downloadProgress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
label = "",
)
Dialog(onDismissRequest = { onDismissRequest() }) {
Surface {
Column {
LinearProgressIndicator(progress = animatedProgress)
}
}
}
}
StateFlow has an extension function to represent it as State
@Composable
fun <T> StateFlow<T>.collectAsState(
context: CoroutineContext = EmptyCoroutineContext
): State<T> = collectAsState(value, context)
I made a sample with ViewModel but you can ignore it if not needed and use how StateFlow
is converted to State
class MyViewModel : ViewModel() {
private var _progressFlow = MutableStateFlow(0f)
val progressFlow = _progressFlow.asStateFlow()
init {
viewModelScope.launch {
for (i in 0..100) {
delay(100)
_progressFlow.value = i/100f
println("ViewModel progress: ${progressFlow.value}")
}
}
}
}
@Preview
@Composable
private fun Test() {
val viewModel = remember {
MyViewModel()
}
MyFun(viewModel = viewModel)
}
@Composable
private fun MyFun(viewModel: MyViewModel) {
val progress by viewModel.progressFlow.collectAsState()
DownloadProgressDialog(downloadProgress = progress)
}
@Composable
fun DownloadProgressDialog(
downloadProgress: Float,
onDismissRequest: () -> Unit = {},
) {
val animatedProgress by animateFloatAsState(
targetValue = downloadProgress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
label = "",
)
Dialog(onDismissRequest = { onDismissRequest() }) {
Surface {
Column {
LinearProgressIndicator(progress = animatedProgress)
}
}
}
}