1

I can change my view model's internal state by calling redirection(verified with Log statements), but it doesn't have any effect on gUiState in my main composable. Why is that?

The main composable supposed to manage my screens:

@Composable
fun BigBrother(gViewModel: GViewModel = viewModel()) {
    val gUiState by gViewModel.uiState.collectAsState()

    Log.d(TAG, "Chosen path is ${gUiState.direction}")

    when (gUiState.direction) {
        "START" -> StartScreen()
        "CHOOSER" -> Chooser()
        "GALLERY" -> ScrollItems(catalogue = gUiState.catalogItem)
        else -> StartScreen()
    }
}

@Composable
fun StartScreen(modifier: Modifier = Modifier) {
    Log.d(TAG, "Launching START")

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Button(onClick = {
            GViewModel().redirection("CHOOSER")
            Log.d(TAG, "Button1 clicked, launching CHOOSER")
        }) {
            Text("New")
        }

        Button(onClick = {
            GViewModel().redirection("GALLERY")
            Log.d(TAG, "Button2 clicked, launching GALLERY")
        }) {
            Text("Browse")
        }
    }
}

UI State:

data class GUiState(
    val item: Item = Item(),
    val catalogItem: List<Item> = listOf(Item()),
    val direction: String = "START",
    val parameterChange: Parameter = Parameter("New", 14),
)

View model:

class GViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(GUiState())
    val uiState: StateFlow<GUiState> = _uiState.asStateFlow()

    // ....

    fun redirection(newDirection: String) {
        _uiState.update { currentState -> currentState.copy(direction = newDirection) }
        Log.d(TAG, "changed UIstate direction to ${uiState.value.direction}")
    }
}

To clarify the result, I can see the message from the redirection function in the logcat, thus confirming the update of the _uiState, but then I never see another log message from the main screen about the new choice for the path.

1
  • Please provide a minimal reproducible example that demonstrates how you call redirection, how that changes the view model and how that doesn't affect your composable.
    – Leviathan
    Commented Jul 8 at 7:50

1 Answer 1

2

In StartScreen you do the following:

GViewModel().redirection("CHOOSER")

This creates a new GViewModel instance (with GViewModel()) that you use to set the state by calling redirection.

That's not what you want: You want to change the already existing view model instance because only that is observed for changes. To accomplish that you need to pass StartScreen the function that should be called:

@Composable
fun StartScreen(
    redirection: (String) -> Unit,
    modifier: Modifier = Modifier,
) {
    // ...

    Button(onClick = { redirection("CHOOSER") }) {
    }

    // ...
}

You can then pass the function like this:

@Composable
fun BigBrother(gViewModel: GViewModel = viewModel()) {
    val gUiState by gViewModel.uiState.collectAsState()

    when (gUiState.direction) {
        "START" -> StartScreen(redirection = gViewModel::redirection)
        // ...
    }
}

So whenever your StartScreen calls redirection, what actually is getting executed is redirection from your BigBrother's gViewModel object. And since this object's uiState is observed for changes, now it will actually have an effect.

2
  • Thanks a lot, I don't think I could have seen this problem with the knowledge I had so far, but you made it easy to understand nonetheless! I wish I could send you a pizza or something instead of just saying my thanks! Commented Jul 8 at 9:41
  • Thank's, no pizza needed :). Glad I could help!
    – Leviathan
    Commented Jul 8 at 12:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.