How to Use Jetpack Compose 1.12: Mesh Gradients, HDR Color, and Credential Manager
Upgrade to Compose BOM 2026.08.00 and ship mesh gradients, Display P3 color, named Grid areas, keyed SideEffect, and Credential Manager autofill in Jetpack Compose 1.12.
Jetpack Compose 1.12 is stable in the August 2026 BOM (2026.08.00). The release is not a rewrite of the toolkit. It is a set of APIs you can use this week: organic mesh gradients, a full wide-color / HDR paint pipeline, named areas on Grid, Credential Manager hooks on text fields, and a faster keyed SideEffect.
This guide is the practical path from the BOM bump to those APIs, based on the official August 2026 Compose release notes and Android developer documentation.
What you must change first
Compose 1.12 targets compileSdk 37 and needs AGP 9.1.1 or newer. That is a hard requirement, not a suggestion.
// settings or module build
implementation(platform("androidx.compose:compose-bom:2026.08.00"))
Also migrate Modifier.onFirstVisible() to Modifier.onVisibilityChanged(). The old modifier is deprecated; the new one lets you set a visibility threshold instead of firing on the first pixel.
If your project still sits on AGP 8.x, finish that upgrade before you chase mesh gradients. The rest of this article assumes the BOM is already on 2026.08.00.
Draw a mesh gradient instead of a linear wash
Linear and radial gradients only interpolate along one axis or from one center. A mesh gradient is a 2D grid of patches. You place vertices, give each a color (and optional Bezier tangents), and the GPU interpolates between them.
A grid of rows × columns patches has (rows + 1) × (columns + 1) vertices. A 1×1 mesh is four corners and one patch — enough for a hero card background.
val mesh = remember {
MeshGradientPainter(rows = 1, columns = 1) {
setVertex(0, 0, Offset(0f, 0f), Color(0xFF5B21B6))
setVertex(0, 1, Offset(1f, 0f), Color(0xFF2563EB))
setVertex(1, 0, Offset(0f, 1f), Color(0xFF059669))
setVertex(1, 1, Offset(1f, 1f), Color(0xFFF59E0B))
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 9f)
.paint(mesh)
)
Positions are normalized: (0f, 0f) is top-left, (1f, 1f) is bottom-right. Pass hasBicubicColor = true when you want Catmull-Rom color interpolation instead of bilinear. Use bilinear if the surface is small and you care about fill cost.
Google’s mesh gradient docs cover extra vertices and control points when you need a warped, “liquid” look rather than a four-corner blend.
Keep Display P3 and HDR colors intact
Compose could already store a Color in Display P3. Before 1.12, parts of the paint and shader path clamped that color back to sRGB. The August release keeps non-sRGB colors through graphics, paint, and shaders on supported devices.
Practical rules from the release notes:
- Display P3 and similar spaces stay intact on API 29+ when the panel supports them.
- Unsupported spaces (CieXyz, CieLab, Oklab) fall back to sRGB.
- Spaces that need a newer OS (for example Bt2020Hlg on Android 13 and below) also fall back.
- Android 9 (API 28) and below always fall back to sRGB.
If you ship brand colors from a design tool that exports P3, define them with an explicit color space instead of hoping hex literals survive the pipeline. On older phones the fallback is automatic; you do not need a second theme file.
LayerOutsets on GraphicsLayer / Modifier.graphicsLayer expands the visual bounds of an offscreen layer so effects are not clipped when the layer is promoted to a buffer.
Place dashboard tiles with named Grid areas
Numeric row and column indexes get brittle as soon as a layout has a header that spans two columns. Compose 1.12 adds named areas on the experimental Grid composable.
@OptIn(ExperimentalGridApi::class)
@Composable
fun DashboardLayout() {
Grid(
config = {
area("header", row = 0, column = 0, rowSpan = 1, columnSpan = 2)
area("sidebar", row = 1, column = 0)
area("content", row = 1, column = 1)
gap(16.dp)
}
) {
HeaderSection(modifier = Modifier.gridItem(areaId = "header"))
NavigationSidebar(modifier = Modifier.gridItem(areaId = "sidebar"))
MainContentView(modifier = Modifier.gridItem(areaId = "content"))
}
}
Treat this as experimental. The area names are just IDs in GridConfigurationScope; they are not CSS grid template strings. Official layout docs live under named areas on Grid.
Log analytics without a LaunchedEffect
SideEffect now takes keys. When userId or screenName changes, the block runs again. You do not need a coroutine or a dispose callback.
Google’s notes put keyed SideEffect at up to 90% faster than LaunchedEffect and about 20% faster than DisposableEffect for this pattern. It also runs before those two effects, so do not migrate a LaunchedEffect that must wait until after the current frame.
@Composable
fun AnalyticsTracker(userId: String, screenName: String) {
SideEffect(key1 = userId, key2 = screenName) {
analytics.logScreenView(userId, screenName)
}
}
Keep LaunchedEffect when you start a job. Keep DisposableEffect when you must unregister a listener.
Wire Credential Manager into a Compose field
On API 34+, Compose text fields can talk to Credential Manager through Autofill. Attach credentialRequest semantics with a CredentialRequestData object. Below API 34, use androidx.credentialslibrary as described in the release post.
@Composable
fun LoginField(textFieldState: TextFieldState) {
val credentialData = remember {
CredentialRequestData(
// Pass your GetCredentialRequest options here
)
}
BasicTextField(
state = textFieldState,
modifier = Modifier.semantics {
credentialRequest = credentialData
}
)
}
Related text changes in 1.12 that are easy to miss:
TextFieldBuffer.addStyle()appliesSpanStyle/ParagraphStyleinsidetextFieldState.edit { }SelectionStateonSelectionContainergivesselectAll(),clear(), andselectedTextsKeyboardTypeadds Date, Time, DateTime, and SignedDecimalBasicSecureTextFielddefaults toTextObfuscationMode.System
Two-stage transitions for predictive back
DeferredTargetAnimation is stable. New composables DeferredAnimatedContent and DeferredAnimatedVisibility let a gesture own the first part of a transition (scale or offset tracking a swipe), then hand velocity to the automatic animation when the user commits or cancels.
Use this when you already implement predictive back. Do not retrofit every AnimatedContent call. Shared elements can opt in with permitTransformDuringDeferredTransition on SharedContentConfig.
Testing hooks that cut idle waits
If you drive the test clock yourself:
hasPendingWork()checks for pending UI work without advancing timerunWithoutImplicitWait { }stops implicit sync while you query several nodes in one frame
Turn mainClock.autoAdvance off, step frames, then sample state. That combination is aimed at animation tests that used to flake or stall on implicit idle waits.
Compose 1.12 also reports Time to Initial Display in Google’s hero benchmarks as comparable to Views. Treat that as a framework claim for the sample suite, not a guarantee for every production app.
What to skip for now
The Styles API for custom design systems is still experimental and expected to break. Do not put it on a shipping theme layer.
Grid named areas are also experimental. Mesh gradients, WCG/HDR paint, keyed SideEffect, Credential Manager semantics, and the deferred animation types are the stable pieces worth a pull request this week.
Upgrade checklist
- AGP ≥ 9.1.1 and
compileSdk37 - Compose BOM
2026.08.00 - Replace
onFirstVisiblewithonVisibilityChanged - Add one
MeshGradientPainteron a hero surface - Define brand colors in Display P3 where your design file already uses P3
- Attach
credentialRequeston login fields if you target API 34+ - Move fire-and-forget logging from
LaunchedEffectto keyedSideEffect
Sources
- What’s new in the Jetpack Compose August ’26 release — Android Developers Blog
- Mesh gradients — Android Developers
- Compose BOM mapping — Android Developers
- AGP and compileSdk compatibility for Compose — Android Developers
- Named areas on Grid — Android Developers
- What’s new in Android development tools (I/O 2026) — Android Developers
- Build custom design systems with Compose Styles API — Android Developers
Share this story