0

I have this Composable function that holds multiple states:

@Composable
fun MyComponent() {
    var stringState by remember { mutableStateOf("foo") }
    var booleanState by remember { mutableStateOf(false) }
    var integerState by remember { mutableStateOf(0) }
}

I now want to modify these states from a non-composable function that requests an API every second or so and then update the state in my application depending on the response.

Note that this isn't the actual component but I need an answer applicable to not just one type.

I have read the docs and read the top SO answers but none of them really matched what I was looking for.

Lolmerkat
  • 5
  • 1
  • 4

2 Answers2

0

Can your function return a Flow of API responses? Then you can use collectAsState():

@Composable
fun MyComponent(apiResponses: Flow<Response>) {
    val state by apiResponses.collectAsState(initial = ...)
}
Egor
  • 39,695
  • 10
  • 113
  • 130
  • Sadly not, I appreciate the (very fast) response though. I could try and refactor it to work like that, despite never really having made use of `Flow`s – Lolmerkat Jul 18 '23 at 19:23
0

A solution you could look at is passing callbacks into your functions where you are able to reassign the variables. (since by remember constructors are just that).

Let's say you have a function doStuffWithStringState(), then you could just do something like this:

fun doStuff(onComplete: () -> Unit) {
 // do stuff here
 onComplete()
}

and when calling the function in the composable:

@Composable
fun MyComponent() {
    var stringState by remember { mutableStateOf("foo") }
    var booleanState by remember { mutableStateOf(false) }
    var integerState by remember { mutableStateOf(0) }

     LaunchedEffect(Unit){
         doStuff { stringState = "new state" }
     }
 }
}

If you have a more complex scenario, you could pass the current state as a parameter into the higher order function and then implement a switch in the callback:

enum class CustomState {State1, State2}

fun doStuff(onComplete: (CustomState) -> Unit) {
 // do stuff here
 val resultingState = determineState()
 onComplete(resultingState)
}
doStuff {
   when (it) {
      State1 -> { stringState = "state1" }
   }
}

But i would not recommend this approach, it looks a bit "unclean" in the compose philosophy. Your scenario very much sounds like it might need a refactor into a flow or livedata which is collected into a StateFlow inside the viewmodel and collected as state in the composable, just as you discussed in the other answer. State is mostly meant to be changed either from a data source (i.e. observing a data source) or from user input, i.e. a composable.

Which by the way, composables which modify state are intended to be used exactly this way. For example a TextField has the function parameter onValueChange: (String) -> Unit where you could set the variable from remember just like I described above.

just_deko
  • 1,024
  • 1
  • 14
  • 29