Install
One Maven artifact, at.exponential:ui-compose (minSdk 26). Its POM brings the primitives, Compose, coroutines and JNA, and the AAR carries the Rust core for arm64-v8a, armeabi-v7a and x86_64. R8 needs no extra rules.
ui-v* tag. Until it is on Maven Central, publish it to your local Maven repository from a checkout:bash apps/desktop/crates/exponential-ui-ffi/build-android.sh
cd packages/exponential-ui-compose
./gradlew :ui-compose:publishReleasePublicationToMavenLocal :ui-compose-primitives:publishReleasePublicationToMavenLocalThen add mavenLocal() to your repositories and the one dependency:
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
}
android {
namespace = "at.exponential.guide"
compileSdk = 36
defaultConfig {
applicationId = "at.exponential.guide"
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "0.1.0"
}
buildFeatures {
compose = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_17
}
}
dependencies {
// The SDK: the painter + host runtime (its POM brings the primitives,
// Compose, coroutines and JNA with the native core).
implementation("at.exponential:ui-compose:0.1.0")
implementation("androidx.activity:activity-compose:1.12.4")
implementation(platform("androidx.compose:compose-bom:2024.12.01"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material3:material3")
}Paint a surface
One ExponentialHost per activity, connected in onCreate and disposed in onDestroy; HostSurface paints a surface by id. The emulator reaches the host machine's server at 10.0.2.2.
package at.exponential.guide
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import at.exponential.ui.ExponentialUi
import at.exponential.ui.host.ExponentialHost
import at.exponential.ui.host.HostOptions
import at.exponential.ui.host.HostSurface
import at.exponential.ui.host.JsonlStreamTransport
import at.exponential.ui.theme.Mode
import at.exponential.ui.theme.ThemeHandle
class MainActivity : ComponentActivity() {
// One host per app: A2UI messages in over a transport, actions back out.
private val host = ExponentialHost(
HostOptions(
transport = JsonlStreamTransport(url = "http://10.0.2.2:4300/a2ui.jsonl", postUrl = "http://10.0.2.2:4300/action"),
theme = ThemeHandle.builtin("exponential"),
mode = Mode.Dark,
),
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ExponentialUi.appContext = applicationContext // links open through it
host.connect()
setContent {
// The surface the agent creates, as tall as its content: you own the scroller.
Column(Modifier.safeDrawingPadding().verticalScroll(rememberScrollState()).padding(16.dp)) {
HostSurface(host, "main", fallback = { Text("Waiting for the agent…") })
}
}
}
override fun onDestroy() {
host.dispose()
super.onDestroy()
}
}Host plugin
The lower-level SurfaceModel + ExponentialSurface take a HostPlugin (every member has a default; ClosureHost builds one from lambdas): icons painted under LocalContentColor, onAction, onInput (Change debounced with a revision, Commit on blur or IME Done), openUrl, fontFamily, markdown. Register theme fonts with ExponentialUi.registerFont(family, FontFamily). Details in the Compose library's README.
Compiled in CI
Every file on this page is a real file in apps/ui-site/guides/compose. CI copies it into an empty directory and builds it from scratch, the way you would, so the example cannot drift from the SDK:
bun apps/ui-site/guides/check.ts composeFor a complete app on this platform, see the samples: four hosts rendering the same surface from a local A2UI server, in a third-party theme, with a custom extension component.