Android Development

Android Modern Architecture: ViewModel, StateFlow, and Compose in 2024

The Android team’s recommended architecture has crystallised in 2024: a unidirectional data flow pattern combining Hilt for dependency injection, Room + StateFlow for the data layer, ViewModel for the presentation layer, and Jetpack Compose for the UI. This stack is opinionated, testable, and the subject of all new official Android documentation.

The Layered Architecture

UI Layer      → Composables + ViewModel
Domain Layer  → Use Cases (optional, for complex apps)
Data Layer    → Repository + Room / Network (Retrofit)

ViewModel with StateFlow

StateFlow replaces LiveData for exposing UI state from the ViewModel. It is a Kotlin-native, lifecycle-aware observable that integrates cleanly with Compose:

data class HomeUiState(
    val posts: List<Post> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null,
)

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val postRepository: PostRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow(HomeUiState(isLoading = true))
    val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()

    init {
        viewModelScope.launch {
            postRepository.getPosts()
                .catch { e -> _uiState.update { it.copy(error = e.message, isLoading = false) } }
                .collect { posts -> _uiState.update { it.copy(posts = posts, isLoading = false) } }
        }
    }
}

Hilt Dependency Injection

// Application
@HiltAndroidApp
class MyApplication : Application()

// Module
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides @Singleton
    fun provideDatabase(@ApplicationContext ctx: Context) =
        Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
}

// ViewModel auto-wired with @HiltViewModel + @Inject

Compose UI Connected to StateFlow

@Composable
fun HomeScreen(vm: HomeViewModel = hiltViewModel()) {
    val uiState by vm.uiState.collectAsStateWithLifecycle()

    when {
        uiState.isLoading -> LoadingIndicator()
        uiState.error != null -> ErrorMessage(uiState.error!!)
        else -> PostList(uiState.posts)
    }
}

Navigation Compose

Use navigation-compose for type-safe navigation between screens. Define your NavGraph in one place and navigate using type-safe route objects (stable in Navigation 2.7+ with the new serialization-based API).

Testing the Stack

  • Unit test ViewModels with UnconfinedTestDispatcher and Turbine for StateFlow
  • Test the Repository with an in-memory Room database
  • UI test Composables with composeTestRule.setContent { }

The 2024 Android architecture stack is mature, well-documented, and backed by years of production experience. If you are starting a new Android project, adopt it wholesale — the investment in structure pays back on the first feature you need to refactor.

Share this post:
Copied!

Leave a Comment

Your email will not be published.

READY TO BUILD?

Let's Build Something
Amazing Together

Tell us about your project. We'll have a proposal in your inbox within 24 hours.

Free Consultation
NDA Available
Fixed-price Options
Dedicated PM