androidkotlinasynchronousandroid-jetpack-compose

Simplest way of updating a textfield with the results of an async task in JetPack Compose


I'm writing a simple Android application which needs to perform a long running task in the background and then return that response (task length will be up to 10 minutes). There seems to be dozens of way of running async tasks within Kotlin, all of which have their own weird quirks.

The way I want it to work (for testing) is to create a row with the title and a value. It should put in a placeholder text for the value whilst kicking off the async task in the background. When the task has finished I'd expect the value to update. There doesn't need to be any user interaction with the app.

I've tried to do this with LaunchedEffect as this seems to be the least convoluted technique, using a composable function like:

@Composable
fun addLine(name: String, pwd: String)
{
   Row
   {
      var output: String="Not Found"
      Text(name)

      LaunchedEffect(Unit)
      {
         val guess: String=bruteforce(pwd)
         if (guess != "")
         {
            # Value to update the text to
            output=guess
         }
      }
      # This is the text the needs to change
      Text(output)
   }
}

I cannot work out any simple way of doing this, all easily Googleable examples require user interactions or multiple extra step or weird custom libraries. This should be a trivial task, and is in other frameworks, but is totally defeating me.

I'm not too fussed with whether I should do it like this - I just want a simple mechanism of updating one or more text fields with a value that is performed asynchronously. The complex task will only ever be performed once.


Solution

  • You initiate a coroutine scope and run your code. You maintain a state using output. If you update output based on the result, the Text will be updated. This is a straightforward approach to achieve your goal. However, in this type of logic, if your addLine compose function undergoes a complete recompose, your code, which takes 10 minutes to execute, may need to restart.

    For a more robust solution, it would make more sense to utilize a viewModel and a stateFlow. However, since you specifically requested the simplest logic, I implemented it this way

    val scope = rememberCoroutineScope()
    var output by remember { mutableStateOf("") }
    
    scope.launch {
        delay(1000) // here add your logic
    
        output = "your text after logic" // set output to text
    }
    
    Text(output)