One Off the Slack: How Do We Merge MutableStates?

Colin Lee asked:

Is there a recommended way to merge two MutableStates into one emitting changes from the most recently updated?

Google’s Alex Vanyo wanted some clarification:

When you say you want to merge two MutableStates into one, what do you mean exactly?

Do you want to compute some other state based on two input states?

Google’s Zach Klippenstein echoed the confusion:

The fact that you’re asking a time-related question (“most recently updated”) about states is a red flag that you might be trying to do something that doesn’t make sense with what “state” really means. Conceptually, states just “are”, they don’t “emit” values at a point in time.

It sounds like you might be dealing with events, and trying to create a state that is “the most recently emitted event”. But you shouldn’t use MutableState to communicate about events, Flows are much better suited to that.

Colin explained that the scenario was a bit complicated:

That seems a reasonable point. What I’m aiming to do is to accept updates from a sub-screen. I’m already updating a rememberSaveable mutableState of type TextFieldValue every time the user enters input into a textfield. I also need to be able to open a new composable to allow the user to craft a link and then pass it back into that textfield (inserting it into the text where the TextFieldValue shows the cursor to have been and also updating the cursor position in that TextFieldValue).

Zach then offered this partial implementation:

var mainText by remember { mutableStateOf(TextFieldValue("")) }
var editingLink by remember { mutableStateOf(false) }
var linkText by remember { mutableStateOf("") }

if (editingLink) {
    imeAction = {
      editingLink = false
      mainText = mainText.copy(/* insert linkText at selection position */)
} else {
  // Some event handler that sets editingLink to true

At this point, Colin saw the light:

Oh, so you’re suggesting putting the link editing sub-screen within the same screen, but swapping out UI. This actually might work great.

I was struggling with going to another screen and passing the data back and forth through Jetpack Compose Navigation routes. It seems like these aren’t intended for larger data objects, like a passage of Markdown text. Even URL-encoding the text was causing crashes because it broke parsing the route if there were encoded newlines. This solution should be far more resilient. Thank you!

Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!