Jetpack Compose πŸš€: Run Preview with Hilt

hilt Dec 16, 2021

How to run preview compassable with hiltViewModel() dependency injection in emulator.



One of the awesome features Jetpack Compose provide is to run any piece of Composable independently speeding up build time, testing Composable in isolation, and developing behavior faster with the use of Preview annotation.

Whenever you slap @Preview annotation over a composable it gives a small icon as in the image below :

Once you click on it and select Run it will run code scoped to this composable in the emulator.

What Magic is does under the hood ⁉️

If you investigate other options, you can see there is modify run configuration

which gives you hint that it has something to do run configuration, can you guess where same option appears in IDE? Hint πŸ‘‡

If you click on Modify Run Configuration from Preview Action, window to modify run configuration for the composable open.



Similarly, exact same window is opened when you open option Edit configuration and select Compose Preview.

making it clear that whenever you add @Preview annotation to a composable and run it, IDE recognise it as a new Run Configuration entry.

What Run Configuration Does πŸ•΅πŸ»β€β™‚οΈ

For Compose Preview behind the scenes it launches an Activity called PreviewActivity from the compose-ui tooling package

Which extends ComponentActivity thus having the functionality to call setContent

which runs the target composable using reflection api :

So basically with some ui-tooling library and IDE magic IDE has given you a shortcut of quick run option.



Issue with Hilt Dependency Injection πŸ›

Dependency Injection plays a major role in software applications in abstracting all the ceremonies related to the creation of dependencies, and Android's ViewModel has been infamous for the complexities involved with its creation.

To ease the pain Google has recommended Hilt as a DI solution than Dagger, abstracting all the boilerplate code behind annotations, which works absolute wonders for our codebase, is not so friendly is UI tooling of Compose, especially with the Preview functionality.

Here is the link for Jetpack Compose hilt Integration in case you want to go-through the API

Compose and other libraries | Jetpack Compose | Android Developers

If you need explanation and advance examples related to Hilt which is not mentioned in guide do drop a post request in form at end of article.

TLDR πŸ“

You use hiltViewModel() function to inject Android ViewModels into your composable

Example of Constructing and Injecting ViewModel Looks like :

Clear and Simple right!, but now if you try to run the Preview Composable :

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:502)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)Β 
     Caused by: java.lang.ClassNotFoundException: Composable Method 'com.proptiger.ui.components.buttons.SampleKt.ButtonPreview' not found
        at androidx.compose.ui.tooling.CommonPreviewUtils.invokeComposableViaReflection$ui_tooling_release(CommonPreviewUtils.kt:196)
        at androidx.compose.ui.tooling.PreviewActivity$setComposableContent$2.invoke(PreviewActivity.kt:72)
        at androidx.compose.ui.tooling.PreviewActivity$setComposableContent$2.invoke(PreviewActivity.kt:71)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:384)

.... and 1000 more lines of bla bla ....

If you analyse the logs it will say ClassNotFoundException, at ui_tooling_release

ClassNotFoundException: Composable Method 'io.github.ch8n.compose97.ui.components.buttons.SampleKt.ButtonPreview' not found at androidx.compose.ui.tooling.CommonPreviewUtils.invokeComposableViaReflection$ui_tooling_release(

Which sounds like UI Tooling Related issue, which might be the case, but this crash doesn't happen if :

If you remove the dependency in the parameter. I didn’t dig more deep into it, but from an overview It appears the source of Β could be Hilt to inject dependency. For Hilt to work Correctly the Parent Activity needs to be marked with @AndroidEntryPoint annotation.



Thus with MainActivity which we have marked @AndroidEntryPoint this code works flawlessly for running into emulator.

But in case of our PreviewActivity which is provided by Compose UI Tooling, this annotation is not provided.

So Actually there is no way right now to make @Preview annotation functional with Hilt unless some compatibility is provided by the Android tooling team.

Are we done? πŸ˜΅β€πŸ’« no solution?

be patient and Wait for a second!

We can't actually do anything about PreviewActivity, but we can make our own run configuration which could run our own PreviewActivity with @AndroidEntryPoint annotation

Setting up Custom Run Configuration πŸ› 

Step 1 : Create an Empty Activity, I call it HiltPreviewActivity, not need to Create Layout for it.

Step 2 : Copy-Paste Following Code

@AndroidEntryPoint
class HiltPreviewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            
        }
    }
}

Note : Don't blindly copy-paste, check the name of your Activity, yours might be different.

Step 3 : Go back to Edit Configurations...



Step 4 : Select + option to add New Configuration.

Step 5 : Select Android App Configuration Option

Step 6 : Β Enter Name, I choose HiltPreview. then Select App Module of your project.

Step 7 : Select Specific Activity from Launch Option Section

Step 8 : Select HiltPreviewActivity , and finally complete with Apply, then ok.

New Option of HiltPreview would be visible in your run configuration .



Do we create our own @Preview Composable? πŸ€”

Well NO!, the slapping @Preview and getting launch icon short cut is IDE magic, we can't really do that, and this is the limitation of this method.

To run your Preview Composable, you need to manually paste them inside of setContent{..} of our created Activity.

Which isn't such a downside if you look at it.

Anyways if anyone has a way to automate this. Do reach me out, but again don't spend that much effort its an overkill.

Note : Don't Forget to Wrap our Composable into MaterialTheme{..} composable else you will miss out fonts and styles.

Conclusion πŸ’†πŸ»β€β™€οΈ

Above method let you add custom run configuration that run Activity with Hilt dependency applied to you don't get the crash. Down side is you need to manually paste preview composable into its set content. Hope it helps and speeds up you android development builds!

Until Next Time ! Happy Hacking πŸ‘¨πŸ»β€πŸ’»

Enjoy the article?

a clap is much appreciated if you enjoyed. No sign up or cost associated :)


Reach me out :

Chetan Gupta
Software engineer πŸ‘¨β€πŸ’», Kotlin πŸš€ Android πŸ“±.



Chetan gupta

Hi there! call me Ch8n, I'm a mobile technology enthusiast! love Android #kotlinAlltheWay, CodingMantra: #cleanCoder #TDD #SOLID #designpatterns

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.