Skip to content
SuperDragonXD edited this page Apr 2, 2026 · 2 revisions

In Lawnicons, the ViewModel layer serves as a crucial link between the User Interface (UI), built with Jetpack Compose, and the Data layer, which consists of Repositories. ViewModels are responsible for preparing and managing UI-specific state and executing business logic based on user interactions. ViewModels are co-located with their screens under the ui/destination/<feature>/ packages.

Core responsibilities

  • Manage UI State:
    • ViewModels hold data that the UI needs to display (for example, a list of icons, current search query, loading indicators).
    • They expose this state to Composables using kotlinx.coroutines.flow.StateFlow. The UI then observes these flows using collectAsStateWithLifecycle to react to changes.
  • Handle Business Logic:
    • When a user interacts with the UI (for example, types in a search bar or taps a button), the event is passed to the ViewModel.
    • The ViewModel processes these events, which might involve calculations, data validation, or fetching data from a Repository.
  • Lifecycle Awareness:
    • ViewModels use viewModelScope (from AndroidX Lifecycle) to launch coroutines. This ensures that any asynchronous operations are automatically cancelled if the ViewModel is cleared (for example, when the screen is destroyed), preventing memory leaks and unnecessary work.
  • Dependency Injection:
    • ViewModels are annotated with @ViewModelKey and @ContributesIntoMap(LawniconsScope::class). This allows Metro to register them in the ViewModel provider map, which is consumed by MetroViewModelFactory to create instances with their dependencies automatically resolved.

Examples in Lawnicons

  • HomeViewModel (interface) / HomeViewModelImpl (in ui/destination/home/):
    • Manages the state for the main home screen. Exposes a uiState: StateFlow<HomeUiState> sealed interface with Loading and Success states.
    • The Success state bundles the icon list (IconInfoModel), home screen announcements, whether new icons are available (hasNewIcons), and whether icon requests exist (hasIconRequests).
    • It processes search queries via searchIcons() and changeMode(), delegating to the IconRepository. Search results are exposed as searchResults: StateFlow<IconInfoModel>.
  • ContributorsViewModel (in ui/destination/contributors/):
    • Responsible for fetching the list of GitHub contributors from the GitHubContributorsRepository.
    • It exposes the data to the UI using a ContributorsUiState class, which can represent Success (with data), Loading, or Error states. This allows the UI to display appropriate feedback to the user.
  • AcknowledgementsViewModel (in ui/destination/acknowledgements/):
    • Provides details about open-source libraries used in the app, including their license URLs, by interacting with the OssLibraryRepository.
  • NewIconsViewModel (in ui/destination/newicons/):
    • Supplies the list of newly added icons to the "New Icons" screen, sourcing this data from the NewIconsRepository.
  • IconRequestViewModel (in ui/destination/iconrequest/):
    • Manages the icon request screen, providing the list of requestable icons and handling the request flow.
  • DebugMenuViewModel (in ui/destination/debugmenu/):
    • Manages state for the debug menu screen, exposing developer-facing settings and debug information.

Benefits for development

  • Separation of Concerns: ViewModels keep complex logic out of the UI code, making Composables simpler and more focused on presentation.
  • Testability: Because ViewModels don't have direct dependencies on the Android framework UI components (they interact with Repositories and expose state), they are easier to unit test. The use of interfaces (like HomeViewModel) and dummy implementations further aids in creating testable UI previews and unit tests.
  • Configuration Change Resilience: ViewModel instances survive configuration changes (like screen rotation), so UI state is preserved automatically.

Clone this wiki locally