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
PreviewProtocolJsonare 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.