Chanzmao ʕ•ᴥ•ʔ Bear Blog

View and State Separation Is No Longer the Main Story in Jetpack Compose

For years, Android developers learned the same architectural principle:

Separate the View from the State.

In the XML View system, this was essential.

The View was mutable. Updating the UI meant calling methods like setText(), setVisibility(), or notifyDataSetChanged(). As applications grew, mixing UI updates with business logic quickly became difficult to maintain.

State
  |
ViewModel
  |
View (XML)

This architecture made sense because the View itself owned a large part of the UI state.

Compose Changed the Relationship

Jetpack Compose fundamentally changed how UI is built.

A Composable doesn’t mutate itself. Instead, it is simply a function of state.

@Composable
fun UserScreen(state: UserState) {
    Text(state.name)
}

When state changes, Compose automatically recomposes the UI.

There is no need to manually update widgets.

As a result, the architectural focus has shifted.

Instead of asking:

How do we separate the View from the State?

we now ask:

Where should this state live?

The Official Documentation Reflects This Shift

The Compose documentation doesn’t primarily discuss separating View and State.

Instead, it introduces State Hoisting.

One of the core principles is:

You should keep state closest to where it is consumed.

This is a significant change in perspective.

If a piece of state is only needed by a single composable, keeping it there is perfectly acceptable.

var expanded by rememberSaveable { mutableStateOf(false) }

The documentation explicitly says:

Keeping UI element state internal to composable functions is acceptable.

In other words, not every piece of UI state belongs in a ViewModel.

ViewModel Is No Longer the Default State Owner

Another interesting statement from the official documentation is:

The lowest common ancestor can also be outside of the Composition. For example, when hoisting state in a ViewModel because business logic is involved.

Notice the wording.

The reason for using a ViewModel is because business logic is involved, not simply because every screen needs one.

The documentation goes even further:

A ViewModel is just an implementation detail of a state holder with certain responsibilities.

This doesn’t reduce the importance of ViewModel.

Instead, it changes its role.

A ViewModel is no longer presented as the only place to store UI state. It is one possible implementation of a state holder, particularly when business logic, lifecycle awareness, or data loading are required.

Think in Terms of State Lifetime

In Compose, different kinds of state naturally belong in different places.

Small UI state
   |
remember
   |
rememberSaveable
   |
Navigation
   |
ViewModel
   |
Repository

Each layer has a different lifetime.

The question is no longer “Should this be in a ViewModel?”

The better question is:

How long should this state live?

Conclusion

Compose hasn’t eliminated the idea of separating UI from state.

Instead, it has made that separation much less of a design challenge because the UI is already declarative.

The more important architectural decision today is determining the appropriate owner and lifetime for each piece of state.

That is why recent Android guidance focuses less on View vs. State, and much more on State ownership and State lifetime.

References

#Android #Flow #Hilt #Jetpack Compose #Kotlin