Implementing Preview for Composables with Hilt ViewModel injected

While directly injecting ViewModels into composable functions with Hilt DI can make UI previews challenging, there are workarounds to achieve both dependency injection and efficient development.

Building on our previous project using RoomDB, let’s dive into the ContactsView composable function. We’ll examine its declaration and explore its functionalities.

@Composable
fun ContactsView(viewModel: ContactsViewModel = hiltViewModel()) {

Since ContactsView relies on a Hilt-injected ViewModel, creating a direct preview won’t work as Hilt dependency injection isn’t available in preview mode. To address this, we can split ContactsView into two composable functions: ContactsView and ContactsViewContent.

The new composable, ContactsViewContent, will act as a standalone component responsible for displaying the content. It will receive all necessary data and callbacks as parameters. Let’s see how we declare this composable function:

@Composable
private fun ContactsViewContent(
    goToAddNewContact: () -> Unit,
    searchString: String,
    updateSearchString: (String) -> Unit,
    data: List<ContactModel>,
    editContact: (ContactModel) -> Unit,
    deleteContact: (ContactModel) -> Unit
) {

Now that ContactsViewContent handles the content logic, the main ContactsView composable becomes much simpler. Here’s its updated declaration:

@Composable
fun ContactsView(viewModel: ContactsViewModel = hiltViewModel()) {
    ContactsViewContent(
        goToAddNewContact = { viewModel.goToAddNewContact() },
        searchString = viewModel.searchString,
        updateSearchString = { viewModel.updateSearchString(it) },
        data = viewModel.data,
        editContact = { viewModel.editContact(it) },
        deleteContact = { viewModel.deleteContact(it) }
    )
}

With ContactsViewContent decoupled, we can now easily create a preview for it. Since this composable expects a list of ContactsModel objects, we’ll define a sample list within the preview function.

Here’s the complete preview function for ContactsViewContent:

@Preview(name = "en LTR", locale = "en", showBackground = true, backgroundColor = 0xFFFFFFFF)
@Preview(name = "en LTR 2f", locale = "en", fontScale = 2f, showBackground = true, backgroundColor = 0xFFFFFFFF)
@Composable
private fun Preview() {
    val list = listOf(
        ContactModel(
            id = 1,
            firstName = "John",
            lastName = "Doe",
            phoneNumber = "1234567890",
            email = "john.doe@example.com"
        ),
        ContactModel(
            id = 2,
            firstName = "Jane",
            lastName = "Doe",
            phoneNumber = "0987654321",
            email = "jane.doe@example.com"
        )
    )
    RoomDBExampleTheme {
        ContactsViewContent(
            goToAddNewContact = {},
            searchString = "",
            updateSearchString = {},
            data = list,
            editContact = {},
            deleteContact = {}
        )
    }
}

With that, we’ve successfully tackled the preview challenge for composables with Hilt-injected ViewModels. This approach allows you to maintain clean separation of concerns and efficient development.

The ability to preview composables is a powerful tool for developers. It allows for rapid iteration and visual confirmation of UI behavior across various configurations. You can effortlessly test your composable’s appearance in different themes (Light/Dark), font scales, and even locales, streamlining the development process.

For a deeper dive into advanced preview functionalities, refer to the official documentation.

Full source code is available on GitHub

Spread the love

Related Post