Stop Building God ViewModels with “One Screen = One ViewModel”
Using the New Android ViewModel APIs for Lifecycle-Aware Compose Architecture
Stop Building God ViewModels with “One Screen = One ViewModel”
Using the New Android ViewModel APIs for Lifecycle-Aware Compose Architecture

Image created by AI
Why This Matters
In classic Android development, a ViewModel was usually scoped to an Activity, Fragment, or navigation destination.
But in modern Jetpack Compose apps, a single screen often contains:
- dynamically appearing UI
- nested composables
- pagers
- bottom sheets
- temporary feature flows
In those cases, scoping everything to the entire screen becomes too broad.
Sometimes you want a ViewModel to live only as long as a specific composable exists.
This is exactly what the new ViewModel APIs solve.
The Problem with Traditional ViewModel Scoping
A normal viewModel() call uses the nearest ViewModelStoreOwner.
Usually that means:
Activity
└─ NavBackStackEntry
└─ Entire Screen
As a result:
- ViewModels survive longer than necessary
- temporary UI state remains in memory
- pager pages may unintentionally share state
- cleanup timing becomes unclear
This becomes especially problematic in highly dynamic Compose UIs.
1. Scoping a ViewModel to a Specific Composable
The first API introduces a ViewModel lifecycle tied directly to a composable call site.
Example
@Composable
fun RememberViewModelStoreOwnerSample() {
// Create a ViewModelStoreOwner scoped to this specific call site.
// When this composable leaves the composition,
// the associated ViewModelStore will be cleared.
val scopedOwner = rememberViewModelStoreOwner() // *
CompositionLocalProvider(LocalViewModelStoreOwner provides scopedOwner) {
// This ViewModel is scoped to `scopedOwner`.
// It will survive configuration changes but will be cleared when
// the composable is removed from the UI tree.
val viewModel = viewModel { TestViewModel("scoped_data") }
// Use the ViewModel
}
}
What Happens Internally
Screen
└─ RememberViewModelStoreOwnerSample()
└─ rememberViewModelStoreOwner()
└─ LocalViewModelStoreOwner
└─ TestViewModel
The important part is this:
Composable leaves composition
↓
ViewModelStore cleared
↓
ViewModel.onCleared()
This gives Compose something it historically lacked:
- composable-level ViewModel ownership
Why This Is Powerful
This pattern is extremely useful for:

Instead of keeping state alive for the whole screen, the lifecycle now matches the UI itself.
Lifecycle Visualization
Time ──────────────────────────────────────────▶
Screen ██████████████████████████████████████████
Composable ████████████████
ViewModel ████████████████
The ViewModel only exists while the composable exists.
That alignment is the key improvement.
2. Independent ViewModels for Pagers, Tabs, and Lazy Lists
The second API becomes even more interesting for pagers.
Example
@Composable
fun RememberViewModelStoreProviderSample() {
val storeProvider = rememberViewModelStoreProvider() // *
val pages = listOf("Page 1", "Page 2", "Page 3")
HorizontalPager(pageCount = pages.size) { page ->
// Create a ViewModelStoreOwner for the specific page
// using the provider.
val pageOwner = rememberViewModelStoreOwner(
provider = storeProvider,
key = page
)
CompositionLocalProvider(LocalViewModelStoreOwner provides pageOwner) {
val pageViewModel = viewModel {
TestViewModel(pages[page])
}
// Use pageViewModel
}
}
}
The Traditional Pager Problem
Without page-scoped ViewModels:
Pager
├─ Page 1
├─ Page 2
└─ Page 3
All pages
└─ Shared ViewModel
This can lead to:
- mixed UI state
- accidental data sharing
- difficult restoration logic
- unclear ownership
With rememberViewModelStoreProvider
Now each page gets its own lifecycle container.
Pager
├─ Page 1
│ └─ ViewModelStoreOwner
│ └─ ViewModel
│
├─ Page 2
│ └─ ViewModelStoreOwner
│ └─ ViewModel
│
└─ Page 3
└─ ViewModelStoreOwner
└─ ViewModel
Each page becomes independently stateful.
Why This Is Important
This pattern works especially well for:
- tab UIs
- onboarding flows
- media carousels
- large lazy pagers
- multi-step forms
- independently cached content
Each page can now:
- preserve its own state
- clear independently
- avoid cross-page contamination
Configuration Changes Still Work
An important detail:
These scoped ViewModels still survive configuration changes.
Rotation
↓
Composable recreated
↓
Same ViewModel restored
But once the composable permanently disappears:
Composable removed
↓
ViewModel cleared
This creates a very natural lifecycle model.
Why This Fits the Compose Philosophy
Jetpack Compose encourages UI-driven architecture.
Traditionally:
Lifecycle defines UI
Now:
UI defines lifecycle
That inversion is significant.
The lifecycle becomes a consequence of composition structure.
This is much closer to how Compose actually works.
Best Practices
Prefer Scoped Ownership for Temporary UI
Good candidates:
- dialogs
- sheets
- pager pages
- expandable panels
- nested flows
Avoid using the screen-level owner for everything.
Keep Business Domains Isolated
Instead of:
One giant screen ViewModel
Prefer:
Screen - ViewModel
├─ Toolbar
├─ Pager
| ├─ Page - ViewModel
| └─ Page - ViewModel
├─ Detail
└─ BottomSheet - ViewModel
Smaller lifecycles reduce unintended coupling.
Avoid Over-Scoping
Do not create unnecessary ViewModel scopes for tiny stateless UI pieces.
A scoped ViewModel should represent meaningful UI state ownership.
Final Thoughts
**rememberViewModelStoreOwner()and`rememberViewModelStoreProvider()`**introduce something Compose developers have needed for a long time:
composable-level lifecycle ownership
This enables:
- more precise memory management
- isolated state containers
- cleaner architecture
- better pager patterns
- UI-aligned lifecycle handling
As Compose applications become more dynamic, these APIs will likely become foundational patterns for advanced state management.
Thanks for reading! 👏 Clap to support the article ⭐ Follow me for more Android and Jetpack Compose tips 🔔 Turn on email notifications so you never miss a post
메타데이터
- post_id
- 569fe4cb3784
- slug
- stop-building-god-viewmodels-with-one-screen-one-viewmodel-569fe4cb3784
- url
- https://proandroiddev.com/stop-building-god-viewmodels-with-one-screen-one-viewmodel-569fe4cb3784
- canonical_url
- https://proandroiddev.com/stop-building-god-viewmodels-with-one-screen-one-viewmodel-569fe4cb3784
- author_url
- https://medium.com/@chanzmao
- status
- ok
- fetched_at
- 2026-06-09 15:37:30