{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreidlzybzpf544uqy3ai3zgrnh45li2aqti7zn4spemajldvxy2wuj4",
"uri": "at://did:plc:6wpza5rzzdrlesihm2bf6fir/app.bsky.feed.post/3mk4p4pyjgkk2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreidpshahkmj7uxj7xezxaargnjjhtrrwquuhm3m7i3klwirxlbai7y"
},
"mimeType": "image/png",
"size": 574906
},
"path": "/2026/04/jetpack-compose-april-2026-updates.html",
"publishedAt": "2026-04-22T23:00:00.000Z",
"site": "https://android-developers.googleblog.com",
"tags": [
"full BOM mapping",
"Compose BOM",
"UnconfinedTestDispatcher",
"StandardTestDispatcher",
"migration guide",
"LookaheadAnimationVisualDebugging",
"two finger swipes",
"pinches",
"performTrackpadInput",
"HostDefaultProvider",
"Compose 1.10 blog post",
"Modifier.onVisibilityChanged()",
"documentation here",
"styling",
"performance benefits",
"documentation",
"here",
"mediaQuery",
"UiMediaScope",
"derivedMediaQuery",
"tabletop mode",
"Grid",
"FlexBox",
"ComposeRuntimeFlags.isLinkBufferComposerEnabled",
"migrate to Jetpack Compose",
"@PreviewWrapper",
"@Composable",
"@Preview",
"@MultiPreview",
"@PreviewWrapperProvider",
"@Experimental",
"@OptIn"
],
"textContent": "_Posted by Meghan Mehta, Android Developer Relations Engineer_\n\n\n\n\n\n\n\nToday, the Jetpack Compose April ‘26 release is stable. This release contains version 1.11 of core Compose modules (see the full BOM mapping), shared element debug tools, trackpad events, and more. We also have a few experimental APIs that we’d love you to try out and give us feedback on.\n\nTo use today’s release, upgrade your Compose BOM version to:\n\n\n implementation(platform(\"androidx.compose:compose-bom:2026.04.01\"))\n\n### Changes in Compose 1.11.0\n\n## Coroutine execution in tests\n\nWe’re introducing a major update to how Compose handles test timing. Following the opt-in period announced in Compose 1.10, the v2 testing APIs are now the default, and the v1 APIs have been deprecated. The key change is a shift in the default test dispatcher. While the v1 APIs relied on `UnconfinedTestDispatcher`, which executed coroutines immediately, the v2 APIs use the `StandardTestDispatcher`. This means that when a coroutine is launched in your tests, it is now queued and does not execute until the virtual clock is advanced.\n\nThis better mimics production conditions, effectively flushing out race conditions and making your test suite significantly more robust and less flaky.\n\nTo ensure your tests align with standard coroutine behavior and to avoid future compatibility issues, we strongly recommend migrating your test suite. Check out our comprehensive migration guide for API mappings and common fixes.\n\n## Shared element improvements and animation tooling\n\n\n\n\nWe’ve also added some handy visual debugging tools for shared elements and `Modifier.animatedBounds`. You can now see exactly what’s happening under the hood—like target bounds, animation trajectories, and how many matches are found—making it much easier to spot why a transition might not be behaving as expected. To use the new tooling, simply surround your `SharedTransitionLayout` with the `LookaheadAnimationVisualDebugging` composable.\n\n\n LookaheadAnimationVisualDebugging( overlayColor = Color(0x4AE91E63), isEnabled = true, multipleMatchesColor = Color.Green, isShowKeylabelEnabled = false, unmatchedElementColor = Color.Red, ) { SharedTransitionLayout { CompositionLocalProvider( LocalSharedTransitionScope provides this, ) { // your content } } }\n\n\n## Trackpad events\n\nWe’ve revamped Compose support for trackpads, like built-in laptop trackpads, attachable trackpads for tablets, or external/virtual trackpads. Basic trackpad events will now generally be considered `PointerType.Mouse` events, aligning mouse and trackpad behavior to better match user expectations. Previously, these trackpad events were interpreted as fake touchscreen fingers of `PointerType.Touch`, which led to confusing user experiences. For example, clicking and dragging with a trackpad would scroll instead of selecting. By changing the pointer type these events have in the latest release of Compose, clicking and dragging with a trackpad will no longer scroll.\n\nWe also added support for more complicated trackpad gestures as recognized by the platform since API 34, including two finger swipes and pinches. These gestures are automatically recognized by components like `Modifier.scrollable` and `Modifier.transformable` to have better behavior with trackpads.\n\nThese changes improve behavior for trackpads across built-in components, with redundant touch slop removed, a more intuitive drag-and-drop starting gesture, double-click and triple-click selection in text fields, and desktop-styled context menus in text fields.\n\nTo test trackpad behavior, there are new testing APIs with `performTrackpadInput`, which allow validating the behavior of your apps when being used with a trackpad. If you have custom gesture detectors, validate behavior across input types, including touchscreens, mice, trackpads, and styluses, and ensure support for mouse scroll wheels and trackpad gestures.\n\nBefore | After\n---|---\n | \n\n## Composition host defaults (Compose runtime)\n\nWe introduced `HostDefaultProvider`, `LocalHostDefaultProvider`, `HostDefaultKey`, and `ViewTreeHostDefaultKey` to supply host-level services directly through compose-runtime. This removes the need for libraries to depend on compose-ui for lookups, better supporting Kotlin Multiplatform. To link these values to the composition tree, library authors can use `compositionLocalWithHostDefaultOf` to create a `CompositionLocal` that resolves defaults from the host.\n\n## Preview wrappers\n\nAndroid Studio custom previews is a new feature that allows you to define exactly how the contents of a Compose preview are displayed.\n\nBy implementing the `PreviewWrapperProvider` interface and applying the new `@PreviewWrapper` annotation, you can easily inject custom logic, such as applying a specific Theme. The annotation can be applied to a function annotated with `@Composable` and `@Preview` or `@MultiPreview`, offering a generic, easy-to-use solution that works across preview features and significantly reduces repetitive code.\n\n\n class ThemeWrapper: PreviewWrapper {\n\n @Composable\n\n override fun Wrap(content: @Composable (() -> Unit)) {\n\n JetsnackTheme {\n\n content()\n\n }\n\n }\n\n }\n\n\n\n\n @PreviewWrapperProvider(ThemeWrapper::class)\n\n @Preview\n\n @Composable\n\n private fun ButtonPreview() {\n\n // JetsnackTheme in effect\n\n Button(onClick = {}) {\n\n Text(text = \"Demo\")\n\n }\n\n }\n\n### Deprecations and removals\n\n * As announced in the Compose 1.10 blog post, we’re deprecating `Modifier.onFirstVisible()`. Its name often led to misconceptions, particularly within lazy layouts, where it would trigger multiple times during scrolling. We recommend migrating to `Modifier.onVisibilityChanged()`, which allows for more precise manual tracking of visibility states tailored to your specific use case requirements.\n * The `ComposeFoundationFlags.isTextFieldDpadNavigationEnabled` flag was removed because D-pad navigation for `TextFields` is now always enabled by default. The new behavior ensures that the D-pad events from a gamepad or a TV remote first move the cursor in the given direction. The focus can move to another element only when the cursor reaches the end of the text.\n\n\n\n### Upcoming APIs\n\nIn the upcoming Compose 1.12.0 release, the `compileSdk` will be upgraded to `compileSdk 37`, with AGP 9 and all apps and libraries that depend on Compose inheriting this requirement. We recommend keeping up to date with the latest released versions, as Compose aims to promptly adopt new `compileSdks` to provide access to the latest Android features. Be sure to check out the documentation here for more information on which version of AGP is supported for different API levels.\n\nIn Compose 1.11.0, the following APIs are introduced as `@Experimental`, and we look forward to hearing your feedback as you explore them in your apps. Note that `@Experimental` APIs are provided for early evaluation and feedback and may undergo significant changes or removal in future releases.\n\n## Styles (Experimental)\n\nWe are introducing a new experimental foundation API for styling. The Style API is a new paradigm for customizing visual elements of components, which has traditionally been performed with modifiers. It is designed to unlock deeper, easier customization by exposing a standard set of styleable properties with simple state-based styling and animated transitions. With this new API, we’re already seeing promising performance benefits. We plan to adopt Styles in Material components once the Style API stabilizes.\n\nA basic example of overriding a pressed state style background:\n\n\n @Composable fun LoginButton(modifier: Modifier = Modifier) { Button( onClick = { // Login logic }, modifier = modifier, style = { background( Brush.linearGradient( listOf(lightPurple, lightBlue) ) ) width(75.dp) height(50.dp) textAlign(TextAlign.Center) externalPadding(16.dp) pressed { background( Brush.linearGradient( listOf(Color.Magenta, Color.Red) ) ) } } ){ Text( text = \"Login\", ) } }\n\n\n\n\n\n\n\n\n\n\nCheck out the documentation and file any bugs here.\n\n## MediaQuery (Experimental)\n\nThe new `mediaQuery` API provides a declarative and performant way to adapt your UI to its environment. It abstracts complex information retrieval into simple conditions within a `UiMediaScope`, ensuring recomposition only happens when needed.\n\nWith support for a wide range of environmental signals—from device capabilities like keyboard types and pointer precision, to contextual states like window size and posture—you can build deeply responsive experiences. Performance is baked in with `derivedMediaQuery` to handle high-frequency updates, while the ability to override scopes makes testing and previews seamless across hardware configurations.\n\nPreviously, to get access to certain device properties — like if a device was in tabletop mode — you’d need to write a lot of boilerplate to do so:\n\n\n @Composable fun isTabletopPosture( context: Context = LocalContext.current ): Boolean { val windowLayoutInfo by WindowInfoTracker .getOrCreate(context) .windowLayoutInfo(context) .collectAsStateWithLifecycle(null) return windowLayoutInfo.displayFeatures.any { displayFeature -> displayFeature is FoldingFeature && displayFeature.state == FoldingFeature.State.HALF_OPENED && displayFeature.orientation == FoldingFeature.Orientation.HORIZONTAL } } @Composable fun VideoPlayer() { if(isTabletopPosture()) { TabletopLayout() } else { FlatLayout() } }\n\nNow, with `UIMediaQuery`, you can add the `mediaQuery` syntax to query device properties, such as if a device is in tabletop mode:\n\n\n @OptIn(ExperimentalMediaQueryApi::class) @Composable fun VideoPlayer() { if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) { TabletopLayout() } else { FlatLayout() } }\n\nCheck out the documentation and file any bugs here.\n\n## Grid (Experimental)\n\n`Grid` is a powerful new API for building complex, two-dimensional layouts in Jetpack Compose. While `Row` and `Column` are great for linear designs, `Grid` gives you the structural control needed for screen-level architecture and intricate components without the overhead of a scrollable list.\n\n`Grid` allows you to define your layout using tracks, gaps, and cells, offering familiar sizing options like `Dp`, percentages, intrinsic content sizes, and flexible \"Fr\" units.\n\n\n @OptIn(ExperimentalGridApi::class)\n\n @Composable\n\n fun GridExample() {\n\n Grid(\n\n config = {\n\n repeat(4) { column(0.25f) }\n\n repeat(2) { row(0.5f) }\n\n gap(16.dp)\n\n }\n\n ) {\n\n Card1(modifier = Modifier.gridItem(rowSpan = 2)\n\n Card2(modifier = Modifier.gridItem(colmnSpan = 3)\n\n Card3(modifier = Modifier.gridItem(columnSpan = 2)\n\n Card4()\n\n }\n\n }\n\n\n\n\nYou can place items automatically or explicitly span them across multiple rows and columns for precision. Best of all, it’s highly adaptive—you can dynamically reconfigure your grid tracks and spans to respond to device states like tabletop mode or orientation changes, ensuring your UI looks great across form factors.\n\n\n\n\nCheck out the documentation and file any bugs here.\n\n## FlexBox (Experimental)\n\nFlexBox is a layout container designed for high performance, adaptive UIs. It manages item sizing and space distribution based on available container dimensions. It handles complex tasks like wrapping (`wrap`) and multi-axis alignment of items (`justifyContent`, `alignItems`, `alignContent`). It allows items to grow (`grow`) or shrink (`shrink`) to fill the container.\n\n\n @OptIn(ExperimentalFlexBoxApi::class) fun FlexBoxWrapping(){ FlexBox( config = { wrap(FlexWrap.Wrap) gap(8.dp) } ) { RedRoundedBox() BlueRoundedBox() GreenRoundedBox(modifier = Modifier.width(350.dp).flex { grow(1.0f) }) OrangeRoundedBox(modifier = Modifier.width(200.dp).flex { grow(0.7f) }) PinkRoundedBox(modifier = Modifier.width(200.dp).flex { grow(0.3f) }) } }\n\n\n\n\n###\n\n\n\n\n\n##\n\n\n##\n\n\nCheck out the documentation and file any bugs here.\n\n## New SlotTable implementation (Experimental)\n\nWe’ve introduced a new implementation of the `SlotTable`, which is disabled by default in this release. `SlotTable` is the internal data structure that the Compose runtime uses to track the state of your composition hierarchy, track invalidations/recompositions, store remembered values, and track all metadata of the composition at runtime. This new implementation is designed to improve performance, primarily around random edits.\n\nTo try the new `SlotTable`, enable `ComposeRuntimeFlags.isLinkBufferComposerEnabled`.\n\n### Start coding today!\n\nWith so many exciting new APIs in Jetpack Compose, and many more coming up, it's never been a better time to migrate to Jetpack Compose. As always, we value your feedback and feature requests (especially on `@Experimental` features that are still baking) — please file them here. Happy composing!",
"title": "What's new in the Jetpack Compose April '26 release",
"updatedAt": "2026-04-22T23:00:20.424Z"
}