Using PreviewParameters and providing Composables to Jetpack Compose Previews

How to generate Jetpack Compose Previews from @Composable annotated data

Katie Barnett
ProAndroidDev

--

If you have been using Jetpack Compose for a while you will have no doubt become tired of writing previews for every single configuration of your composables, and become lost in large files full of almost-but-not-quite duplicated code producing all of these previews.

We have PreviewParameter to the rescue, but when you want to pass composables or theming to your previews you may have come across a familiar error:

@Composable invocations can only happen from the context of a @Composable function

How can we get around this? Go back to the old way of duplicating each preview and changing the colors and content configuration manually? Not use Material theme values or flexible slot based layouts?

Fear not, there is a way!

First, a recap on PreviewParameter

You may already be familiar with Preview Parameters, but if not, here is a quick summary. Preview parameters are a way of passing a list of data into your preview and generating a separate preview for each data item in the list. You can also pass the same data into multiple previews and generate versions for each preview you need.

They are simple to set up, you first need a PreviewParameterProvider which creates a list of values, one for each preview configuration you wish to generate:

This can then be passed to the preview as a parameter:

And this will generate a preview for each data item:

Previews for each data element

You can only pass in one preview parameter per preview, so if you have multiple configuration values to change you will need to create your own custom object.

In this case, I want to pass in the text and a color for the text, so I have a configuration object:

Which can then be passed to the PreviewParameterProvider and use in the Preview, accessing each configuration value via the data parameter.

Resulting in the previews:

Previews for each data element, now with color!

Composables in Preview Parameters

As you can see in the data configuration above, I am passing in a Color object to my TextConfig objects, but what if instead we want to use a Material theme colorSchemevalue? Unfortunatly theme colorScheme values are composables, passing these into a PreviewParameterProvider results in an error:

Attempting to access a colorScheme value in a PreviewParameterProvider

The initial way I found to get around this was to not use preview parameters at all and instead create the data within the preview composable itself.

This does work, but has the disadvantage of only creating one preview, requiring you to set up the component repeating within a column or similar and may give a false impression of how the components exist on their own.

Two configurations within the one preview

Using a Kotlin Functional Interface

A Kotlin functional interface, or a Single Abstract Method (SAM) interface allows you to hide the fact an object contains composables from the PreviewParameterProvider.

To do this, create an interface that has a @Composable annotated function:

Then create each configuration object using this interface, for better code readability we can use a lambda when creating the object:

These could also be created in a separate file or at the bottom of the file to ease readability

These can then be passed into the PreviewParameterProvider sequence

And finally, used in the Preview by accessing the value() function

Giving a set of individual previews:

While this does involve adding a bit more boilerplate (since each config object must be created separately), it does have the following advantages:

  • Configuration objects can be sensibly named for better code documentation
  • Configuration objects and preview parameter lists can be set up and stored in a separate file, making your actual UI code cleaner and easier to understand
  • Configuration objects can be recombined in other PreviewParameterProvider sequences and could be used for many previews. For example, for one component you may only want a subset of previews generated.

Passing in a full Composable as configuration

Using the same technique above we can even pass in a composable to be used as part of the preview of a slot based composable layout. For example, if I want four configurations made up of two color variations and two different button contents:

We could then use the same PreviewParameterProvider for a different composable if desired.

So next time you need to make a whole lot of previews, even if using composable parameters, consider PreviewParameterProvider!

To check out the full code used here, see ComposablePreviewParameters.kt on Github:

Thank-you to Francois Blavoet and Jamie Sanson for helping me find the final solution.

--

--