Skip to main content

Build your first ViewCompose application

This tutorial builds a runnable counter backed by Android Views. Pressing the button updates snapshot state, invalidates the observed UI, and patches the existing native View tree.

The complete compiled application lives in samples/counter. The code below is copied from that module. qaQuick compiles the application, its device test, and the debug-only preview entry; qaPreview also verifies that preview discovery stays connected to the compiled function.

Required dependencies

Make sure the application resolves Maven Central, then add the named Material Android aggregate:

build.gradle.kts
repositories { mavenCentral() }

dependencies {
implementation("com.viewcompose:viewcompose-material3-android:0.1.0-alpha01")
}

The aggregate exposes runtime, UI-contract, UI Foundation, host, Material 3 theme, Lifecycle, and ViewModel APIs transitively. Their advanced APIs may still be addressed through a deliberate direct dependency. Add a lower-level coordinate directly only when building an integration without the aggregate.

The counter runs without preview tooling. To follow the optional preview section, also add the published plugin and its debug-only artifacts now:

build.gradle.kts (optional preview)
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-alpha04",
)
}

These preview entries configure the native render pipeline but do not install its Android Studio interface. Before opening the preview, go to Settings | Plugins | Marketplace, search for ViewCompose Preview, and install it. Restart Android Studio if prompted. The IDE plugin is a separate installation from id("com.viewcompose.preview"); the current Marketplace line is 1.1.0 for Android Studio build family 261.*.

What you will build

The application contains one Activity and one declarative tree:

  • a centered text label showing Count: 0;
  • an Increment button;
  • retained snapshot state that drives the label;
  • an Android host that owns lifecycle, SavedState, theme, and rendering services.
  • light and dark static previews of the same CounterScreen used by the Activity.

Expected result: every press increments the visible count without replacing the Activity.

Prerequisites and verified baseline

You need an Android application using Kotlin, an Android SDK, and JDK 17 for the Android Gradle Plugin. The sample uses compileSdk = 36, minSdk = 24, and JVM target 11.

This hard-cut dependency set was verified on 2026-08-06 through the repository-generated local Maven repository. It becomes a public installation path after the listed new coordinates are released to Maven Central:

ArtifactVersionHow it is supplied
viewcompose-material3-android0.1.0-alpha01Explicit application dependency
viewcompose-android0.1.0-alpha01Transitive neutral application aggregate
viewcompose-host-android0.1.0-alpha03Transitive low-level engine dependency
viewcompose-runtime0.1.0-alpha02Transitive foundation dependency
viewcompose-ui-contract0.1.0-alpha03Transitive foundation dependency
viewcompose-ui-foundation0.1.0-alpha01Transitive UI Foundation dependency
viewcompose-material30.1.0-alpha01Transitive design-system dependency
viewcompose-lifecycle-androidx0.1.0-alpha01Transitive AndroidX integration
viewcompose-viewmodel-androidx0.1.0-alpha01Transitive AndroidX integration
viewcompose-preview-gradle-plugin0.1.0-alpha03Optional explicit plugin
viewcompose-preview-core0.1.0-alpha03Optional debug dependency
viewcompose-preview-worker-host0.1.0-alpha03Optional preview configuration
viewcompose-preview-runner0.1.0-alpha04Optional preview configuration

ViewCompose artifacts evolve independently. Check the published module catalog before mixing versions outside this verified set.

The repository sample uses these exact Maven coordinates. qaQuick first publishes the current checkout to build/maven-repository, then verifies the same generated POM path used by an external application.

1. Use a Material application theme

The host resolves ViewCompose tokens from the Android theme. A new View-based Android Studio project normally already has a suitable Material theme. The counter sample uses:

res/values/themes.xml
<resources>
<style name="Theme.ViewCompose.Counter" parent="Theme.Material3.DayNight.NoActionBar" />
</resources>

Apply that theme to the application or Activity in AndroidManifest.xml. ViewCompose will follow the host's light/dark configuration and Android theme bridge; no Compose theme is involved.

