Skip to main content

ViewCompose Preview

Use the first-party static-preview pipeline for compiled ViewCompose DSL rendered as native Android Views through Layoutlib. It needs no Compose compiler/runtime in the application module. The Compose Preview bridge is optional for projects that already use Compose tooling.

Install the static-preview pipeline​

The Gradle plugin discovers compiled entries and prepares Android inputs; Preview Core defines annotations/protocol; Worker Host owns Layoutlib; Runner mounts and exports the frame. Install the Android Studio ViewCompose Preview plugin separately for gutter actions, gallery/tool window, refresh, source navigation, and diagnostics.

build.gradle.kts
plugins {
id("com.viewcompose.preview") version "0.1.0-alpha03"
}

dependencies {
debugImplementation("com.viewcompose:viewcompose-preview-core:0.1.0-alpha03")
add(
"viewComposePreviewWorkerHost",
"com.viewcompose:viewcompose-preview-worker-host:0.1.0-alpha03",
)
add(
"viewComposePreviewRunner",
"com.viewcompose:viewcompose-preview-runner:0.1.0-alpha05",
)
}

The artifacts are independently versioned. Keep them on debug/tooling configurations and verify the current module catalog before mixing versions.

Run rendering on JDK 21 or newer. The current verified repository lane is Gradle 9.3.1, AGP 9.1.1, Kotlin 2.2.10, and compile SDK 37. Published Android libraries still target Java 11 bytecode. The Robolectric-only Preview unit tests use SDK 35 because Robolectric 4.14.1 does not model API 37; Paparazzi/Layoutlib remains the screenshot engine, so the SDK 35 test pin is not screenshot or production-runtime evidence.

Declare and render an entry​

Annotate a public top-level/static function with exactly one UiTreeBuilder receiver/parameter and Unit return. Repeated annotations and source-visible meta-annotations create variants.

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

Sync, open the source, and use the gutter icon or View | Tool Windows | ViewCompose Preview. Source-only saves use incremental refresh; signatures, resources, manifest, or dependencies require a full update. The inspector exposes native/VNode structure, bounds, composition/patch activity, phase timings, and source-aware diagnostics while application/Layoutlib code stays outside Studio.

Match application themes​

Add com.viewcompose:viewcompose-preview:0.1.0-alpha05 to debugImplementation when the default Android theme bridge is insufficient. One provider returns a configuration-qualified Context and matching ViewCompose tokens.

@ViewComposePreviewThemeProvider
object ApplicationPreviewThemeProvider : PreviewThemeProvider {
override fun resolve(
context: Context,
theme: PreviewTheme,
): PreviewThemeResolution {
val tokens = when (theme) {
PreviewTheme.Light -> UiThemeDefaults.light()
PreviewTheme.Dark -> UiThemeDefaults.dark()
}
return PreviewThemeResolution(context = context, tokens = tokens)
}
}

Native Views and stringResource/colorResource/dimensionResource then share locale, density, direction, night qualifiers, and one resource environment.

Use the optional Compose bridge​

Enable Compose and add the same optional viewcompose-preview artifact only when an existing Compose Preview surface is valuable. The bridge is not the native gallery, application-theme provider, static artifact, or structured-diagnostic pipeline.

@Preview
@Composable
fun composePreviewBridgeSample() {
val diagnostics = remember {
RenderDiagnostics(
collection = RenderDiagnosticCollection(),
sink = { event -> println(event) },
)
}
ViewComposePreview(
options = ViewComposePreviewOptions(diagnostics = diagnostics),
) {
Text("ViewCompose")
}
}

Inspect a running debug build​

With viewcompose-preview in a debuggable foreground application, Studio offers two explicit, request-only tools:

  • Inspect Device Diagnostics selects a session, shows correlated committed frame/failure state, navigates bounded source candidates, snapshots/highlights mounted Views, and records a finite eight-frame/two-second ViewCompose timing workload. Capture next LazyItem instead arms the selected exact parent for ten seconds and records one first supported frame from the next logical LazyItem, including an opaque physical-container token for holder-reuse correlation.
  • Inspect Device Animation Timeline discovers committed transitions and records one selected, read-only capture for at most 500 ms. It cannot seek or mutate device state.

Both use Android DUMP, a one-use nonce, foreground package/process checks, private atomic response files, bounded payloads, and fail-closed stale/disposed paths. No valid request means no report polling/write, recurring tree traversal, frame observer, timing allocation, or active capture. The future-item arm matches only parent Session ID, LazyItem role, and a post-request Session-ID floor; it never accepts or returns an application key or native object. Device timing excludes Android measure/layout/draw, GPU, RenderThread, SurfaceFlinger, decode, network, database, and external SDK work. See the Preview Integration module for exact ownership and bounds.

Snapshot verification​

Run ./gradlew :viewcompose-preview:verifyPaparazziDebug; reviewed Goldens live under viewcompose-preview/src/test/snapshots/images/. After an intentional reviewed visual change, use ./gradlew :viewcompose-preview:recordPaparazziDebug. Never record an unexplained mismatch. qaPreview is the required independent CI gate and uploads difference/test artifacts after failure. The current 0.15% catalog tolerance covers the documented Layoutlib editable-text glyph variance, not layout, color, or content regressions. Static catalog scenes model overlays without opening real windows; instrumentation owns actual dialog/popup/sheet behavior.

Ownership map​

  • Preview Core: annotations, configurations, protocol, snapshots.
  • Gradle Plugin: variants, discovery, fingerprints, tasks, stripping.
  • Runner: entry resolution, Android frame, capture and diagnostics.
  • Worker Host: Layoutlib process and class loader isolation.
  • Preview Integration: application themes, Compose bridge, optional running-device tooling.