How to Use ViewModel with Jetpack Compose

Betül Necanlı
4 min readJun 18, 2024

--

This article will guide you through integrating ViewModel with Jetpack Compose to manage and display data efficiently.

Understanding ViewModel

ViewModel is part of the Android Architecture Components that helps manage UI-related data in a lifecycle-conscious way. It survives configuration changes and is ideal for holding and managing UI-related data over a longer lifecycle than traditional Android components like activities or fragments.

Setting Up the ViewModel

First, ensure you have Jetpack Compose set up in your project. Here’s a basic setup of ViewModel with Jetpack Compose using Kotlin:

FoodCategoriesUiState: This sealed interface defines the various states that the UI can be in: Loading, Success (with a list of photos), and Error. This helps manage and represent the UI state in a structured way.

FoodCategoriesViewModel: This class extends ViewModel and is responsible for managing the UI-related data for displaying Food Categories photos.

foodCategoriesUiState: This property represents the current state of the UI using mutableStateOf, initialized to Loading. It’s a reactive state that Jetpack Compose can observe and update the UI accordingly. It’s private set to ensure external classes cannot modify it directly.

init block: Upon initialization of FoodCategoriesViewModel, getFoodCategories() is called to immediately initiate the process of fetching Food Categories photos.

getFoodCategories(): This function initiates a coroutine in the viewModelScope. It updates foodCategoriesUiState to Loading before attempting to fetch Food Categories photos asynchronously.
Inside the coroutine:
- It tries to fetch photos from foodCategoriesRepository using getFoodCategories() method.
- If successful (try block), it updates foodCategoriesUiState to Success with the fetched photos.
- If an IOException or HttpException occurs (catch blocks), it updates foodCategoriesUiState to Error.

Factory Companion Object: This object provides a ViewModelProvider.Factory that can be used to instantiate FoodCategoriesViewModel instances. It’s common practice to use a factory pattern when ViewModel instances require dependencies, such as a repository.

viewModelFactory: This is a function provided by the androidx.lifecycle.viewmodel library. It allows you to create a ViewModelProvider.Factory that initializes a ViewModel instance using a lambda (initializer).

initializer Lambda: Inside the lambda, it retrieves the FoodCategoriesRepository dependency from the FoodCategoriesApplication. This is done using the APPLICATION_KEY, which is a key used to retrieve the application instance from a map-like structure (this).

Dependency Injection: By using this factory pattern, FoodCategoriesViewModel can be instantiated with its dependencies injected (foodCategoriesRepository). This promotes separation of concerns and facilitates unit testing by allowing mock implementations of dependencies to be injected.

📖 The FoodCategoriesViewModel class integrates with Jetpack Compose to manage the state of Food Categories photo retrieval from a repository (foodCategoriesRepository). It ensures that the UI (foodCategoriesUiState) reflects whether data is being loaded, successfully fetched, or if an error occurred during fetching. This setup leverages Kotlin coroutines (viewModelScope.launch) for asynchronous operations and Jetpack Compose’s reactive state management (mutableStateOf) for UI updates based on state changes.

HomeScreen

📖 HomeScreen dynamically displays different UI components based on the state managed by FoodCategoriesViewModel

👉 HomeScreen Composable Function:

foodCategoriesUiState: Represents the current state of the UI, managed by FoodCategoriesViewModel.
retryAction: A lambda function passed from FoodCategoriesViewModel to handle retrying fetching photos in case of an error.
modifier: A Compose Modifier that allows specifying layout and appearance properties.

👉 When Expression:

Depending on the foodCategoriesUiState:
Loading: Calls LoadingScreen, which displays a loading indicator.
Success: Calls PhotosGridScreen, passing the list of photos (foodCategoriesUiState.photos) to display a grid of photos.
Error: Calls ErrorScreen, which displays an error message along with a retry button.

Integrating ViewModel with Jetpack Compose

👉 FoodCategoriesApp Composable Function:
@Composable: Indicates that this function defines a composable UI element using Jetpack Compose.
Scaffold: Provides a basic layout structure for the app, including a top bar, content area, and optional floating action button.
Surface: A container for drawing content, in this case, it takes up the entire available space (fillMaxSize() modifier).
viewModel: A composable function from androidx.lifecycle.viewmodel.compose that retrieves a ViewModel instance (FoodCategoriesViewModel in this case) using the provided factory (FoodCategoriesViewModel.Factory).
HomeScreen: Another composable function defined in HomeScreen.kt, which is passed the foodCategoriesUiState and retryAction from the FoodCategoriesViewModel.

👉 ViewModel Integration:
viewModel(factory = FoodCategoriesViewModel.Factory): This line retrieves an instance of FoodCategoriesViewModel using the Factory defined earlier. It manages the state related to fetching and displaying food Categories photos.
The FoodCategoriesViewModel instance (foodCategoriesViewModel) manages the UI state (foodCategoriesUiState) and provides a method (getFoodCategories) to fetch food category photos from a repository.

In summary, FoodCategoriesApp sets up the basic structure of the app using Jetpack Compose’s Scaffold and Surface, integrating FoodCategoriesViewModel to manage UI state and data fetching. HomeScreen, on the other hand, defines the UI components based on different states (Loading, Success, Error) managed by FoodCategoriesViewModel. Together, they demonstrate how to build dynamic and reactive UIs in Android apps using Jetpack Compose and ViewModel architecture.

--

--