vinaygaba / Learn-Jetpack-Compose-By-Example

🚀 This project contains various examples that show how you would do things the "Jetpack Compose" way

Home Page:https://jetpackcompose.app/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

I hope there is an example of a button triggering the thread to update the progress bar.

nieaowei opened this issue · comments

I hope there is an example of a button triggering the thread to update the progress bar.

Could you elaborate a bit more? When you say thread, did you mean it running on a different thread/background thread or something?

LinearProgressIndicator(progress = value )
Use Button to start the thread to update this value, the UI should be updated synchronously, but I can't do it.
fun countdownFlow() = flow<Float> { for (i in 0..10) { delay(1000L) emit(i*0.1f) } }
GlobalScope.launch { for (index in 0..10) { progress.value =0.1*index sleep(1000 ) } }
I have tried using GlobalScope.launch and Flow, the former will update the UI after the end of the loop, the latter cannot trigger the update UI through the Button.

In fact, a typical application scenario is to download files.

thanks.

I see what you mean! I have a very similar example where I am doing a countdown using coroutine flow's - https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/blob/master/app/src/main/java/com/example/jetpackcompose/state/coroutine/CoroutineFlowActivity.kt

I think this is what you need right?

I have referred to it, but I cannot use Button to start this thread, and the thread is already started when he enters Compose.

fun countdownFlow() = flow<Float> {
    for (i in 0..10) {
        delay(1000L)
        emit(i*0.1f)
    }
}

@Composable
fun UI(){
    var flow :Flow<Float> = countdownFlow()
    val countDownValue by flow.collectAsState(initial = 0.1f)
    Column(modifier =  Modifier.padding(top = 10.dp)) {
        LinearProgressIndicator(progress = countDownValue )

    }

}

This is what I wrote based on the code snippet you provided.
When I enter UI, the countdownFlow() method has already started.

I know what's going on! So the flow starts "collecting" values as soon as the collectAsState method is called. So you would want to ensure that the collect method is called only once the button is clicked. I'll be adding this example with comments once I get a chance but in order to unblock you, here's what the logic needs to look like -

fun countdownFlow() = flow<Float> {
    for (i in 0..10) {
        delay(1000L)
        emit(i*0.1f)
    }
}

fun staticFlow() = flow<Float> {
    emit(0f)
}

@Composable
fun UI(){
    var flow1 :Flow<Float> = countdownFlow()
    var flow2 :Flow<Float> = staticFlow()
    val buttonClicked by state { 0 }
    val countDownValue = if (buttonClicked) {
        flow1.collectAsState(initial = 0.1f)
    } else {
        flow2.collectAsState(initial = 0f)
   }

    Text("Start Countdown", modifier =  Modifier.clickable(onClick = {
            buttonClicked = true
    }))
    Column(modifier =  Modifier.padding(top = 10.dp)) {
        LinearProgressIndicator(progress = countDownValue )
    }

}

I thought about it this way.
I thought there would be a better way to deal with this problem.
Thank you for your answers.

I'd be curious to learn about a better way as well. Let me know if you come up with an alternate approach!

You can also move the business logic somewhere else (like a view model) to clean up the logic here so that the composable is just listening to the Flow exposed by the view model (which in turn could have some logic to know the button was clicked and based on that return the correct stream of values). But the overall logic remains the same, just your composable looks cleaner.