Skip to main content

Preview Core

viewcompose-preview-core is the JVM-only contract shared by preview annotations, Gradle discovery, the Layoutlib runner and worker, Android Studio, tests, and CI. Add com.viewcompose:viewcompose-preview-core:0.1.0-alpha03 to a debuggable or dedicated preview source set; the Gradle plugin strips root and composed preview annotations from non-debuggable output.

Entry and configuration contract

@ViewComposePreview marks a public static JVM method that accepts exactly one UiTreeBuilder receiver or parameter and returns Unit. It is repeatable and supports source-visible custom multi-preview annotations. The built-ins cover light/dark, phone/tablet, LTR/RTL, and common font scales.

@ViewComposePreview(
name = "Counter · Light",
group = "Samples/Getting started",
)
@ViewComposePreview(
name = "Counter · Dark",
group = "Samples/Getting started",
theme = PreviewTheme.Dark,
)
fun UiTreeBuilder.CounterPreview() {
CounterScreen()
}

PreviewConfiguration deterministically owns viewport, density, font scale, locales, direction, theme, and optional API level. Matrix axes preserve declaration order; later axes win when several override the same field. IDs are validated lowercase cache/artifact identities.

fun previewConfigurationMatrixSample(): List<PreviewVariant> {
return PreviewConfigurationMatrix(
axes = listOf(
PreviewConfigurationPresets.Theme,
PreviewConfigurationPresets.LayoutDirection,
),
).variants()
}

Protocol and snapshot contract

The core model separates project bytecode from Layoutlib-compatible inputs, sorts inputs before SHA-256 fingerprinting, caps worker batches at eight sequential commands, requires unique response paths, and requires exact protocol-version equality. Filesystem paths remain opaque until the process responsible for access resolves and constrains them.

fun previewProtocolRoundTripSample(): PreviewRenderRequest {
val variant = PreviewVariant(
id = "phone-light",
displayName = "Phone / Light",
configuration = PreviewConfiguration(),
)
val request = PreviewRenderRequest(
requestId = "render-1",
descriptor = PreviewDescriptor(
id = "account-preview",
displayName = "Account preview",
entryPoint = PreviewJvmEntryPoint(
ownerClassName = "com.example.AccountPreviewsKt",
methodName = "accountPreview",
methodDescriptor = "(Lcom/viewcompose/ui/foundation/UiTreeBuilder;)V",
),
variants = listOf(variant),
),
variantId = variant.id,
modulePath = ":app",
buildVariant = "debug",
buildFingerprint = "0".repeat(64),
outputDirectory = "build/viewcompose-preview/account-preview/phone-light",
)
return PreviewProtocolJson.decodeRequest(PreviewProtocolJson.encodeRequest(request))
}

Failures cross process boundaries as structured diagnostics. Success requires an image artifact; timings use unique names and non-negative durations. Immutable snapshots contain only bounded, serializable structure, native bounds/properties, clipping, layout diagnostics, patches, composition information, and source call sites—never live Views, VNodes, class loaders, or exceptions. JSON writes defaults, omits explicit nulls, accepts additive unknown keys, and still validates identities and protocol compatibility.

Compatibility and verification

  • Stability: Alpha; annotation shape is established, but the wire protocol may advance between alpha lines.
  • Runtime: JVM 11, no Android, Gradle, Layoutlib, or IDE dependency.
  • Serialization JSON is an API dependency because protocol models and PreviewProtocolJson are public.
  • Verify configuration ordering, protocol round trips, invalid-input rejection, deterministic fingerprints, snapshot bounds, and old/new worker mismatch with :viewcompose-preview-core:test.

See ViewCompose Preview tooling for installation and the generated API reference for the exhaustive symbol inventory.