2. Install the declarative content

Replace the generated Activity content with the compiled MainActivity.kt:

package com.example.counter

import android.os.Bundle
import androidx.activity.ComponentActivity
import com.viewcompose.material3.android.setMaterial3UiContent
import com.viewcompose.runtime.mutableStateOf
import com.viewcompose.ui.layout.HorizontalAlignment
import com.viewcompose.ui.layout.MainAxisArrangement
import com.viewcompose.ui.modifier.Modifier
import com.viewcompose.ui.modifier.fillMaxSize
import com.viewcompose.ui.modifier.padding
import com.viewcompose.ui.unit.dp
import com.viewcompose.ui.foundation.Button
import com.viewcompose.ui.foundation.Column
import com.viewcompose.ui.foundation.Text
import com.viewcompose.ui.foundation.TextDefaults
import com.viewcompose.ui.foundation.UiTreeBuilder
import com.viewcompose.ui.foundation.remember

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setMaterial3UiContent {
CounterScreen()
}
}
}

internal fun UiTreeBuilder.CounterScreen() {
val count = remember { mutableStateOf(0) }

Column(
spacing = 16.dp,
arrangement = MainAxisArrangement.Center,
horizontalAlignment = HorizontalAlignment.Center,
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
) {
Text(
text = "Count: ${count.value}",
style = TextDefaults.titleLargeStyle(),
)
Button(
text = "Increment",
onClick = { count.value += 1 },
)
}
}

Four pieces form the complete update path:

  1. setMaterial3UiContent installs a lifecycle-aware Android host and performs the first render.
  2. remember retains the state object at its composition position.
  3. reading count.value subscribes this composition scope to state invalidation.
  4. the button writes a new value; ViewCompose recomposes the affected scope and patches the native TextView rather than recreating the Activity.

remember retains values while this composition survives. Use rememberSaveable when the value must also survive Activity recreation or process restoration; see Lifecycle and SavedState.

3. Preview the compiled screen

Keep the optional preview dependencies listed at the top on the debug path. The repository sample and an external application both use the published plugin artifacts.

The sample's debug source set exposes the same CounterScreen through a public static-preview entry point:

CounterPreview.kt
package com.viewcompose.samples.counter

import com.viewcompose.preview.tooling.PreviewTheme
import com.viewcompose.preview.tooling.ViewComposePreview
import com.viewcompose.ui.foundation.UiTreeBuilder

/**
* Renders the initial counter state through the native static-preview toolchain.
*
* @receiver DSL tree builder supplied by the static preview runner.
*/
@ViewComposePreview(
name = "Counter · Light",
group = "Samples/Getting started",
)
@ViewComposePreview(
name = "Counter · Dark",
group = "Samples/Getting started",
theme = PreviewTheme.Dark,
)
fun UiTreeBuilder.CounterPreview() {
CounterScreen()
}

After installing ViewCompose Preview from the Android Studio Marketplace, open CounterPreview.kt and click its preview gutter icon, or open the ViewCompose Preview tool window, to inspect both variants. The native static runner uses the compiled DSL function, so the Activity and preview cannot drift into two separate screen implementations. Verify discovery locally with:

./gradlew :samples:counter:verifyCounterPreview
./gradlew qaPreview

4. Run and verify

Run the application from Android Studio, or build the repository sample from the command line:

./gradlew :samples:counter:assembleDebug

With an emulator or device connected, install the sample and run its click regression:

./gradlew :samples:counter:installDebug
./gradlew :samples:counter:connectedDebugAndroidTest

The test asserts Count: 0, presses Increment, and then asserts Count: 1 against the real Android View hierarchy.

Where to go next

  • Read State snapshots for transaction and observation rules.
  • Read the theme architecture before defining application tokens, and use the dynamic-color guide for Android resource policy.
  • Read Preview tooling for theme providers, diagnostics, and snapshot policy.
  • Use the module catalog to add navigation, text editing, graphics, or other optional capabilities without pulling them into the minimal application.