0

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)
            }
        }
    }
}
Egis
  • 5,081
  • 5
  • 39
  • 61

1 Answers1

1

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)
            }
        }
    }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222