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