Merge "Define the default constructor for ReplaceFileCorruptionHandler which can be used in common code" into androidx-main
diff --git a/annotation/annotation/build.gradle b/annotation/annotation/build.gradle
index c8681d9..dd9bd8a 100644
--- a/annotation/annotation/build.gradle
+++ b/annotation/annotation/build.gradle
@@ -17,15 +17,16 @@
 
 androidXMultiplatform {
     androidNative()
+    ios()
+    js()
     jvm()
+    linux()
     mac()
     mingwX64()
-    linux()
-    ios()
-    watchosDeviceArm64()
-    watchos()
     tvos()
     wasmJs()
+    watchos()
+    watchosDeviceArm64()
 
     defaultPlatform(PlatformIdentifier.JVM)
 
@@ -44,6 +45,10 @@
             dependsOn(nonJvmMain)
         }
 
+        jsMain {
+            dependsOn(nonJvmMain)
+        }
+
         targets.configureEach { target ->
             if (target.platformType !in [KotlinPlatformType.jvm, KotlinPlatformType.common]) {
                 target.compilations["main"].defaultSourceSet {
diff --git a/benchmark/baseline-profile-gradle-plugin/build.gradle b/benchmark/baseline-profile-gradle-plugin/build.gradle
index 6856063..2e6c028 100644
--- a/benchmark/baseline-profile-gradle-plugin/build.gradle
+++ b/benchmark/baseline-profile-gradle-plugin/build.gradle
@@ -49,7 +49,7 @@
     compileOnly(libs.kotlinGradlePluginz)
 
     implementation(gradleApi())
-    implementation(libs.protobuf)
+    implementation("com.google.protobuf:protobuf-java:3.22.3")
     implementation(libs.agpTestingPlatformCoreProto)
 
     testImplementation(gradleTestKit())
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
index be77a52..41335c3d 100644
--- a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
@@ -95,6 +95,7 @@
                                 platformTargets = listOf(),
                                 compilerVersion = "",
                                 abiVersion = "",
+                                libraryVersion = "",
                                 irProviderName = ""
                             )
                     )
diff --git a/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt b/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt
index 23c9ff9..a50cc57 100644
--- a/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt
@@ -32,7 +32,10 @@
                     PlatformGroup.WINDOWS,
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
-                    PlatformGroup.ANDROID_NATIVE))
+                    PlatformGroup.ANDROID_NATIVE,
+                    PlatformGroup.JS
+                )
+            )
     }
 
     @Test
@@ -47,7 +50,9 @@
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
                     PlatformGroup.ANDROID_NATIVE,
-                ))
+                    PlatformGroup.JS,
+                )
+            )
     }
 
     @Test
@@ -62,12 +67,14 @@
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
                     PlatformGroup.ANDROID_NATIVE,
-                ))
+                    PlatformGroup.JS,
+                )
+            )
     }
 
     @Test
     fun withNoPlatforms_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("-jvm,-desktop,-native,-wasm"))
+        assertThat(parseTargetPlatformsFlag("-jvm,-desktop,-native,-wasm,-js"))
             .isEqualTo(emptySet<PlatformGroup>())
     }
 
@@ -84,7 +91,9 @@
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
                     PlatformGroup.ANDROID_NATIVE,
-                ))
+                    PlatformGroup.JS,
+                )
+            )
     }
 
     @Test
@@ -100,12 +109,14 @@
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
                     PlatformGroup.ANDROID_NATIVE,
-                ))
+                    PlatformGroup.JS,
+                )
+            )
     }
 
     @Test
     fun withNegativeFlags_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("-jvm,+mac,-wasm"))
+        assertThat(parseTargetPlatformsFlag("-jvm,+mac,-wasm,-js"))
             .isEqualTo(
                 setOf(
                     PlatformGroup.MAC,
@@ -113,7 +124,8 @@
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
                     PlatformGroup.ANDROID_NATIVE,
-                ))
+                )
+            )
     }
 
     @Test
@@ -127,7 +139,10 @@
                     PlatformGroup.WINDOWS,
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
-                    PlatformGroup.ANDROID_NATIVE))
+                    PlatformGroup.ANDROID_NATIVE,
+                    PlatformGroup.JS
+                )
+            )
     }
 
     @Test
@@ -140,18 +155,22 @@
                     PlatformGroup.WINDOWS,
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
-                    PlatformGroup.ANDROID_NATIVE))
+                    PlatformGroup.ANDROID_NATIVE
+                )
+            )
     }
 
     @Test
     fun withRedundentFlags_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("-wasm,-jvm,+native,+linux,+mac,+linux,-wasm"))
+        assertThat(parseTargetPlatformsFlag("-wasm,-jvm,+native,+linux,+mac,+linux,-wasm,-js"))
             .isEqualTo(
                 setOf(
                     PlatformGroup.MAC,
                     PlatformGroup.WINDOWS,
                     PlatformGroup.LINUX,
                     PlatformGroup.DESKTOP,
-                    PlatformGroup.ANDROID_NATIVE))
+                    PlatformGroup.ANDROID_NATIVE
+                )
+            )
     }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
index 6f6e0b20..76cb02d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
@@ -185,7 +185,11 @@
 
             compile.pluginClasspath.from(kotlinPluginProvider.get())
 
-            compile.enableFeatureFlag(ComposeFeatureFlag.OptimizeNonSkippingGroups)
+            // todo(b/291587160): enable when Compose compiler 2.0.20 is merged
+            // compile.enableFeatureFlag(ComposeFeatureFlag.StrongSkipping)
+            // compile.enableFeatureFlag(ComposeFeatureFlag.OptimizeNonSkippingGroups)
+            compile.addPluginOption(ComposeCompileOptions.StrongSkipping, "true")
+            compile.addPluginOption(ComposeCompileOptions.NonSkippingGroupOptimization, "true")
             if (shouldPublish) {
                 compile.addPluginOption(ComposeCompileOptions.SourceOption, "true")
             }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 00a8c9b..29a3abd 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -1108,9 +1108,8 @@
 
         buildToolsVersion = project.defaultAndroidConfig.buildToolsVersion
 
-        defaultConfig.ndk.abiFilters.addAll(
-            listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64", "riscv64")
-        )
+        // b/366238650
+        defaultConfig.ndk.abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))
         defaultConfig.minSdk = defaultMinSdk
         defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index 5b4605e..2dcaa60 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -34,7 +34,6 @@
 import org.gradle.api.plugins.ExtensionAware
 import org.gradle.kotlin.dsl.findByType
 import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
-import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
@@ -45,6 +44,7 @@
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
 import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
 import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWasmTargetDsl
 import org.jetbrains.kotlin.gradle.targets.js.ir.DefaultIncrementalSyncTask
@@ -658,7 +658,12 @@
     fun js(block: Action<KotlinJsTargetDsl>? = null): KotlinJsTargetDsl? {
         supportedPlatforms.add(PlatformIdentifier.JS)
         return if (project.enableJs()) {
-            kotlinExtension.js { block?.execute(this) }
+            kotlinExtension.js() {
+                block?.execute(this)
+                binaries.library()
+                browser {}
+                project.configureJs()
+            }
         } else {
             null
         }
@@ -700,20 +705,19 @@
     }
 }
 
+private fun Project.configureJs() {
+    configureNode()
+    // Use DSL API when https://youtrack.jetbrains.com/issue/KT-70029 is closed for all tasks below
+    tasks.named("jsDevelopmentLibraryCompileSync", DefaultIncrementalSyncTask::class.java) {
+        it.destinationDirectory.set(file(layout.buildDirectory.dir("js/packages/js/dev/kotlin")))
+    }
+    tasks.named("jsProductionLibraryCompileSync", DefaultIncrementalSyncTask::class.java) {
+        it.destinationDirectory.set(file(layout.buildDirectory.dir("js/packages/js/prod/kotlin")))
+    }
+}
+
 private fun Project.configureWasm() {
-    rootProject.extensions.findByType<NodeJsRootExtension>()?.let {
-        it.version = getVersionByName("node")
-        it.downloadBaseUrl =
-            File(project.getPrebuiltsRoot(), "androidx/external/org/nodejs/node").toURI().toString()
-    }
-
-    rootProject.extensions.findByType(YarnRootExtension::class.java)?.let {
-        it.version = getVersionByName("yarn")
-        it.lockFileDirectory =
-            File(project.getPrebuiltsRoot(), "androidx/external/wasm/yarn-offline-mirror")
-        it.yarnLockMismatchReport = YarnLockMismatchReport.WARNING
-    }
-
+    configureNode()
     // Use DSL API when https://youtrack.jetbrains.com/issue/KT-70029 is closed for all tasks below
     tasks.named("wasmJsDevelopmentLibraryCompileSync", DefaultIncrementalSyncTask::class.java) {
         it.destinationDirectory.set(
@@ -734,6 +738,21 @@
     }
 }
 
+private fun Project.configureNode() {
+    rootProject.extensions.findByType<NodeJsRootExtension>()?.let {
+        it.version = getVersionByName("node")
+        it.downloadBaseUrl =
+            File(project.getPrebuiltsRoot(), "androidx/external/org/nodejs/node").toURI().toString()
+    }
+
+    rootProject.extensions.findByType(YarnRootExtension::class.java)?.let {
+        it.version = getVersionByName("yarn")
+        it.lockFileDirectory =
+            File(project.getPrebuiltsRoot(), "androidx/external/wasm/yarn-offline-mirror")
+        it.yarnLockMismatchReport = YarnLockMismatchReport.FAIL
+    }
+}
+
 fun Project.validatePublishedMultiplatformHasDefault() {
     val extension = project.extensions.getByType(AndroidXMultiplatformExtension::class.java)
     if (extension.defaultPlatform == null && extension.supportedPlatforms.isNotEmpty()) {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRepackageImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRepackageImplPlugin.kt
index 833f947..6a57ac1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRepackageImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRepackageImplPlugin.kt
@@ -58,6 +58,11 @@
                         dropResourcesWithSuffix = ".proto"
                     }
                 )
+                task.transformers.add(
+                    BundleInsideHelper.DontIncludeResourceTransformer().apply {
+                        dropResourcesWithSuffix = ".proto.bin"
+                    }
+                )
                 task.from(sourceSets.named("main").map { it.output })
                 relocationExtension.getRelocations().forEach {
                     task.relocate(it.sourcePackage, it.targetPackage)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt
index 96b2c68..1e77374 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt
@@ -136,12 +136,13 @@
 
         // The following tasks have the same output configFile file:
         // projectBuildDir/js/packages/projectName-wasm-js/webpack.config.js
-        // Remove when https://youtrack.jetbrains.com/issue/KT-70029 is resolved
+        // Remove when https://youtrack.jetbrains.com/issue/KT-70029 / b/361319689 is resolved
         // and set configFile location for each task
         "wasmJsBrowserDevelopmentWebpack",
         "wasmJsBrowserDevelopmentRun",
         "wasmJsBrowserProductionWebpack",
         "wasmJsBrowserProductionRun",
+        "jsTestTestDevelopmentExecutableCompileSync",
 
         // Remove when https://youtrack.jetbrains.com/issue/KT-71688 is resolved and set
         // destinationDirectory to the project's build directory
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
index fd90716..ac96f91 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
@@ -307,7 +307,7 @@
         // will not move adjacent elements, so any comments would remain in their
         // original order.
         element.content().replaceAll {
-            val index = deps.indexOf(it)
+            val index = sortedDeps.indexOf(it)
             if (index >= 0) {
                 sortedDeps[index]
             } else {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt b/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
index b0b2916..50f8f8ec 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
@@ -35,9 +35,7 @@
 import org.gradle.process.ExecSpec
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
 import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
-import org.jetbrains.kotlin.konan.TempFiles
 import org.jetbrains.kotlin.konan.target.Family
-import org.jetbrains.kotlin.konan.target.LinkerArguments
 import org.jetbrains.kotlin.konan.target.LinkerOutputKind
 import org.jetbrains.kotlin.konan.target.Platform
 import org.jetbrains.kotlin.konan.target.PlatformManager
@@ -140,22 +138,18 @@
         val objectFiles = parameters.objectFiles.regularFilePaths()
         val linkedObjectFiles = parameters.linkedObjects.regularFilePaths()
         val linkCommands =
-            with(platform.linker) {
-                LinkerArguments(
-                        TempFiles(),
-                        objectFiles = objectFiles,
-                        executable = outputFile.canonicalPath,
-                        libraries = linkedObjectFiles,
-                        linkerArgs = linkerFlags,
-                        optimize = true,
-                        debug = false,
-                        kind = LinkerOutputKind.DYNAMIC_LIBRARY,
-                        outputDsymBundle = "unused",
-                        mimallocEnabled = false,
-                        sanitizer = null
-                    )
-                    .finalLinkCommands()
-            }
+            platform.linker.finalLinkCommands(
+                objectFiles = objectFiles,
+                executable = outputFile.canonicalPath,
+                libraries = linkedObjectFiles,
+                linkerArgs = linkerFlags,
+                optimize = true,
+                debug = false,
+                kind = LinkerOutputKind.DYNAMIC_LIBRARY,
+                outputDsymBundle = "unused",
+                mimallocEnabled = false,
+                sanitizer = null
+            )
         linkCommands
             .map { it.argsWithExecutable }
             .forEach { args ->
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
index ca78a94..047be08 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
@@ -46,12 +46,10 @@
         val native = listOf(MAC, LINUX, WINDOWS, ANDROID_NATIVE)
 
         /**
-         * Target platform groups which are enabled by default.
-         *
-         * Do *not* enable [JS] unless you have read and understand this:
-         * https://blog.jetbrains.com/kotlin/2021/10/important-ua-parser-js-exploit-and-kotlin-js/
+         * Target platform groups which are enabled by default. We currently enable all platforms by
+         * default.
          */
-        val enabledByDefault = listOf(JVM, DESKTOP, MAC, LINUX, WINDOWS, ANDROID_NATIVE, WASM)
+        val enabledByDefault = listOf(ANDROID_NATIVE, DESKTOP, JS, JVM, LINUX, MAC, WASM, WINDOWS)
     }
 }
 
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/FakeTakePictureCallbackDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/FakeTakePictureCallbackDeviceTest.kt
index 1584d69..b95cdc6 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/FakeTakePictureCallbackDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/FakeTakePictureCallbackDeviceTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.camera.core.imagecapture
 
+import android.graphics.ImageFormat
 import androidx.camera.core.ImageCapture.OutputFileResults
 import androidx.camera.testing.impl.fakes.FakeImageInfo
 import androidx.camera.testing.impl.fakes.FakeImageProxy
@@ -33,7 +34,7 @@
     @Test
     fun onDiskResultArrivesBeforeGet_canGetResult() = runBlocking {
         // Arrange.
-        val onDiskResult = OutputFileResults(null)
+        val onDiskResult = OutputFileResults(null, ImageFormat.JPEG)
         // Assert.
         fakeTakePictureCallback.onFinalResult(onDiskResult)
         // Act.
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
index 065ac04..42c55dd 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
@@ -196,7 +196,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    if (outputFileOptions == null) null else listOf(outputFileOptions),
+                    outputFileOptions,
+                    null,
                     CROP_RECT,
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
@@ -231,7 +232,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    if (outputFileOptions == null) null else listOf(outputFileOptions),
+                    outputFileOptions,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
@@ -273,7 +275,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    if (outputFileOptions == null) null else listOf(outputFileOptions),
+                    outputFileOptions,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
@@ -321,7 +324,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    if (outputFileOptions == null) null else listOf(outputFileOptions),
+                    outputFileOptions,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
@@ -363,7 +367,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    if (outputFileOptions == null) null else listOf(outputFileOptions),
+                    outputFileOptions,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
@@ -407,7 +412,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    if (outputFileOptions == null) null else listOf(outputFileOptions),
+                    outputFileOptions,
+                    null,
                     CROP_RECT,
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
@@ -452,7 +458,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    if (outputFileOptions == null) null else listOf(outputFileOptions),
+                    outputFileOptions,
+                    null,
                     CROP_RECT,
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
@@ -507,6 +514,7 @@
                 { listOf() },
                 createTakePictureRequest(
                     /*outputFileOptions=*/ null,
+                    /*secondaryOutputFileOptions=*/ null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 0, // 0 because exif does not have rotation.
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Utils.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Utils.kt
index f59512c..4991c23 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Utils.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Utils.kt
@@ -50,7 +50,8 @@
         )
 
     fun createTakePictureRequest(
-        outputFileOptions: List<ImageCapture.OutputFileOptions>?,
+        outputFileOptions: ImageCapture.OutputFileOptions?,
+        secondaryFileOption: ImageCapture.OutputFileOptions?,
         cropRect: Rect,
         sensorToBufferTransform: Matrix,
         rotationDegrees: Int,
@@ -70,6 +71,7 @@
             onMemoryCallback,
             onDiskCallback,
             outputFileOptions,
+            secondaryFileOption,
             cropRect,
             sensorToBufferTransform,
             rotationDegrees,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 72c23be..3fac0e4 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -922,7 +922,7 @@
         }
 
         takePictureInternal(executor, callback, /*onDiskCallback=*/null,
-                /*outputFileOptions=*/null);
+                /*outputFileOptions=*/null, /*secondaryOutputFileOptions=*/null);
     }
 
     /**
@@ -944,7 +944,14 @@
             final @NonNull OutputFileOptions outputFileOptions,
             final @NonNull Executor executor,
             final @NonNull OnImageSavedCallback imageSavedCallback) {
-        takePicture(List.of(outputFileOptions), executor, imageSavedCallback);
+        if (Looper.getMainLooper() != Looper.myLooper()) {
+            CameraXExecutors.mainThreadExecutor().execute(() -> takePicture(
+                    outputFileOptions, executor, imageSavedCallback));
+            return;
+        }
+
+        takePictureInternal(executor, /*inMemoryCallback=*/null, imageSavedCallback,
+                outputFileOptions, /*secondaryOutputFileOptions=*/null);
     }
 
     /**
@@ -956,7 +963,8 @@
      * {@link ImageFormat#RAW_SENSOR} image and the second one is for {@link ImageFormat#JPEG}. The
      * order in which image format is triggered first is not guaranteed.
      *
-     * @param outputFileOptions  List of options to store the newly captured images.
+     * @param rawOutputFileOptions  Options to store the newly captured raw image.
+     * @param jpegOutputFileOptions Options to store the newly captured jpeg image.
      * @param executor           The executor in which the callback methods will be run.
      * @param imageSavedCallback Callback to be called for the newly captured image.
      *
@@ -965,17 +973,18 @@
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public void takePicture(
-            final @NonNull List<OutputFileOptions> outputFileOptions,
+            final @NonNull OutputFileOptions rawOutputFileOptions,
+            final @NonNull OutputFileOptions jpegOutputFileOptions,
             final @NonNull Executor executor,
             final @NonNull OnImageSavedCallback imageSavedCallback) {
         if (Looper.getMainLooper() != Looper.myLooper()) {
             CameraXExecutors.mainThreadExecutor().execute(
-                    () -> takePicture(outputFileOptions,
+                    () -> takePicture(rawOutputFileOptions, jpegOutputFileOptions,
                             executor, imageSavedCallback));
             return;
         }
         takePictureInternal(executor, /*inMemoryCallback=*/null, imageSavedCallback,
-                outputFileOptions);
+                rawOutputFileOptions, jpegOutputFileOptions);
     }
 
     /**
@@ -1453,8 +1462,9 @@
     @MainThread
     private void takePictureInternal(@NonNull Executor executor,
             @Nullable OnImageCapturedCallback inMemoryCallback,
-            @Nullable ImageCapture.OnImageSavedCallback onDiskCallback,
-            @Nullable List<OutputFileOptions> outputFileOptions) {
+            @Nullable OnImageSavedCallback onDiskCallback,
+            @Nullable OutputFileOptions outputFileOptions,
+            @Nullable OutputFileOptions secondaryOutputFileOptions) {
         checkMainThread();
         if (getFlashMode() == ImageCapture.FLASH_MODE_SCREEN
                 && mScreenFlashWrapper.getBaseScreenFlash() == null) {
@@ -1471,6 +1481,7 @@
                 inMemoryCallback,
                 onDiskCallback,
                 outputFileOptions,
+                secondaryOutputFileOptions,
                 getTakePictureCropRect(),
                 getSensorToBufferTransformMatrix(),
                 getRelativeRotation(camera),
@@ -2193,12 +2204,12 @@
         @Nullable
         private final Uri mSavedUri;
 
-        /**
-         *
-         */
+        private final int mImageFormat;
+
         @RestrictTo(Scope.LIBRARY_GROUP)
-        public OutputFileResults(@Nullable Uri savedUri) {
+        public OutputFileResults(@Nullable Uri savedUri, int imageFormat) {
             mSavedUri = savedUri;
+            mImageFormat = imageFormat;
         }
 
         /**
@@ -2212,6 +2223,14 @@
         public Uri getSavedUri() {
             return mSavedUri;
         }
+
+        /**
+         * Returns the {@link ImageFormat} of the saved file.
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        public int getImageFormat() {
+            return mImageFormat;
+        }
     }
 
     /** Holder class for metadata that will be saved with captured images. */
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/DngImage2Disk.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/DngImage2Disk.java
index c19ed71..9f1b768 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/DngImage2Disk.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/DngImage2Disk.java
@@ -20,6 +20,7 @@
 import static androidx.camera.core.imagecapture.FileUtil.createTempFile;
 import static androidx.camera.core.imagecapture.FileUtil.moveFileToTarget;
 
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.DngCreator;
@@ -63,7 +64,7 @@
         File tempFile = createTempFile(options);
         writeImageToFile(tempFile, in.getImageProxy(), in.getRotationDegrees());
         Uri uri = moveFileToTarget(tempFile, options);
-        return new ImageCapture.OutputFileResults(uri);
+        return new ImageCapture.OutputFileResults(uri, ImageFormat.RAW_SENSOR);
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java
index 6e32225..bfde90c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java
@@ -22,6 +22,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.graphics.ImageFormat;
 import android.net.Uri;
 
 import androidx.annotation.NonNull;
@@ -55,7 +56,7 @@
         updateFileExif(tempFile, requireNonNull(packet.getExif()), options,
                 packet.getRotationDegrees());
         Uri uri = moveFileToTarget(tempFile, options);
-        return new ImageCapture.OutputFileResults(uri);
+        return new ImageCapture.OutputFileResults(uri, ImageFormat.JPEG);
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
index 5819ecd..1b46be5 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
@@ -252,30 +252,29 @@
                 String.format("On-disk capture only support JPEG and"
                 + " JPEG/R and RAW output formats. Output format: %s", format));
         ProcessingRequest request = inputPacket.getProcessingRequest();
-        checkArgument(request.getOutputFileOptions() != null
-                && !request.getOutputFileOptions().isEmpty(),
+        checkArgument(request.getOutputFileOptions() != null,
                 "OutputFileOptions cannot be empty");
         Packet<ImageProxy> originalImage = mInput2Packet.apply(inputPacket);
         boolean isSimultaneousCaptureEnabled = outputFormats.size() > 1;
         if (isSimultaneousCaptureEnabled) {
-            // If simultaneous capture RAW + JPEG, use the first output file options for JPEG and
-            // the second for RAW.
+            // If simultaneous capture RAW + JPEG, use the first output file options for RAW and
+            // the second for JPEG.
             checkArgument(request.getOutputFileOptions() != null
-                            && request.getOutputFileOptions().size() > 1,
+                            && request.getSecondaryOutputFileOptions() != null,
                     "The number of OutputFileOptions for simultaneous capture "
                             + "should be at least two");
             ImageCapture.OutputFileResults outputFileResults = null;
             switch (originalImage.getFormat()) {
                 case RAW_SENSOR:
                     outputFileResults = saveRawToDisk(originalImage,
-                            requireNonNull(request.getOutputFileOptions()).get(0));
+                            requireNonNull(request.getOutputFileOptions()));
                     request.getTakePictureRequest()
                             .markFormatProcessStatusInSimultaneousCapture(RAW_SENSOR, true);
                     return outputFileResults;
                 case JPEG:
                 default:
                     outputFileResults = saveJpegToDisk(originalImage,
-                            requireNonNull(request.getOutputFileOptions()).get(1),
+                            requireNonNull(request.getSecondaryOutputFileOptions()),
                             request.getJpegQuality());
                     request.getTakePictureRequest()
                             .markFormatProcessStatusInSimultaneousCapture(JPEG, true);
@@ -285,11 +284,11 @@
             switch (format) {
                 case RAW_SENSOR:
                     return saveRawToDisk(originalImage,
-                            requireNonNull(request.getOutputFileOptions()).get(0));
+                            requireNonNull(request.getOutputFileOptions()));
                 case JPEG:
                 default:
                     return saveJpegToDisk(originalImage,
-                            requireNonNull(request.getOutputFileOptions()).get(0),
+                            requireNonNull(request.getOutputFileOptions()),
                             request.getJpegQuality());
             }
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingRequest.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingRequest.java
index bf70e64..0e2b5d3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingRequest.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingRequest.java
@@ -43,7 +43,9 @@
     private final int mRequestId;
     @NonNull TakePictureRequest mTakePictureRequest;
     @Nullable
-    private final List<ImageCapture.OutputFileOptions> mOutputFileOptions;
+    private final ImageCapture.OutputFileOptions mOutputFileOptions;
+    @Nullable
+    private final ImageCapture.OutputFileOptions mSecondaryOutputFileOptions;
     @NonNull
     private final Rect mCropRect;
     private final int mRotationDegrees;
@@ -77,6 +79,7 @@
         mRequestId = requestId;
         mTakePictureRequest = takePictureRequest;
         mOutputFileOptions = takePictureRequest.getOutputFileOptions();
+        mSecondaryOutputFileOptions = takePictureRequest.getSecondaryOutputFileOptions();
         mJpegQuality = takePictureRequest.getJpegQuality();
         mRotationDegrees = takePictureRequest.getRotationDegrees();
         mCropRect = takePictureRequest.getCropRect();
@@ -110,10 +113,15 @@
     }
 
     @Nullable
-    List<ImageCapture.OutputFileOptions> getOutputFileOptions() {
+    ImageCapture.OutputFileOptions getOutputFileOptions() {
         return mOutputFileOptions;
     }
 
+    @Nullable
+    ImageCapture.OutputFileOptions getSecondaryOutputFileOptions() {
+        return mSecondaryOutputFileOptions;
+    }
+
     @NonNull
     Rect getCropRect() {
         return mCropRect;
@@ -133,7 +141,7 @@
     }
 
     boolean isInMemoryCapture() {
-        return getOutputFileOptions() == null || getOutputFileOptions().isEmpty();
+        return getOutputFileOptions() == null && getSecondaryOutputFileOptions() == null;
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureRequest.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureRequest.java
index 120b23c..6ed06ba 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureRequest.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/TakePictureRequest.java
@@ -101,7 +101,13 @@
      * Gets the app provided options for on-disk capture.
      */
     @Nullable
-    public abstract List<ImageCapture.OutputFileOptions> getOutputFileOptions();
+    public abstract ImageCapture.OutputFileOptions getOutputFileOptions();
+
+    /**
+     * Gets the app provided options for secondary on-disk capture.
+     */
+    @Nullable
+    public abstract ImageCapture.OutputFileOptions getSecondaryOutputFileOptions();
 
     /**
      * A snapshot of {@link ImageCapture#getViewPortCropRect()} when
@@ -283,7 +289,8 @@
     public static TakePictureRequest of(@NonNull Executor appExecutor,
             @Nullable ImageCapture.OnImageCapturedCallback inMemoryCallback,
             @Nullable ImageCapture.OnImageSavedCallback onDiskCallback,
-            @Nullable List<ImageCapture.OutputFileOptions> outputFileOptions,
+            @Nullable ImageCapture.OutputFileOptions outputFileOptions,
+            @Nullable ImageCapture.OutputFileOptions secondaryOutputFileOptions,
             @NonNull Rect cropRect,
             @NonNull Matrix sensorToBufferTransform,
             int rotationDegrees,
@@ -296,7 +303,7 @@
         checkArgument((onDiskCallback == null) ^ (inMemoryCallback == null),
                 "One and only one on-disk or in-memory callback should be present.");
         TakePictureRequest request = new AutoValue_TakePictureRequest(appExecutor, inMemoryCallback,
-                onDiskCallback, outputFileOptions, cropRect,
+                onDiskCallback, outputFileOptions, secondaryOutputFileOptions, cropRect,
                 sensorToBufferTransform, rotationDegrees, jpegQuality, captureMode,
                 isSimultaneousCapture,
                 sessionConfigCameraCaptureCallbacks);
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureExtTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureExtTest.kt
index af73043..962eec1 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureExtTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureExtTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.graphics.Bitmap
+import android.graphics.ImageFormat
 import android.os.Build
 import android.os.Looper
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
@@ -225,7 +226,7 @@
     @Test
     fun takePicture_onDisk_canGetResult(): Unit = runTest {
         // Arrange
-        val outputFileResults = ImageCapture.OutputFileResults(null)
+        val outputFileResults = ImageCapture.OutputFileResults(null, ImageFormat.JPEG)
 
         // Arrange & Act.
         val takePictureAsync =
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeProcessingRequest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeProcessingRequest.kt
index 7a40e02..0d0eec8 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeProcessingRequest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeProcessingRequest.kt
@@ -40,9 +40,8 @@
     ProcessingRequest(
         captureBundle,
         createTakePictureRequest(
-            if (outputFileOptions == null) null
-            else if (secondaryOutputFileOptions == null) listOf(outputFileOptions)
-            else listOf(outputFileOptions, secondaryOutputFileOptions),
+            outputFileOptions,
+            secondaryOutputFileOptions,
             cropRect,
             sensorToBufferTransform,
             rotationDegrees,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeTakePictureRequest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeTakePictureRequest.kt
index 7bf9c75..b99c301 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeTakePictureRequest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/FakeTakePictureRequest.kt
@@ -109,8 +109,12 @@
         imageSavedCallback = onDiskCallback
     }
 
-    override fun getOutputFileOptions(): List<ImageCapture.OutputFileOptions> {
-        return listOfNotNull(fileOptions, secondaryFileOptions)
+    override fun getOutputFileOptions(): ImageCapture.OutputFileOptions? {
+        return fileOptions
+    }
+
+    override fun getSecondaryOutputFileOptions(): ImageCapture.OutputFileOptions? {
+        return secondaryFileOptions
     }
 
     override fun getCropRect(): Rect {
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
index f6a7c82..8b2adf4f 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
@@ -67,7 +67,6 @@
 import androidx.camera.testing.impl.fakes.FakeImageReaderProxy
 import androidx.camera.testing.impl.fakes.GrayscaleImageEffect
 import androidx.core.util.Pair
-import com.google.common.collect.ImmutableList
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -373,7 +372,8 @@
 
                     override fun onError(exception: ImageCaptureException) {}
                 },
-                ImmutableList.of(OUTPUT_FILE_OPTIONS, SECONDARY_OUTPUT_FILE_OPTIONS),
+                OUTPUT_FILE_OPTIONS,
+                SECONDARY_OUTPUT_FILE_OPTIONS,
                 cropRect,
                 SENSOR_TO_BUFFER,
                 ROTATION_DEGREES,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
index d059a5a..ed218f1 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
@@ -139,7 +139,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    null,
                     CROP_RECT,
                     SENSOR_TO_BUFFER,
                     /*rotationDegrees=*/ 90,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
index 7653d47..b5757ec 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
@@ -84,7 +84,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     ROTATION_DEGREES,
@@ -113,7 +114,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     ROTATION_DEGREES,
@@ -144,7 +146,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     ROTATION_DEGREES,
@@ -183,7 +186,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS, SECONDARY_OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    SECONDARY_OUTPUT_FILE_OPTIONS,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     ROTATION_DEGREES,
@@ -230,7 +234,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     ROTATION_DEGREES,
@@ -258,7 +263,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     ROTATION_DEGREES,
@@ -287,7 +293,8 @@
             ProcessingRequest(
                 { listOf() },
                 createTakePictureRequest(
-                    listOf(OUTPUT_FILE_OPTIONS),
+                    OUTPUT_FILE_OPTIONS,
+                    null,
                     Rect(0, 0, WIDTH, HEIGHT),
                     SENSOR_TO_BUFFER,
                     ROTATION_DEGREES,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt
index 5f7324e..db3652f 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.camera.core.imagecapture
 
+import android.graphics.ImageFormat
 import android.os.Build
 import android.os.Looper.getMainLooper
 import androidx.camera.core.ImageCapture
@@ -57,7 +58,7 @@
         abortError = ImageCaptureException(ERROR_CAMERA_CLOSED, "", null)
         otherError = ImageCaptureException(ERROR_CAPTURE_FAILED, "", null)
         imageResult = FakeImageProxy(FakeImageInfo())
-        fileResult = ImageCapture.OutputFileResults(null)
+        fileResult = ImageCapture.OutputFileResults(null, ImageFormat.JPEG)
         retryControl = FakeRetryControl()
         captureRequestFuture = CallbackToFutureAdapter.getFuture { "captureRequestFuture" }
     }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt
index 62f161b..0d081ab 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/TakePictureManagerTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.camera.core.imagecapture
 
+import android.graphics.ImageFormat
 import android.hardware.camera2.CameraCharacteristics
 import android.os.Build
 import android.os.Looper.getMainLooper
@@ -173,7 +174,7 @@
         processingRequest.onImageCaptured()
         // Act.
         processingRequest.onFinalResult(FakeImageProxy(FakeImageInfo()))
-        processingRequest.onFinalResult(OutputFileResults(null))
+        processingRequest.onFinalResult(OutputFileResults(null, ImageFormat.JPEG))
     }
 
     @Test
@@ -327,7 +328,7 @@
         takePictureManager.offerRequest(request3)
         shadowOf(getMainLooper()).idle()
         val response1 = exception
-        val response2 = OutputFileResults(null)
+        val response2 = OutputFileResults(null, ImageFormat.JPEG)
         val response3 = FakeImageProxy(FakeImageInfo())
         imagePipeline.getProcessingRequest(request1).onImageCaptured()
         shadowOf(getMainLooper()).idle()
@@ -370,7 +371,7 @@
         takePictureManager.offerRequest(request)
 
         // Act: send OutputFileResults via ImagePipeline
-        val outputFileResults = OutputFileResults(null)
+        val outputFileResults = OutputFileResults(null, ImageFormat.JPEG)
         imagePipeline.getProcessingRequest(request).onImageCaptured()
         imagePipeline.getProcessingRequest(request).onFinalResult(outputFileResults)
         shadowOf(getMainLooper()).idle()
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Utils.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Utils.kt
index 3305a82..6536cdb 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Utils.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Utils.kt
@@ -72,7 +72,8 @@
         return ProcessingRequest(
             captureBundle,
             createTakePictureRequest(
-                listOf(OUTPUT_FILE_OPTIONS),
+                OUTPUT_FILE_OPTIONS,
+                null,
                 CROP_RECT,
                 SENSOR_TO_BUFFER,
                 ROTATION_DEGREES,
@@ -113,7 +114,8 @@
     }
 
     fun createTakePictureRequest(
-        outputFileOptions: List<ImageCapture.OutputFileOptions>?,
+        outputFileOptions: ImageCapture.OutputFileOptions?,
+        secondaryOutputFileOptions: ImageCapture.OutputFileOptions?,
         cropRect: Rect,
         sensorToBufferTransform: Matrix,
         rotationDegrees: Int,
@@ -133,6 +135,7 @@
             onMemoryCallback,
             onDiskCallback,
             outputFileOptions,
+            secondaryOutputFileOptions,
             cropRect,
             sensorToBufferTransform,
             rotationDegrees,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/wrappers/TakePictureManagerWrapper.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/wrappers/TakePictureManagerWrapper.kt
index a035008..c043e29 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/wrappers/TakePictureManagerWrapper.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/wrappers/TakePictureManagerWrapper.kt
@@ -167,23 +167,19 @@
                 //  user-provided data like bitmap/image proxy and use that to complete capture.
                 val bitmap = takePictureRequest.createBitmap()
 
-                val outputFileOptions =
-                    takePictureRequest.outputFileOptions // enables smartcast for null check
+                val outputFileOptions = takePictureRequest.outputFileOptions // enables
+                val secondaryOutputResults = takePictureRequest.secondaryOutputFileOptions
 
                 if (takePictureRequest.onDiskCallback != null && outputFileOptions != null) {
                     if (outputFileResultsQueue.isEmpty()) {
-                        if (outputFileOptions.size > 1) {
+                        if (secondaryOutputResults != null) {
                             Logger.w(
                                 TAG,
                                 "Simultaneous capture not supported, outputFileOptions = $outputFileOptions"
                             )
                         }
                         onFinalResult(
-                            createOutputFileResults(
-                                takePictureRequest,
-                                outputFileOptions[0],
-                                bitmap
-                            )
+                            createOutputFileResults(takePictureRequest, outputFileOptions, bitmap)
                         )
                     } else {
                         onFinalResult(outputFileResultsQueue.removeFirst())
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index b8cdb7e..8609e1b 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -1034,7 +1034,8 @@
                             ImageCapture.OutputFileOptions jpegOutputFileOptions =
                                     createOutputFileOptions(OUTPUT_FORMAT_JPEG);
                             getImageCapture().takePicture(
-                                    List.of(rawOutputFileOptions, jpegOutputFileOptions),
+                                    rawOutputFileOptions,
+                                    jpegOutputFileOptions,
                                     mImageCaptureExecutorService,
                                     callback);
                         } else {
diff --git a/collection/collection/bcv/native/current.txt b/collection/collection/bcv/native/current.txt
index 4fcb3f8..012c700f 100644
--- a/collection/collection/bcv/native/current.txt
+++ b/collection/collection/bcv/native/current.txt
@@ -981,6 +981,8 @@
 }
 
 sealed class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/ScatterMap { // androidx.collection/ScatterMap|null[0]
+    constructor <init>() // androidx.collection/ScatterMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/ScatterMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ScatterMap.size|{}size[0]
@@ -1022,6 +1024,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/FloatObjectMap { // androidx.collection/FloatObjectMap|null[0]
+    constructor <init>() // androidx.collection/FloatObjectMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/FloatObjectMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatObjectMap.size|{}size[0]
@@ -1063,6 +1067,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/IntObjectMap { // androidx.collection/IntObjectMap|null[0]
+    constructor <init>() // androidx.collection/IntObjectMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/IntObjectMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntObjectMap.size|{}size[0]
@@ -1104,6 +1110,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/LongObjectMap { // androidx.collection/LongObjectMap|null[0]
+    constructor <init>() // androidx.collection/LongObjectMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/LongObjectMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongObjectMap.size|{}size[0]
@@ -1145,6 +1153,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectFloatMap { // androidx.collection/ObjectFloatMap|null[0]
+    constructor <init>() // androidx.collection/ObjectFloatMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/ObjectFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ObjectFloatMap.size|{}size[0]
@@ -1187,6 +1197,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectIntMap { // androidx.collection/ObjectIntMap|null[0]
+    constructor <init>() // androidx.collection/ObjectIntMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/ObjectIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ObjectIntMap.size|{}size[0]
@@ -1229,6 +1241,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectList { // androidx.collection/ObjectList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/ObjectList.<init>|<init>(kotlin.Int){}[0]
+
     final val indices // androidx.collection/ObjectList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/ObjectList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/ObjectList.lastIndex|{}lastIndex[0]
@@ -1287,6 +1301,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectLongMap { // androidx.collection/ObjectLongMap|null[0]
+    constructor <init>() // androidx.collection/ObjectLongMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/ObjectLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ObjectLongMap.size|{}size[0]
@@ -1329,6 +1345,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/OrderedScatterSet { // androidx.collection/OrderedScatterSet|null[0]
+    constructor <init>() // androidx.collection/OrderedScatterSet.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/OrderedScatterSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/OrderedScatterSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/OrderedScatterSet.size|{}size[0]
@@ -1378,6 +1396,8 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ScatterSet { // androidx.collection/ScatterSet|null[0]
+    constructor <init>() // androidx.collection/ScatterSet.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/ScatterSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ScatterSet.size|{}size[0]
@@ -1412,6 +1432,8 @@
 }
 
 sealed class androidx.collection/DoubleList { // androidx.collection/DoubleList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/DoubleList.<init>|<init>(kotlin.Int){}[0]
+
     final val indices // androidx.collection/DoubleList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/DoubleList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/DoubleList.lastIndex|{}lastIndex[0]
@@ -1464,6 +1486,8 @@
 }
 
 sealed class androidx.collection/FloatFloatMap { // androidx.collection/FloatFloatMap|null[0]
+    constructor <init>() // androidx.collection/FloatFloatMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/FloatFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatFloatMap.size|{}size[0]
@@ -1506,6 +1530,8 @@
 }
 
 sealed class androidx.collection/FloatIntMap { // androidx.collection/FloatIntMap|null[0]
+    constructor <init>() // androidx.collection/FloatIntMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/FloatIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatIntMap.size|{}size[0]
@@ -1548,6 +1574,8 @@
 }
 
 sealed class androidx.collection/FloatList { // androidx.collection/FloatList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/FloatList.<init>|<init>(kotlin.Int){}[0]
+
     final val indices // androidx.collection/FloatList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/FloatList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/FloatList.lastIndex|{}lastIndex[0]
@@ -1600,6 +1628,8 @@
 }
 
 sealed class androidx.collection/FloatLongMap { // androidx.collection/FloatLongMap|null[0]
+    constructor <init>() // androidx.collection/FloatLongMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/FloatLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatLongMap.size|{}size[0]
@@ -1642,6 +1672,8 @@
 }
 
 sealed class androidx.collection/FloatSet { // androidx.collection/FloatSet|null[0]
+    constructor <init>() // androidx.collection/FloatSet.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/FloatSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatSet.size|{}size[0]
@@ -1675,6 +1707,8 @@
 }
 
 sealed class androidx.collection/IntFloatMap { // androidx.collection/IntFloatMap|null[0]
+    constructor <init>() // androidx.collection/IntFloatMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/IntFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntFloatMap.size|{}size[0]
@@ -1717,6 +1751,8 @@
 }
 
 sealed class androidx.collection/IntIntMap { // androidx.collection/IntIntMap|null[0]
+    constructor <init>() // androidx.collection/IntIntMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/IntIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntIntMap.size|{}size[0]
@@ -1759,6 +1795,8 @@
 }
 
 sealed class androidx.collection/IntList { // androidx.collection/IntList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/IntList.<init>|<init>(kotlin.Int){}[0]
+
     final val indices // androidx.collection/IntList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/IntList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/IntList.lastIndex|{}lastIndex[0]
@@ -1811,6 +1849,8 @@
 }
 
 sealed class androidx.collection/IntLongMap { // androidx.collection/IntLongMap|null[0]
+    constructor <init>() // androidx.collection/IntLongMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/IntLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntLongMap.size|{}size[0]
@@ -1853,6 +1893,8 @@
 }
 
 sealed class androidx.collection/IntSet { // androidx.collection/IntSet|null[0]
+    constructor <init>() // androidx.collection/IntSet.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/IntSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntSet.size|{}size[0]
@@ -1886,6 +1928,8 @@
 }
 
 sealed class androidx.collection/LongFloatMap { // androidx.collection/LongFloatMap|null[0]
+    constructor <init>() // androidx.collection/LongFloatMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/LongFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongFloatMap.size|{}size[0]
@@ -1928,6 +1972,8 @@
 }
 
 sealed class androidx.collection/LongIntMap { // androidx.collection/LongIntMap|null[0]
+    constructor <init>() // androidx.collection/LongIntMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/LongIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongIntMap.size|{}size[0]
@@ -1970,6 +2016,8 @@
 }
 
 sealed class androidx.collection/LongList { // androidx.collection/LongList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/LongList.<init>|<init>(kotlin.Int){}[0]
+
     final val indices // androidx.collection/LongList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/LongList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/LongList.lastIndex|{}lastIndex[0]
@@ -2022,6 +2070,8 @@
 }
 
 sealed class androidx.collection/LongLongMap { // androidx.collection/LongLongMap|null[0]
+    constructor <init>() // androidx.collection/LongLongMap.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/LongLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongLongMap.size|{}size[0]
@@ -2064,6 +2114,8 @@
 }
 
 sealed class androidx.collection/LongSet { // androidx.collection/LongSet|null[0]
+    constructor <init>() // androidx.collection/LongSet.<init>|<init>(){}[0]
+
     final val capacity // androidx.collection/LongSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongSet.size|{}size[0]
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 62e0b08..fac6444 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -135,4 +135,7 @@
 android {
     compileSdk 35
     namespace "androidx.compose.animation.core"
+    buildTypes.configureEach {
+        consumerProguardFiles("proguard-rules.pro")
+    }
 }
diff --git a/compose/animation/animation-core/proguard-rules.pro b/compose/animation/animation-core/proguard-rules.pro
new file mode 100644
index 0000000..67d118b
--- /dev/null
+++ b/compose/animation/animation-core/proguard-rules.pro
@@ -0,0 +1,24 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Keep all the functions created to throw an exception. We don't want these functions to be
+# inlined in any way, which R8 will do by default. The whole point of these functions is to
+# reduce the amount of code generated at the call site.
+-keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+    static void throw*Exception(...);
+    static void throw*ExceptionForNullCheck(...);
+    # For methods returning Nothing
+    static java.lang.Void throw*Exception(...);
+    static java.lang.Void throw*ExceptionForNullCheck(...);
+}
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.android.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.android.kt
index 03417725..05381b5 100644
--- a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.android.kt
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.android.kt
@@ -25,11 +25,11 @@
 // This test can't be in commonTest because
 // Float.ulp is jvm only: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/ulp.html
 class EasingTestAndroid {
-    private val ZeroEpsilon = -(1.0f.ulp * 2.0f)
+    private val ZeroEpsilon = 1.0f.ulp * 2.0f
     private val OneEpsilon = 1.0f + 1.0f.ulp * 2.0f
 
     @Test
-    fun canSolveCubicForFractionsCloseToOne() {
+    fun canSolveCubicForFractionsCloseToZeroOrOne() {
         // Only test curves defined in [0..1]
         // For instance, EaseInOutBack is defined in a larger domain, so exclude it from the list
         val curves =
@@ -67,7 +67,6 @@
             )
 
         for (curve in curves) {
-            // Test the last 16 ulps until 1.0f
             for (i in 0x3f7f9d99..0x3f7fffff) {
                 val fraction = floatFromBits(i)
                 val t = curve.transform(fraction)
@@ -76,6 +75,23 @@
                     "f($fraction) = $t out of range for $curve | ${-ZeroEpsilon}..${OneEpsilon}"
                 )
             }
+
+            for (i in 0x0..0x6266) {
+                val fraction = floatFromBits(i)
+                val t = curve.transform(fraction)
+                assertTrue(
+                    t in -ZeroEpsilon..OneEpsilon,
+                    "f($fraction) = $t out of range for $curve | ${-ZeroEpsilon}..${OneEpsilon}"
+                )
+            }
+
+            // Test at 1.5058824E-7, small value but not too close to 0.0 either
+            val fraction = floatFromBits(0x3421b161)
+            val t = curve.transform(fraction)
+            assertTrue(
+                t in -ZeroEpsilon..OneEpsilon,
+                "f($fraction) = $t out of range for $curve | ${-ZeroEpsilon}..${OneEpsilon}"
+            )
         }
     }
 
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
index 9ff5f0f..80bdfb9 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
@@ -22,6 +22,7 @@
 import androidx.compose.ui.graphics.evaluateCubic
 import androidx.compose.ui.graphics.findFirstCubicRoot
 import androidx.compose.ui.util.fastCoerceIn
+import kotlin.math.max
 
 /**
  * Easing is a way to adjust an animation’s fraction. Easing allows transitioning elements to speed
@@ -70,6 +71,10 @@
  */
 public val LinearEasing: Easing = Easing { fraction -> fraction }
 
+// This is equal to 1f.ulp or 1f.nextUp() - 1f, but neither ulp nor nextUp() are part of all KMP
+// targets, only JVM and native
+private const val OneUlpAt1 = 1.1920929e-7f
+
 /**
  * A cubic polynomial easing.
  *
@@ -125,12 +130,16 @@
      */
     override fun transform(fraction: Float): Float {
         return if (fraction > 0f && fraction < 1f) {
+            // We translate the coordinates by the fraction when calling findFirstCubicRoot,
+            // but we need to make sure the translation can be done at 1.0f so we take at
+            // least 1 ulp at 1.0f
+            val f = max(fraction, OneUlpAt1)
             val t =
                 findFirstCubicRoot(
-                    0.0f - fraction,
-                    a - fraction,
-                    c - fraction,
-                    1.0f - fraction,
+                    0.0f - f,
+                    a - f,
+                    c - f,
+                    1.0f - f,
                 )
 
             // No root, the cubic curve has no solution
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index 45e5e44..185b73a 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -231,12 +231,6 @@
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
-  public final class InsetsHelper_androidKt {
-    method @androidx.compose.runtime.Composable public static androidx.core.graphics.Insets roundToAndroidXInsets(androidx.compose.ui.unit.DpRect);
-    method public static androidx.core.graphics.Insets toAndroidXInsets(androidx.compose.ui.unit.IntRect);
-    method public static androidx.compose.ui.unit.IntRect toComposeIntRect(androidx.core.graphics.Insets);
-  }
-
   public final class IntrinsicKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index 0d70ee8..fbd6f49 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -237,12 +237,6 @@
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
-  public final class InsetsHelper_androidKt {
-    method @androidx.compose.runtime.Composable public static androidx.core.graphics.Insets roundToAndroidXInsets(androidx.compose.ui.unit.DpRect);
-    method public static androidx.core.graphics.Insets toAndroidXInsets(androidx.compose.ui.unit.IntRect);
-    method public static androidx.compose.ui.unit.IntRect toComposeIntRect(androidx.core.graphics.Insets);
-  }
-
   public final class IntrinsicKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
diff --git a/compose/foundation/foundation-layout/proguard-rules.pro b/compose/foundation/foundation-layout/proguard-rules.pro
index d07663a..67d118b 100644
--- a/compose/foundation/foundation-layout/proguard-rules.pro
+++ b/compose/foundation/foundation-layout/proguard-rules.pro
@@ -17,6 +17,8 @@
 # reduce the amount of code generated at the call site.
 -keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
     static void throw*Exception(...);
+    static void throw*ExceptionForNullCheck(...);
     # For methods returning Nothing
     static java.lang.Void throw*Exception(...);
+    static java.lang.Void throw*ExceptionForNullCheck(...);
 }
diff --git a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/InsetsHelper.kt b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/InsetsHelper.kt
deleted file mode 100644
index a5e3a11..0000000
--- a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/InsetsHelper.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.layout
-
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.DpRect
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import org.junit.Assert.assertEquals
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class InsetsHelperTest {
-    @get:Rule val rule = createComposeRule()
-
-    @Test
-    fun dpRectToAndroidXInsetsConverts() {
-        lateinit var androidXInsets: androidx.core.graphics.Insets
-        lateinit var density: Density
-
-        rule.setContent {
-            androidXInsets = DpRect(5.dp, 6.dp, 7.dp, 8.dp).roundToAndroidXInsets()
-            density = LocalDensity.current
-        }
-
-        assertEquals(with(density) { 5.dp.roundToPx() }, androidXInsets.left)
-        assertEquals(with(density) { 6.dp.roundToPx() }, androidXInsets.top)
-        assertEquals(with(density) { 7.dp.roundToPx() }, androidXInsets.right)
-        assertEquals(with(density) { 8.dp.roundToPx() }, androidXInsets.bottom)
-    }
-}
diff --git a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/InsetsHelper.android.kt b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/InsetsHelper.android.kt
deleted file mode 100644
index 079d267..0000000
--- a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/InsetsHelper.android.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.layout
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.unit.DpRect
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.roundToIntRect
-
-/**
- * Creates a new instance of [androidx.core.graphics.Insets] with the same bounds specified in the
- * given [IntRect].
- */
-fun IntRect.toAndroidXInsets(): androidx.core.graphics.Insets =
-    androidx.core.graphics.Insets.of(left, top, right, bottom)
-
-/**
- * Creates a new instance of [IntRect] with the same bounds specified in the given
- * [androidx.core.graphics.Insets].
- */
-fun androidx.core.graphics.Insets.toComposeIntRect(): IntRect = IntRect(left, top, right, bottom)
-
-/**
- * Converts the [DpRect] to [androidx.core.graphics.Insets] by using the [LocalDensity] and rounding
- * to the nearest pixel values in each dimension.
- */
-@Composable
-fun DpRect.roundToAndroidXInsets(): androidx.core.graphics.Insets =
-    with(LocalDensity.current) { toRect() }.roundToIntRect().toAndroidXInsets()
diff --git a/compose/foundation/foundation-layout/src/androidUnitTest/kotlin/androidx/compose/foundation/layout/InsetsHelperTest.kt b/compose/foundation/foundation-layout/src/androidUnitTest/kotlin/androidx/compose/foundation/layout/InsetsHelperTest.kt
deleted file mode 100644
index 8c1ebbb..0000000
--- a/compose/foundation/foundation-layout/src/androidUnitTest/kotlin/androidx/compose/foundation/layout/InsetsHelperTest.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.layout
-
-import androidx.compose.ui.unit.IntRect
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class InsetsHelperTest {
-    @Test
-    fun intRectToAndroidXInsetsConverts() {
-        assertEquals(
-            androidx.core.graphics.Insets.of(5, 6, 7, 8),
-            IntRect(5, 6, 7, 8).toAndroidXInsets(),
-        )
-    }
-
-    @Test
-    fun androidXInsetsToOntRectConverts() {
-        assertEquals(
-            IntRect(5, 6, 7, 8),
-            androidx.core.graphics.Insets.of(5, 6, 7, 8).toComposeIntRect(),
-        )
-    }
-}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ScrollableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
index fb71215..4e75878 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
@@ -55,7 +55,6 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
@@ -130,7 +129,6 @@
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import kotlin.math.abs
-import kotlin.math.absoluteValue
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -143,6 +141,7 @@
 import org.junit.After
 import org.junit.Assert
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -1850,66 +1849,6 @@
     }
 
     @Test
-    fun scrollable_nestedFling_parentShouldFlingWithVelocityLeft_whenInnerDisappears() {
-        var postFlingCalled = false
-        var postFlingAvailableVelocity = Velocity.Zero
-        var postFlingConsumedVelocity = Velocity.Zero
-        var flingDelta by mutableFloatStateOf(0.0f)
-        var preFlingVelocity = Velocity.Zero
-
-        val topConnection =
-            object : NestedScrollConnection {
-                override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
-                    // accumulate deltas for second fling only
-                    if (source == NestedScrollSource.SideEffect) {
-                        flingDelta += available.y
-                    }
-                    return super.onPreScroll(available, source)
-                }
-
-                override suspend fun onPreFling(available: Velocity): Velocity {
-                    preFlingVelocity = available
-                    return super.onPreFling(available)
-                }
-
-                override suspend fun onPostFling(
-                    consumed: Velocity,
-                    available: Velocity
-                ): Velocity {
-                    postFlingCalled = true
-                    postFlingAvailableVelocity = available
-                    postFlingConsumedVelocity = consumed
-                    return super.onPostFling(consumed, available)
-                }
-            }
-
-        val columnState = ScrollState(with(rule.density) { (50 * 200.dp).roundToPx() })
-
-        rule.setContent {
-            Box(Modifier.nestedScroll(topConnection)) {
-                if (flingDelta.absoluteValue < 100) {
-                    Column(Modifier.testTag("column").verticalScroll(columnState)) {
-                        repeat(100) { Box(Modifier.size(200.dp)) }
-                    }
-                }
-            }
-        }
-
-        rule.onNodeWithTag("column").performTouchInput { swipeUp() }
-        rule.waitForIdle()
-        // removed scrollable
-        rule.onNodeWithTag("column").assertDoesNotExist()
-        rule.runOnIdle {
-            // we fired a post fling call after the disappearance
-            assertThat(postFlingCalled).isTrue()
-
-            // fling velocity in onPostFling is correctly propagated
-            assertThat(postFlingConsumedVelocity + postFlingAvailableVelocity)
-                .isEqualTo(preFlingVelocity)
-        }
-    }
-
-    @Test
     fun scrollable_bothOrientations_proxiesPostFling() {
         val velocityFlung = 5000f
         val outerState = ScrollableState(consumeScrollDelta = { 0f })
@@ -2582,6 +2521,77 @@
     }
 
     @Test
+    @Ignore("b/175010956") // re-enable when we come back to fling continuation fix
+    fun nestedScrollable_shouldImmediateScrollIfChildIsFlinging() {
+        var innerDelta = 0f
+        var middleDelta = 0f
+        var outerDelta = 0f
+        var touchSlop = 0f
+
+        val outerStateController = ScrollableState {
+            outerDelta += it
+            0f
+        }
+
+        val middleController = ScrollableState {
+            middleDelta += it
+            0f
+        }
+
+        val innerController = ScrollableState {
+            innerDelta += it
+            it / 2f
+        }
+
+        rule.setContentAndGetScope {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            Box(
+                modifier =
+                    Modifier.testTag("outerScrollable")
+                        .size(600.dp)
+                        .background(Color.Red)
+                        .scrollable(outerStateController, orientation = Orientation.Vertical),
+                contentAlignment = Alignment.BottomStart
+            ) {
+                Box(
+                    modifier =
+                        Modifier.testTag("middleScrollable")
+                            .size(300.dp)
+                            .background(Color.Blue)
+                            .scrollable(middleController, orientation = Orientation.Vertical),
+                    contentAlignment = Alignment.BottomStart
+                ) {
+                    Box(
+                        modifier =
+                            Modifier.testTag("innerScrollable")
+                                .size(50.dp)
+                                .background(Color.Yellow)
+                                .scrollable(innerController, orientation = Orientation.Vertical)
+                    )
+                }
+            }
+        }
+
+        rule.mainClock.autoAdvance = false
+        rule.onNodeWithTag("innerScrollable").performTouchInput { swipeUp() }
+
+        rule.mainClock.advanceTimeByFrame()
+        rule.mainClock.advanceTimeByFrame()
+
+        val previousOuter = outerDelta
+
+        rule.onNodeWithTag("outerScrollable").performTouchInput {
+            down(topCenter)
+            // Move less than touch slop, should start immediately
+            moveBy(Offset(0f, touchSlop / 2))
+        }
+
+        rule.mainClock.autoAdvance = true
+
+        rule.runOnIdle { assertThat(outerDelta).isEqualTo(previousOuter + touchSlop / 2) }
+    }
+
+    @Test
     fun nestedScrollable_noFlingContinuationInCrossAxis_shouldAllowClicksOnCrossAxis_scrollable() {
         var clicked = 0
         rule.setContentAndGetScope {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
index 9fd4053..425cd33 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
@@ -30,13 +30,17 @@
 import androidx.compose.foundation.text.TEST_FONT_FAMILY
 import androidx.compose.foundation.text.input.TextFieldLineLimits
 import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.foundation.text.input.internal.CodepointTransformation
+import androidx.compose.foundation.text.input.internal.mask
 import androidx.compose.foundation.text.input.placeCursorAtEnd
 import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
 import androidx.compose.foundation.text.selection.assertHandlePositionMatches
 import androidx.compose.foundation.text.selection.isSelectionHandle
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.onSizeChanged
@@ -46,6 +50,7 @@
 import androidx.compose.ui.platform.WindowInfo
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.hasPerformImeAction
 import androidx.compose.ui.test.isDisplayed
@@ -587,6 +592,32 @@
     }
 
     @Test
+    fun cursorHandle_disappears_whenCodepointTransformationChanges() {
+        state = TextFieldState("hello")
+
+        var codepointTransformation: CodepointTransformation? by mutableStateOf(null)
+
+        rule.setContent {
+            BasicTextField(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier.testTag(TAG),
+                codepointTransformation = codepointTransformation,
+            )
+        }
+
+        focusAndWait()
+
+        rule.onNodeWithTag(TAG).performClick()
+        rule.onNode(isSelectionHandle(Handle.Cursor)).assertIsDisplayed()
+
+        codepointTransformation = CodepointTransformation.mask('c')
+
+        rule.waitForIdle()
+        rule.onNode(isSelectionHandle(Handle.Cursor)).assertIsNotDisplayed()
+    }
+
+    @Test
     fun cursorHandleDrag_getsFiltered() {
         state = TextFieldState("abc abc")
         rule.setTextFieldTestContent {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index 5d6a2da..f81e6db 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -298,16 +298,6 @@
             reverseDirection = reverseDirection,
             flingBehavior = flingBehavior ?: defaultFlingBehavior,
             nestedScrollDispatcher = nestedScrollDispatcher,
-            shouldCancelFling = { flingPixels ->
-                // fling should be cancelled if we try to scroll more than we can or if this node
-                // is detached during a fling.
-                // tries to scroll forward but cannot.
-                (flingPixels > 0.0f && !state.canScrollForward) ||
-                    // tries to scroll backward but cannot.
-                    (flingPixels < 0.0f && !state.canScrollBackward) ||
-                    // node is detached.
-                    !isAttached
-            }
         )
 
     private val nestedScrollConnection =
@@ -350,7 +340,12 @@
 
     @OptIn(ExperimentalFoundationApi::class)
     override fun onDragStopped(velocity: Velocity) {
-        nestedScrollDispatcher.coroutineScope.launch { scrollingLogic.onDragStopped(velocity) }
+        if (NewNestedFlingPropagationEnabled) {
+            if (!isAttached) return
+            coroutineScope.launch { scrollingLogic.onDragStopped(velocity) }
+        } else {
+            nestedScrollDispatcher.coroutineScope.launch { scrollingLogic.onDragStopped(velocity) }
+        }
     }
 
     override fun startDragImmediately(): Boolean {
@@ -602,7 +597,6 @@
     private var orientation: Orientation,
     private var reverseDirection: Boolean,
     private var nestedScrollDispatcher: NestedScrollDispatcher,
-    private val shouldCancelFling: (Float) -> Boolean
 ) {
 
     fun Float.toOffset(): Offset =
@@ -723,14 +717,20 @@
             val reverseScope =
                 object : ScrollScope {
                     override fun scrollBy(pixels: Float): Float {
-                        // Fling has hit the bounds or node left composition,
-                        // cancel it to allow continuation. This will conclude this node's fling,
-                        // allowing the onPostFling signal to be called
+                        // Fling has hit the bounds, cancel it to allow continuation. This will
+                        // conclude this node's fling, allowing the onPostFling signal to be called
                         // with the leftover velocity from the fling animation. Any nested scroll
                         // node above will be able to pick up the left over velocity and continue
                         // the fling.
-                        if (NewNestedFlingPropagationEnabled && shouldCancelFling(pixels)) {
-                            throw FlingCancellationException()
+                        if (NewNestedFlingPropagationEnabled) {
+                            if (
+                                pixels > 0.0f && !scrollableState.canScrollForward ||
+                                    pixels < 0.0f && !scrollableState.canScrollBackward
+                            ) {
+                                throw kotlin.coroutines.cancellation.CancellationException(
+                                    "The fling was cancelled"
+                                )
+                            }
                         }
 
                         return nestedScrollScope
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
index 5d15ac5..36da905 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
@@ -414,9 +414,10 @@
 @Composable
 internal fun TextFieldCursorHandle(selectionState: TextFieldSelectionState) {
     // Does not recompose if only position of the handle changes.
-    val cursorHandleState by remember {
-        derivedStateOf { selectionState.getCursorHandleState(includePosition = false) }
-    }
+    val cursorHandleState by
+        remember(selectionState) {
+            derivedStateOf { selectionState.getCursorHandleState(includePosition = false) }
+        }
     if (cursorHandleState.visible) {
         CursorHandle(
             offsetProvider = {
diff --git a/compose/material/material-navigation/api/restricted_current.txt b/compose/material/material-navigation/api/restricted_current.txt
index dd70def..1dbe896 100644
--- a/compose/material/material-navigation/api/restricted_current.txt
+++ b/compose/material/material-navigation/api/restricted_current.txt
@@ -39,6 +39,7 @@
   public final class NavGraphBuilderKt {
     method public static void bottomSheet(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ColumnScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
     method public static inline <reified T> void bottomSheet(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ColumnScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method @kotlin.PublishedApi internal static void bottomSheet(androidx.navigation.NavGraphBuilder, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ColumnScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
   }
 
 }
diff --git a/compose/material/material-navigation/src/main/java/androidx/compose/material/navigation/NavGraphBuilder.kt b/compose/material/material-navigation/src/main/java/androidx/compose/material/navigation/NavGraphBuilder.kt
index 9ecaa4b..f802f25 100644
--- a/compose/material/material-navigation/src/main/java/androidx/compose/material/navigation/NavGraphBuilder.kt
+++ b/compose/material/material-navigation/src/main/java/androidx/compose/material/navigation/NavGraphBuilder.kt
@@ -73,10 +73,21 @@
     deepLinks: List<NavDeepLink> = emptyList(),
     noinline content: @Composable ColumnScope.(backstackEntry: NavBackStackEntry) -> Unit
 ) {
+    bottomSheet(T::class, typeMap, arguments, deepLinks, content)
+}
+
+@PublishedApi
+internal fun NavGraphBuilder.bottomSheet(
+    route: KClass<*>,
+    typeMap: Map<KType, @JvmSuppressWildcards NavType<*>>,
+    arguments: List<NamedNavArgument>,
+    deepLinks: List<NavDeepLink> = emptyList(),
+    content: @Composable ColumnScope.(backstackEntry: NavBackStackEntry) -> Unit
+) {
     destination(
         BottomSheetNavigatorDestinationBuilder(
                 provider[BottomSheetNavigator::class],
-                T::class,
+                route,
                 typeMap,
                 content
             )
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt
index b055339..a90ff91 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt
@@ -169,7 +169,6 @@
                 newSize
             }
         target.size = newSize
-        createBoundsAnimationIfPossible()
     }
 
     fun updateTargetOffset(newOffset: IntOffset) {
@@ -185,26 +184,31 @@
                 newOffset
             }
         target.topLeft = newOffset
-        createBoundsAnimationIfPossible()
     }
 
     fun updateAndGetCurrentBounds(fraction: Float): IntRect {
         current =
-            if (fraction == 0f) {
-                origin.rect
-            } else if (fraction == 1f) {
-                target.rect
-            } else if (boundsAnimation != null) {
-                boundsAnimation!!.valueAtProgress(fraction)
-            } else {
-                InvalidIntRect
+            when (fraction) {
+                0f -> origin.rect
+                1f -> target.rect
+                else -> {
+                    updateBoundsAnimationIfNeeded()
+                    boundsAnimation?.valueAtProgress(fraction) ?: InvalidIntRect
+                }
             }
 
         return current
     }
 
-    private fun createBoundsAnimationIfPossible() {
-        if (origin.isValid && target.isValid) {
+    private fun updateBoundsAnimationIfNeeded() {
+        if (!origin.isValid || !target.isValid) {
+            return
+        }
+        if (
+            boundsAnimation == null ||
+                boundsAnimation!!.initialValue != origin.rect ||
+                boundsAnimation!!.targetValue != target.rect
+        ) {
             boundsAnimation =
                 TargetBasedAnimation(animationSpec, IntRectToVector, origin.rect, target.rect)
         }
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
index 8f3d4a6..9c1865c 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
@@ -21,18 +21,13 @@
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Edit
 import androidx.compose.material.icons.outlined.KeyboardArrowDown
-import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.ElevatedSplitButton
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import androidx.compose.material3.FilledSplitButton
 import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedSplitButton
-import androidx.compose.material3.SplitButton
+import androidx.compose.material3.SplitButtonDefaults
+import androidx.compose.material3.SplitButtonLayout
 import androidx.compose.material3.Text
-import androidx.compose.material3.TonalSplitButton
 import androidx.compose.runtime.Composable
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
@@ -97,51 +92,77 @@
     @Composable
     override fun MeasuredContent() {
         when (type) {
-            SplitButtonType.SplitButton ->
-                SplitButton(
+            SplitButtonType.Filled ->
+                SplitButtonLayout(
                     leadingButton = {
-                        Button(onClick = { /* Do something! */ }) { Text("Button") }
+                        SplitButtonDefaults.LeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            leadingContent()
+                        }
                     },
                     trailingButton = {
-                        IconButton(onClick = { /* doSomething() */ }) {
-                            Icon(
-                                Icons.Outlined.Edit,
-                                contentDescription = "Localized description",
-                            )
+                        SplitButtonDefaults.TrailingButton(
+                            checked = false,
+                            onCheckedChange = { /* Do Nothing */ },
+                        ) {
+                            trailingContent()
                         }
                     }
                 )
-            SplitButtonType.Filled ->
-                FilledSplitButton(
-                    onLeadingButtonClick = {},
-                    checked = false,
-                    onTrailingButtonClick = { /* Do Nothing */ },
-                    leadingContent = { leadingContent() },
-                    trailingContent = { trailingContent() },
-                )
             SplitButtonType.Tonal ->
-                TonalSplitButton(
-                    onLeadingButtonClick = {},
-                    checked = false,
-                    onTrailingButtonClick = { /* Do Nothing */ },
-                    leadingContent = { leadingContent() },
-                    trailingContent = { trailingContent() },
+                SplitButtonLayout(
+                    leadingButton = {
+                        SplitButtonDefaults.TonalLeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            leadingContent()
+                        }
+                    },
+                    trailingButton = {
+                        SplitButtonDefaults.TonalTrailingButton(
+                            checked = false,
+                            onCheckedChange = { /* Do Nothing */ },
+                        ) {
+                            trailingContent()
+                        }
+                    }
                 )
             SplitButtonType.Elevated ->
-                ElevatedSplitButton(
-                    onLeadingButtonClick = {},
-                    checked = false,
-                    onTrailingButtonClick = { /* Do Nothing */ },
-                    leadingContent = { leadingContent() },
-                    trailingContent = { trailingContent() },
+                SplitButtonLayout(
+                    leadingButton = {
+                        SplitButtonDefaults.ElevatedLeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            leadingContent()
+                        }
+                    },
+                    trailingButton = {
+                        SplitButtonDefaults.ElevatedTrailingButton(
+                            checked = false,
+                            onCheckedChange = { /* Do Nothing */ },
+                        ) {
+                            trailingContent()
+                        }
+                    }
                 )
             SplitButtonType.Outlined ->
-                OutlinedSplitButton(
-                    onLeadingButtonClick = {},
-                    checked = false,
-                    onTrailingButtonClick = { /* Do Nothing */ },
-                    leadingContent = { leadingContent() },
-                    trailingContent = { trailingContent() },
+                SplitButtonLayout(
+                    leadingButton = {
+                        SplitButtonDefaults.OutlinedLeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            leadingContent()
+                        }
+                    },
+                    trailingButton = {
+                        SplitButtonDefaults.OutlinedTrailingButton(
+                            checked = false,
+                            onCheckedChange = { /* Do Nothing */ },
+                        ) {
+                            trailingContent()
+                        }
+                    }
                 )
         }
     }
@@ -168,7 +189,6 @@
 }
 
 enum class SplitButtonType {
-    SplitButton,
     Filled,
     Tonal,
     Elevated,
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index f131965..dd78059 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -2119,8 +2119,15 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class SplitButtonDefaults {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void ElevatedLeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void ElevatedTrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void LeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void OutlinedLeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void OutlinedTrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TonalLeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TonalTrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method public androidx.compose.foundation.shape.CornerSize getInnerCornerSize();
     method public androidx.compose.foundation.layout.PaddingValues getLeadingButtonContentPadding();
     method public float getLeadingIconSize();
@@ -2141,11 +2148,7 @@
   }
 
   public final class SplitButtonKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ElevatedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FilledSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void OutlinedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> leadingButton, kotlin.jvm.functions.Function0<kotlin.Unit> trailingButton, optional androidx.compose.ui.Modifier modifier, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void TonalSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SplitButtonLayout(kotlin.jvm.functions.Function0<kotlin.Unit> leadingButton, kotlin.jvm.functions.Function0<kotlin.Unit> trailingButton, optional androidx.compose.ui.Modifier modifier, optional float spacing);
   }
 
   public final class SplitButtonShapes {
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index f131965..dd78059 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -2119,8 +2119,15 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class SplitButtonDefaults {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void ElevatedLeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void ElevatedTrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void LeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void OutlinedLeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void OutlinedTrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TonalLeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TonalTrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SplitButtonShapes shapes, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method public androidx.compose.foundation.shape.CornerSize getInnerCornerSize();
     method public androidx.compose.foundation.layout.PaddingValues getLeadingButtonContentPadding();
     method public float getLeadingIconSize();
@@ -2141,11 +2148,7 @@
   }
 
   public final class SplitButtonKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ElevatedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FilledSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void OutlinedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> leadingButton, kotlin.jvm.functions.Function0<kotlin.Unit> trailingButton, optional androidx.compose.ui.Modifier modifier, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void TonalSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SplitButtonLayout(kotlin.jvm.functions.Function0<kotlin.Unit> leadingButton, kotlin.jvm.functions.Function0<kotlin.Unit> trailingButton, optional androidx.compose.ui.Modifier modifier, optional float spacing);
   }
 
   public final class SplitButtonShapes {
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index d5987aa..2d8c46a 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -203,9 +203,9 @@
 import androidx.compose.material3.samples.SmallExtendedFloatingActionButtonSample
 import androidx.compose.material3.samples.SmallExtendedFloatingActionButtonTextSample
 import androidx.compose.material3.samples.SmallFloatingActionButtonSample
-import androidx.compose.material3.samples.SplitButtonSample
 import androidx.compose.material3.samples.SplitButtonWithIconSample
 import androidx.compose.material3.samples.SplitButtonWithTextSample
+import androidx.compose.material3.samples.SplitButtonWithUnCheckableTrailingButtonSample
 import androidx.compose.material3.samples.SquareButtonSample
 import androidx.compose.material3.samples.StepRangeSliderSample
 import androidx.compose.material3.samples.StepsSliderSample
@@ -1664,13 +1664,6 @@
 val SplitButtonExamples =
     listOf(
         Example(
-            name = "SplitButtonSample",
-            description = SplitButtonExampleDescription,
-            sourceUrl = SplitButtonSourceUrl
-        ) {
-            SplitButtonSample()
-        },
-        Example(
             name = "FilledSplitButtonSample",
             description = SplitButtonExampleDescription,
             sourceUrl = SplitButtonSourceUrl
@@ -1678,6 +1671,13 @@
             FilledSplitButtonSample()
         },
         Example(
+            name = "SplitButtonWithUnCheckableTrailingButtonSample",
+            description = SplitButtonExampleDescription,
+            sourceUrl = SplitButtonSourceUrl
+        ) {
+            SplitButtonWithUnCheckableTrailingButtonSample()
+        },
+        Example(
             name = "TonalSplitButtonSample",
             description = SplitButtonExampleDescription,
             sourceUrl = SplitButtonSourceUrl
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
index 3342355..0a7af3f 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
@@ -24,15 +24,11 @@
 import androidx.compose.material.icons.filled.Edit
 import androidx.compose.material.icons.filled.KeyboardArrowDown
 import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.ElevatedSplitButton
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import androidx.compose.material3.FilledSplitButton
 import androidx.compose.material3.Icon
-import androidx.compose.material3.OutlinedSplitButton
-import androidx.compose.material3.SplitButton
 import androidx.compose.material3.SplitButtonDefaults
+import androidx.compose.material3.SplitButtonLayout
 import androidx.compose.material3.Text
-import androidx.compose.material3.TonalSplitButton
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -49,10 +45,10 @@
 @Sampled
 @Composable
 @Preview
-fun SplitButtonSample() {
+fun FilledSplitButtonSample() {
     var checked by remember { mutableStateOf(false) }
 
-    SplitButton(
+    SplitButtonLayout(
         leadingButton = {
             SplitButtonDefaults.LeadingButton(
                 onClick = { /* Do Nothing */ },
@@ -68,8 +64,8 @@
         },
         trailingButton = {
             SplitButtonDefaults.TrailingButton(
-                onClick = { checked = !checked },
                 checked = checked,
+                onCheckedChange = { checked = it },
                 modifier =
                     Modifier.semantics {
                         stateDescription = if (checked) "Checked" else "Unchecked"
@@ -98,36 +94,31 @@
 @Sampled
 @Composable
 @Preview
-fun FilledSplitButtonSample() {
-    var checked by remember { mutableStateOf(false) }
-
-    FilledSplitButton(
-        onLeadingButtonClick = {},
-        checked = checked,
-        onTrailingButtonClick = { checked = !checked },
-        leadingContent = {
-            Icon(
-                Icons.Filled.Edit,
-                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
-                contentDescription = "Localized description"
-            )
-            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-            Text("My Button")
-        },
-        trailingContent = {
-            val rotation: Float by
-                animateFloatAsState(
-                    targetValue = if (checked) 180f else 0f,
-                    label = "Trailing Icon Rotation"
+fun SplitButtonWithUnCheckableTrailingButtonSample() {
+    SplitButtonLayout(
+        leadingButton = {
+            SplitButtonDefaults.LeadingButton(
+                onClick = { /* Do Nothing */ },
+            ) {
+                Icon(
+                    Icons.Filled.Edit,
+                    modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                    contentDescription = "Localized description",
                 )
-            Icon(
-                Icons.Filled.KeyboardArrowDown,
-                modifier =
-                    Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
-                        this.rotationZ = rotation
-                    },
-                contentDescription = "Localized description"
-            )
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("My Button")
+            }
+        },
+        trailingButton = {
+            SplitButtonDefaults.TrailingButton(
+                onClick = { /* Do Nothing */ },
+            ) {
+                Icon(
+                    Icons.Filled.KeyboardArrowDown,
+                    modifier = Modifier.size(SplitButtonDefaults.TrailingIconSize),
+                    contentDescription = "Localized description"
+                )
+            }
         }
     )
 }
@@ -139,33 +130,44 @@
 fun TonalSplitButtonSample() {
     var checked by remember { mutableStateOf(false) }
 
-    TonalSplitButton(
-        onLeadingButtonClick = {},
-        checked = checked,
-        onTrailingButtonClick = { checked = !checked },
-        leadingContent = {
-            Icon(
-                Icons.Filled.Edit,
-                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
-                contentDescription = "Localized description"
-            )
-            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-            Text("My Button")
-        },
-        trailingContent = {
-            val rotation: Float by
-                animateFloatAsState(
-                    targetValue = if (checked) 180f else 0f,
-                    label = "Trailing Icon Rotation"
+    SplitButtonLayout(
+        leadingButton = {
+            SplitButtonDefaults.TonalLeadingButton(
+                onClick = { /* Do Nothing */ },
+            ) {
+                Icon(
+                    Icons.Filled.Edit,
+                    modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                    contentDescription = "Localized description",
                 )
-            Icon(
-                Icons.Filled.KeyboardArrowDown,
-                modifier =
-                    Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
-                        this.rotationZ = rotation
-                    },
-                contentDescription = "Localized description"
-            )
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("My Button")
+            }
+        },
+        trailingButton = {
+            SplitButtonDefaults.TonalTrailingButton(
+                checked = checked,
+                onCheckedChange = { checked = it },
+                //                modifier =
+                //                    Modifier.semantics {
+                //                        stateDescription = if (checked) "Checked" else "Unchecked"
+                //                        contentDescription = "Toggle Button"
+                //                    },
+            ) {
+                val rotation: Float by
+                    animateFloatAsState(
+                        targetValue = if (checked) 180f else 0f,
+                        label = "Trailing Icon Rotation"
+                    )
+                Icon(
+                    Icons.Filled.KeyboardArrowDown,
+                    modifier =
+                        Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
+                            this.rotationZ = rotation
+                        },
+                    contentDescription = "Localized description"
+                )
+            }
         }
     )
 }
@@ -177,33 +179,44 @@
 fun ElevatedSplitButtonSample() {
     var checked by remember { mutableStateOf(false) }
 
-    ElevatedSplitButton(
-        onLeadingButtonClick = {},
-        checked = checked,
-        onTrailingButtonClick = { checked = !checked },
-        leadingContent = {
-            Icon(
-                Icons.Filled.Edit,
-                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
-                contentDescription = "Localized description"
-            )
-            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-            Text("My Button")
-        },
-        trailingContent = {
-            val rotation: Float by
-                animateFloatAsState(
-                    targetValue = if (checked) 180f else 0f,
-                    label = "Trailing Icon Rotation"
+    SplitButtonLayout(
+        leadingButton = {
+            SplitButtonDefaults.ElevatedLeadingButton(
+                onClick = { /* Do Nothing */ },
+            ) {
+                Icon(
+                    Icons.Filled.Edit,
+                    modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                    contentDescription = "Localized description",
                 )
-            Icon(
-                Icons.Filled.KeyboardArrowDown,
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("My Button")
+            }
+        },
+        trailingButton = {
+            SplitButtonDefaults.ElevatedTrailingButton(
+                checked = checked,
+                onCheckedChange = { checked = it },
                 modifier =
-                    Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
-                        this.rotationZ = rotation
+                    Modifier.semantics {
+                        stateDescription = if (checked) "Checked" else "Unchecked"
+                        contentDescription = "Toggle Button"
                     },
-                contentDescription = "Localized description"
-            )
+            ) {
+                val rotation: Float by
+                    animateFloatAsState(
+                        targetValue = if (checked) 180f else 0f,
+                        label = "Trailing Icon Rotation"
+                    )
+                Icon(
+                    Icons.Filled.KeyboardArrowDown,
+                    modifier =
+                        Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
+                            this.rotationZ = rotation
+                        },
+                    contentDescription = "Localized description"
+                )
+            }
         }
     )
 }
@@ -215,33 +228,44 @@
 fun OutlinedSplitButtonSample() {
     var checked by remember { mutableStateOf(false) }
 
-    OutlinedSplitButton(
-        onLeadingButtonClick = {},
-        checked = checked,
-        onTrailingButtonClick = { checked = !checked },
-        leadingContent = {
-            Icon(
-                Icons.Filled.Edit,
-                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
-                contentDescription = "Localized description"
-            )
-            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-            Text("My Button")
-        },
-        trailingContent = {
-            val rotation: Float by
-                animateFloatAsState(
-                    targetValue = if (checked) 180f else 0f,
-                    label = "Trailing Icon Rotation"
+    SplitButtonLayout(
+        leadingButton = {
+            SplitButtonDefaults.OutlinedLeadingButton(
+                onClick = { /* Do Nothing */ },
+            ) {
+                Icon(
+                    Icons.Filled.Edit,
+                    modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                    contentDescription = "Localized description",
                 )
-            Icon(
-                Icons.Filled.KeyboardArrowDown,
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("My Button")
+            }
+        },
+        trailingButton = {
+            SplitButtonDefaults.OutlinedTrailingButton(
+                checked = checked,
+                onCheckedChange = { checked = it },
                 modifier =
-                    Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
-                        this.rotationZ = rotation
+                    Modifier.semantics {
+                        stateDescription = if (checked) "Checked" else "Unchecked"
+                        contentDescription = "Toggle Button"
                     },
-                contentDescription = "Localized description"
-            )
+            ) {
+                val rotation: Float by
+                    animateFloatAsState(
+                        targetValue = if (checked) 180f else 0f,
+                        label = "Trailing Icon Rotation"
+                    )
+                Icon(
+                    Icons.Filled.KeyboardArrowDown,
+                    modifier =
+                        Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
+                            this.rotationZ = rotation
+                        },
+                    contentDescription = "Localized description"
+                )
+            }
         }
     )
 }
@@ -253,7 +277,7 @@
 fun SplitButtonWithTextSample() {
     var checked by remember { mutableStateOf(false) }
 
-    SplitButton(
+    SplitButtonLayout(
         leadingButton = {
             SplitButtonDefaults.LeadingButton(
                 onClick = { /* Do Nothing */ },
@@ -263,8 +287,8 @@
         },
         trailingButton = {
             SplitButtonDefaults.TrailingButton(
-                onClick = { checked = !checked },
                 checked = checked,
+                onCheckedChange = { checked = it }
             ) {
                 val rotation: Float by
                     animateFloatAsState(
@@ -291,7 +315,7 @@
 fun SplitButtonWithIconSample() {
     var checked by remember { mutableStateOf(false) }
 
-    SplitButton(
+    SplitButtonLayout(
         leadingButton = {
             SplitButtonDefaults.LeadingButton(
                 onClick = { /* Do Nothing */ },
@@ -305,8 +329,8 @@
         },
         trailingButton = {
             SplitButtonDefaults.TrailingButton(
-                onClick = { checked = !checked },
                 checked = checked,
+                onCheckedChange = { checked = it }
             ) {
                 val rotation: Float by
                     animateFloatAsState(
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
index bfe9fa0..3fb8f8d 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
@@ -19,12 +19,10 @@
 import android.os.Build
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Edit
-import androidx.compose.material.icons.filled.KeyboardArrowDown
 import androidx.compose.material.icons.outlined.KeyboardArrowDown
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Alignment
@@ -58,10 +56,10 @@
     private val trailingButtonTag = "trailingButton"
 
     @Test
-    fun splitButton() {
+    fun filledSplitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                SplitButton(
+                SplitButtonLayout(
                     leadingButton = {
                         SplitButtonDefaults.LeadingButton(
                             onClick = { /* Do Nothing */ },
@@ -77,8 +75,8 @@
                     },
                     trailingButton = {
                         SplitButtonDefaults.TrailingButton(
-                            onClick = {},
                             checked = false,
+                            onCheckedChange = {},
                         ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
@@ -95,67 +93,36 @@
     }
 
     @Test
-    fun filledSplitButton() {
+    fun filledSplitButtonChecked() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                FilledSplitButton(
-                    onLeadingButtonClick = {},
-                    onTrailingButtonClick = {},
-                    checked = false,
-                    leadingContent = {
-                        Icon(
-                            Icons.Filled.Edit,
-                            contentDescription = "Localized description",
-                            Modifier.size(SplitButtonDefaults.LeadingIconSize)
-                        )
-                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                        Text("My Button")
+                SplitButtonLayout(
+                    leadingButton = {
+                        SplitButtonDefaults.LeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            Icon(
+                                Icons.Filled.Edit,
+                                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                                contentDescription = "Localized description",
+                            )
+                            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                            Text("My Button")
+                        }
                     },
-                    trailingContent = {
-                        Box(
-                            modifier = Modifier.fillMaxHeight(),
-                            contentAlignment = Alignment.Center
+                    trailingButton = {
+                        SplitButtonDefaults.TrailingButton(
+                            checked = true,
+                            onCheckedChange = {},
                         ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
                                 contentDescription = "Localized description",
-                                Modifier.size(SplitButtonDefaults.TrailingIconSize)
-                            )
-                        }
-                    }
-                )
-            }
-        }
-
-        assertAgainstGolden("filledSplitButton_${scheme.name}")
-    }
-
-    @Test
-    fun filledSplitButtonChecked() {
-        rule.setMaterialContent(scheme.colorScheme) {
-            Box(wrap.testTag(wrapperTestTag)) {
-                FilledSplitButton(
-                    onLeadingButtonClick = {},
-                    checked = true,
-                    onTrailingButtonClick = {},
-                    leadingContent = {
-                        Icon(
-                            Icons.Filled.Edit,
-                            contentDescription = "Localized description",
-                            Modifier.size(SplitButtonDefaults.LeadingIconSize)
-                        )
-                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                        Text("My Button")
-                    },
-                    trailingContent = {
-                        Icon(
-                            Icons.Filled.KeyboardArrowDown,
-                            modifier =
                                 Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer {
                                     this.rotationZ = 180f
                                 },
-                            contentDescription = "Localized description"
-                        )
+                            )
+                        }
                     }
                 )
             }
@@ -168,21 +135,25 @@
     fun tonalSplitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                TonalSplitButton(
-                    onLeadingButtonClick = {},
-                    onTrailingButtonClick = {},
-                    checked = false,
-                    leadingContent = {
-                        Icon(
-                            Icons.Filled.Edit,
-                            contentDescription = "Localized description",
-                            Modifier.size(SplitButtonDefaults.LeadingIconSize)
-                        )
-                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                        Text("My Button")
+                SplitButtonLayout(
+                    leadingButton = {
+                        SplitButtonDefaults.TonalLeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            Icon(
+                                Icons.Filled.Edit,
+                                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                                contentDescription = "Localized description",
+                            )
+                            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                            Text("My Button")
+                        }
                     },
-                    trailingContent = {
-                        Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
+                    trailingButton = {
+                        SplitButtonDefaults.TonalTrailingButton(
+                            checked = false,
+                            onCheckedChange = {},
+                        ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
                                 contentDescription = "Localized description",
@@ -201,21 +172,25 @@
     fun elevatedSplitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                ElevatedSplitButton(
-                    onLeadingButtonClick = {},
-                    onTrailingButtonClick = {},
-                    checked = false,
-                    leadingContent = {
-                        Icon(
-                            Icons.Filled.Edit,
-                            contentDescription = "Localized description",
-                            Modifier.size(SplitButtonDefaults.LeadingIconSize)
-                        )
-                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                        Text("My Button")
+                SplitButtonLayout(
+                    leadingButton = {
+                        SplitButtonDefaults.ElevatedLeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            Icon(
+                                Icons.Filled.Edit,
+                                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                                contentDescription = "Localized description",
+                            )
+                            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                            Text("My Button")
+                        }
                     },
-                    trailingContent = {
-                        Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
+                    trailingButton = {
+                        SplitButtonDefaults.ElevatedTrailingButton(
+                            checked = false,
+                            onCheckedChange = {},
+                        ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
                                 contentDescription = "Localized description",
@@ -234,21 +209,25 @@
     fun outlinedSplitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                OutlinedSplitButton(
-                    onLeadingButtonClick = {},
-                    onTrailingButtonClick = {},
-                    checked = false,
-                    leadingContent = {
-                        Icon(
-                            Icons.Filled.Edit,
-                            contentDescription = "Localized description",
-                            Modifier.size(SplitButtonDefaults.LeadingIconSize)
-                        )
-                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                        Text("My Button")
+                SplitButtonLayout(
+                    leadingButton = {
+                        SplitButtonDefaults.OutlinedLeadingButton(
+                            onClick = { /* Do Nothing */ },
+                        ) {
+                            Icon(
+                                Icons.Filled.Edit,
+                                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
+                                contentDescription = "Localized description",
+                            )
+                            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                            Text("My Button")
+                        }
                     },
-                    trailingContent = {
-                        Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
+                    trailingButton = {
+                        SplitButtonDefaults.OutlinedTrailingButton(
+                            checked = false,
+                            onCheckedChange = {},
+                        ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
                                 contentDescription = "Localized description",
@@ -267,7 +246,7 @@
     fun splitButton_iconLeadingButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                SplitButton(
+                SplitButtonLayout(
                     leadingButton = {
                         SplitButtonDefaults.LeadingButton(
                             onClick = { /* Do Nothing */ },
@@ -281,8 +260,8 @@
                     },
                     trailingButton = {
                         SplitButtonDefaults.TrailingButton(
-                            onClick = {},
                             checked = false,
+                            onCheckedChange = {},
                         ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
@@ -302,7 +281,7 @@
     fun splitButton_textLeadingButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                SplitButton(
+                SplitButtonLayout(
                     leadingButton = {
                         SplitButtonDefaults.LeadingButton(
                             onClick = { /* Do Nothing */ },
@@ -311,7 +290,7 @@
                         }
                     },
                     trailingButton = {
-                        SplitButtonDefaults.TrailingButton(onClick = {}, checked = false) {
+                        SplitButtonDefaults.TrailingButton(checked = false, onCheckedChange = {}) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
                                 contentDescription = "Localized description",
@@ -330,7 +309,7 @@
     fun splitButton_leadingButton_pressed() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                SplitButton(
+                SplitButtonLayout(
                     leadingButton = {
                         SplitButtonDefaults.LeadingButton(
                             onClick = { /* Do Nothing */ },
@@ -347,8 +326,8 @@
                     },
                     trailingButton = {
                         SplitButtonDefaults.TrailingButton(
-                            onClick = {},
                             checked = false,
+                            onCheckedChange = {},
                         ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
@@ -368,7 +347,7 @@
     fun splitButton_trailingButton_pressed() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
-                SplitButton(
+                SplitButtonLayout(
                     leadingButton = {
                         SplitButtonDefaults.LeadingButton(
                             onClick = { /* Do Nothing */ },
@@ -384,8 +363,8 @@
                     },
                     trailingButton = {
                         SplitButtonDefaults.TrailingButton(
-                            onClick = {},
                             checked = false,
+                            onCheckedChange = {},
                             modifier = Modifier.testTag(trailingButtonTag),
                         ) {
                             Icon(
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt
index bb8c0ba..0a9014c 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt
@@ -35,7 +35,6 @@
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import org.junit.Rule
@@ -45,14 +44,14 @@
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+// TODO: b/370605854 - Add test case for checked trailing button
 class SplitButtonTest {
-
     @get:Rule val rule = createComposeRule()
 
     @Test
-    fun basicSplitButton_contentDisplay() {
+    fun filledSplitButton_contentDisplay() {
         rule.setMaterialContent(lightColorScheme()) {
-            SplitButton(
+            SplitButtonLayout(
                 leadingButton = {
                     SplitButtonDefaults.LeadingButton(
                         onClick = { /* Do Nothing */ },
@@ -70,7 +69,7 @@
                     SplitButtonDefaults.TrailingButton(
                         modifier = Modifier.size(34.dp).testTag("trailingButton"),
                         checked = false,
-                        onClick = {},
+                        onCheckedChange = {},
                     ) {
                         Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
                     }
@@ -86,9 +85,9 @@
     }
 
     @Test
-    fun basicSplitButton_defaultSemantics() {
+    fun filledSplitButton_defaultSemantics() {
         rule.setMaterialContent(lightColorScheme()) {
-            SplitButton(
+            SplitButtonLayout(
                 leadingButton = {
                     SplitButtonDefaults.LeadingButton(
                         onClick = { /* Do Nothing */ },
@@ -99,7 +98,7 @@
                 },
                 trailingButton = {
                     SplitButtonDefaults.TrailingButton(
-                        onClick = {},
+                        onCheckedChange = {},
                         checked = false,
                         modifier = Modifier.size(34.dp).testTag("trailing button"),
                     ) {
@@ -114,15 +113,15 @@
             assertIsEnabled()
         }
         rule.onNodeWithTag("trailing button").apply {
-            assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Checkbox))
             assertIsEnabled()
         }
     }
 
     @Test
-    fun basicSplitButton_disabledSemantics() {
+    fun filledSplitButton_disabledSemantics() {
         rule.setMaterialContent(lightColorScheme()) {
-            SplitButton(
+            SplitButtonLayout(
                 leadingButton = {
                     SplitButtonDefaults.LeadingButton(
                         onClick = { /* Do Nothing */ },
@@ -134,7 +133,7 @@
                 },
                 trailingButton = {
                     SplitButtonDefaults.TrailingButton(
-                        onClick = {},
+                        onCheckedChange = {},
                         checked = false,
                         modifier = Modifier.size(34.dp).testTag("trailing button"),
                         enabled = false,
@@ -150,56 +149,36 @@
             assertIsNotEnabled()
         }
         rule.onNodeWithTag("trailing button").apply {
-            assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Checkbox))
             assertIsNotEnabled()
         }
     }
 
     @Test
-    fun FilledSplitButton_contentDisplay() {
-        rule.setMaterialContent(lightColorScheme()) {
-            FilledSplitButton(
-                onLeadingButtonClick = {},
-                checked = false,
-                onTrailingButtonClick = {},
-                leadingContent = {
-                    Icon(
-                        Icons.Outlined.Edit,
-                        contentDescription = "Leading Icon",
-                        modifier = Modifier.size(48.dp)
-                    )
-                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                    Text("My Button", fontSize = 15.sp)
-                },
-                trailingContent = {
-                    Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
-                }
-            )
-        }
-
-        rule.onNodeWithText("My Button").assertIsDisplayed()
-        rule.onNodeWithContentDescription("Leading Icon").assertIsDisplayed()
-        rule.onNodeWithContentDescription("Trailing Icon").assertIsDisplayed()
-    }
-
-    @Test
     fun TonalSplitButton_contentDisplay() {
         rule.setMaterialContent(lightColorScheme()) {
-            TonalSplitButton(
-                onLeadingButtonClick = {},
-                checked = false,
-                onTrailingButtonClick = {},
-                leadingContent = {
-                    Icon(
-                        Icons.Outlined.Edit,
-                        contentDescription = "Leading Icon",
-                        modifier = Modifier.size(48.dp)
-                    )
-                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                    Text("My Button", fontSize = 15.sp)
+            SplitButtonLayout(
+                leadingButton = {
+                    SplitButtonDefaults.TonalLeadingButton(
+                        onClick = { /* Do Nothing */ },
+                        modifier = Modifier.testTag("leadingButton")
+                    ) {
+                        Icon(
+                            Icons.Outlined.Edit,
+                            contentDescription = "Leading Icon",
+                        )
+                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                        Text("My Button")
+                    }
                 },
-                trailingContent = {
-                    Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
+                trailingButton = {
+                    SplitButtonDefaults.TonalTrailingButton(
+                        modifier = Modifier.size(34.dp).testTag("trailingButton"),
+                        checked = false,
+                        onCheckedChange = {},
+                    ) {
+                        Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
+                    }
                 }
             )
         }
@@ -212,21 +191,28 @@
     @Test
     fun ElevatedSplitButton_contentDisplay() {
         rule.setMaterialContent(lightColorScheme()) {
-            ElevatedSplitButton(
-                onLeadingButtonClick = {},
-                checked = false,
-                onTrailingButtonClick = {},
-                leadingContent = {
-                    Icon(
-                        Icons.Outlined.Edit,
-                        contentDescription = "Leading Icon",
-                        modifier = Modifier.size(48.dp)
-                    )
-                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                    Text("My Button", fontSize = 15.sp)
+            SplitButtonLayout(
+                leadingButton = {
+                    SplitButtonDefaults.ElevatedLeadingButton(
+                        onClick = { /* Do Nothing */ },
+                        modifier = Modifier.testTag("leadingButton")
+                    ) {
+                        Icon(
+                            Icons.Outlined.Edit,
+                            contentDescription = "Leading Icon",
+                        )
+                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                        Text("My Button")
+                    }
                 },
-                trailingContent = {
-                    Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
+                trailingButton = {
+                    SplitButtonDefaults.ElevatedTrailingButton(
+                        modifier = Modifier.size(34.dp).testTag("trailingButton"),
+                        checked = false,
+                        onCheckedChange = {},
+                    ) {
+                        Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
+                    }
                 }
             )
         }
@@ -239,21 +225,28 @@
     @Test
     fun OutlinedSplitButton_contentDisplay() {
         rule.setMaterialContent(lightColorScheme()) {
-            OutlinedSplitButton(
-                onLeadingButtonClick = {},
-                checked = false,
-                onTrailingButtonClick = {},
-                leadingContent = {
-                    Icon(
-                        Icons.Outlined.Edit,
-                        contentDescription = "Leading Icon",
-                        modifier = Modifier.size(48.dp)
-                    )
-                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-                    Text("My Button", fontSize = 15.sp)
+            SplitButtonLayout(
+                leadingButton = {
+                    SplitButtonDefaults.OutlinedLeadingButton(
+                        onClick = { /* Do Nothing */ },
+                        modifier = Modifier.testTag("leadingButton")
+                    ) {
+                        Icon(
+                            Icons.Outlined.Edit,
+                            contentDescription = "Leading Icon",
+                        )
+                        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                        Text("My Button")
+                    }
                 },
-                trailingContent = {
-                    Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
+                trailingButton = {
+                    SplitButtonDefaults.OutlinedTrailingButton(
+                        modifier = Modifier.size(34.dp).testTag("trailingButton"),
+                        checked = false,
+                        onCheckedChange = {},
+                    ) {
+                        Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Trailing Icon")
+                    }
                 }
             )
         }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
index e2e3cae..2bf0c6d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
@@ -63,346 +63,54 @@
 import androidx.compose.ui.util.fastSumBy
 
 /**
- * A [SplitButton] let user define a button group consisting of 2 buttons. The leading button
+ * A [SplitButtonLayout] let user define a button group consisting of 2 buttons. The leading button
  * performs a primary action, and the trailing button performs a secondary action that is
  * contextually related to the primary action.
  *
- * @sample androidx.compose.material3.samples.SplitButtonSample
+ * @sample androidx.compose.material3.samples.FilledSplitButtonSample
+ * @sample androidx.compose.material3.samples.TonalSplitButtonSample
+ * @sample androidx.compose.material3.samples.ElevatedSplitButtonSample
+ * @sample androidx.compose.material3.samples.OutlinedSplitButtonSample
+ * @sample androidx.compose.material3.samples.SplitButtonWithUnCheckableTrailingButtonSample
  * @sample androidx.compose.material3.samples.SplitButtonWithTextSample
  * @sample androidx.compose.material3.samples.SplitButtonWithIconSample
  *
  * Choose the best split button for an action based on the amount of emphasis it needs. The more
  * important an action is, the higher emphasis its button should be.
  *
+ * Use [SplitButtonDefaults.LeadingButton] and [SplitButtonDefaults.TrailingButton] to construct a
+ * `FilledSplitButton`. Filled split button is the high-emphasis version of split button. It should
+ * be used for emphasizing important or final actions.
+ *
+ * Use [SplitButtonDefaults.TonalLeadingButton] and [SplitButtonDefaults.TonalTrailingButton] to
+ * construct a `tonal SplitButton`. Tonal split button is the medium-emphasis version of split
+ * buttons. It's a middle ground between `filled SplitButton` and `outlined SplitButton`
+ *
+ * Use [SplitButtonDefaults.ElevatedLeadingButton] and [SplitButtonDefaults.ElevatedTrailingButton]
+ * to construct a `elevated SplitButton`. Elevated split buttons are essentially `tonal
+ * SplitButton`s with a shadow. To prevent shadow creep, only use them when absolutely necessary,
+ * such as when the button requires visual separation from patterned container.
+ *
+ * Use [SplitButtonDefaults.OutlinedLeadingButton] and [SplitButtonDefaults.OutlinedTrailingButton]
+ * to construct a `outlined SplitButton`. Outlined split buttons are medium-emphasis buttons. They
+ * contain actions that are important, but are not the primary action in an app. Outlined buttons
+ * pair well with `filled SplitButton`s to indicate an alternative, secondary action.
+ *
  * @param leadingButton the leading button. You can specify your own composable or construct a
  *   [SplitButtonDefaults.LeadingButton]
  * @param trailingButton the trailing button.You can specify your own composable or construct a
  *   [SplitButtonDefaults.TrailingButton]
  * @param modifier the [Modifier] to be applied to this split button.
  * @param spacing The spacing between the [leadingButton] and [trailingButton]
- * @see FilledSplitButton for a high-emphasis split button without a shadow.
- * @see OutlinedSplitButton for a medium-emphasis split button with a border.
- * @see TonalSplitButton for a middle ground between [OutlinedSplitButton] and [FilledSplitButton].
- * @see ElevatedSplitButton for an [TonalSplitButton] with a shadow.
  */
 @ExperimentalMaterial3ExpressiveApi
 @Composable
-fun SplitButton(
+fun SplitButtonLayout(
     leadingButton: @Composable () -> Unit,
     trailingButton: @Composable () -> Unit,
     modifier: Modifier = Modifier,
     spacing: Dp = SplitButtonDefaults.Spacing,
 ) {
-    SplitButtonLayout(
-        leadingButton,
-        trailingButton,
-        spacing,
-        modifier.minimumInteractiveComponentSize()
-    )
-}
-
-/**
- * A [SplitButton] let user define a button group consisting of 2 buttons. The leading button
- * performs a primary action, and the trailing button performs a secondary action that is
- * contextually related to the primary action.
- *
- * Filled split button is the high-emphasis version of split button. It should be used for
- * emphasizing important or final actions.
- *
- * @sample androidx.compose.material3.samples.FilledSplitButtonSample
- *
- * Choose the best split button for an action based on the amount of emphasis it needs. The more
- * important an action is, the higher emphasis its button should be.
- *
- * @param onLeadingButtonClick called when the leading button is clicked
- * @param onTrailingButtonClick called when the trailing button is clicked
- * @param leadingContent the content to be placed inside the leading button. A container will be
- *   provided internally to offer the standard design and style for a [FilledSplitButton].
- * @param trailingContent the content to be placed inside the trailing button. A container is
- *   provided internally to ensure the standard design and style of a [FilledSplitButton]. The
- *   container corner radius morphs to `full` when the [checked] state changes to `true`.
- * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
- *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
- * @param modifier the [Modifier] to be applied to this this split button.
- * @param enabled controls the enabled state of the split button. When `false`, this component will
- *   not respond to user input, and it will appear visually disabled and disabled to accessibility
- *   services.
- * @param innerCornerSize The size for leading button's end corners and trailing button's start
- *   corners
- * @param spacing The spacing between the leading and trailing buttons
- * @see OutlinedSplitButton for a medium-emphasis split button with a border.
- * @see TonalSplitButton for a middle ground between [OutlinedSplitButton] and [FilledSplitButton].
- * @see ElevatedSplitButton for an [TonalSplitButton] with a shadow.
- *
- * The default text style for internal [Text] components will be set to [Typography.labelLarge].
- */
-@ExperimentalMaterial3ExpressiveApi
-@Composable
-fun FilledSplitButton(
-    onLeadingButtonClick: () -> Unit,
-    onTrailingButtonClick: () -> Unit,
-    leadingContent: @Composable RowScope.() -> Unit,
-    trailingContent: @Composable RowScope.() -> Unit,
-    checked: Boolean,
-    modifier: Modifier = Modifier,
-    enabled: Boolean = true,
-    innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
-    spacing: Dp = SplitButtonDefaults.Spacing
-) {
-    SplitButton(
-        modifier = modifier,
-        spacing = spacing,
-        leadingButton = {
-            SplitButtonDefaults.LeadingButton(
-                onClick = onLeadingButtonClick,
-                enabled = enabled,
-                shapes = SplitButtonDefaults.leadingButtonShapes(innerCornerSize),
-                content = leadingContent
-            )
-        },
-        trailingButton = {
-            SplitButtonDefaults.TrailingButton(
-                onClick = onTrailingButtonClick,
-                modifier = Modifier,
-                enabled = enabled,
-                checked = checked,
-                shapes =
-                    SplitButtonDefaults.trailingButtonShapes(startCornerSize = innerCornerSize),
-                content = trailingContent,
-            )
-        },
-    )
-}
-
-/**
- * A [SplitButton] let user define a button group consisting of 2 buttons. The leading button
- * performs a primary action, and the trailing button performs a secondary action that is
- * contextually related to the primary action.
- *
- * Tonal split button is the medium-emphasis version of split buttons. It's a middle ground between
- * [FilledSplitButton] and [OutlinedSplitButton]
- *
- * @sample androidx.compose.material3.samples.TonalSplitButtonSample
- *
- * Choose the best split button for an action based on the amount of emphasis it needs. The more
- * important an action is, the higher emphasis its button should be.
- *
- * @param onLeadingButtonClick called when the leading button is clicked
- * @param onTrailingButtonClick called when the trailing button is clicked
- * @param leadingContent the content to be placed inside the leading button. A container will be
- *   provided internally to offer the standard design and style for a [TonalSplitButton].
- * @param trailingContent the content to be placed inside the trailing button. A container is
- *   provided internally to ensure the standard design and style of a [TonalSplitButton]. The
- *   container corner radius morphs to full when the [checked] state changes to `true`.
- * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
- *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
- * @param modifier the [Modifier] to be applied to this split button.
- * @param enabled controls the enabled state of the split button. When `false`, this component will
- *   not respond to user input, and it will appear visually disabled and disabled to accessibility
- *   services.
- * @param innerCornerSize The size for leading button's end corners and trailing button's start
- *   corners
- * @param spacing The spacing between the leading and trailing buttons
- * @see FilledSplitButton for a high-emphasis split button without a shadow.
- * @see OutlinedSplitButton for a medium-emphasis split button with a border.
- * @see ElevatedSplitButton for an [TonalSplitButton] with a shadow.
- *
- * The default text style for internal [Text] components will be set to [Typography.labelLarge].
- */
-@ExperimentalMaterial3ExpressiveApi
-@Composable
-fun TonalSplitButton(
-    onLeadingButtonClick: () -> Unit,
-    onTrailingButtonClick: () -> Unit,
-    leadingContent: @Composable RowScope.() -> Unit,
-    trailingContent: @Composable RowScope.() -> Unit,
-    checked: Boolean,
-    modifier: Modifier = Modifier,
-    enabled: Boolean = true,
-    innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
-    spacing: Dp = SplitButtonDefaults.Spacing
-) {
-    SplitButton(
-        leadingButton = {
-            TonalLeadingButton(
-                onClick = onLeadingButtonClick,
-                modifier = Modifier,
-                enabled = enabled,
-                shapes = SplitButtonDefaults.leadingButtonShapes(endCornerSize = innerCornerSize),
-                content = leadingContent,
-            )
-        },
-        trailingButton = {
-            TonalTrailingButton(
-                onClick = onTrailingButtonClick,
-                modifier = Modifier,
-                enabled = enabled,
-                checked = checked,
-                shapes = SplitButtonDefaults.trailingButtonShapes(innerCornerSize),
-                content = trailingContent,
-            )
-        },
-        modifier = modifier,
-        spacing = spacing
-    )
-}
-
-/**
- * A [SplitButton] let user define a button group consisting of 2 buttons. The leading button
- * performs a primary action, and the trailing button performs a secondary action that is
- * contextually related to the primary action.
- *
- * Elevated split buttons are essentially [OutlinedSplitButton]s with a shadow. To prevent shadow
- * creep, only use them when absolutely necessary, such as when the button requires visual
- * separation from patterned container.
- *
- * @sample androidx.compose.material3.samples.ElevatedSplitButtonSample
- *
- * Choose the best split button for an action based on the amount of emphasis it needs. The more
- * important an action is, the higher emphasis its button should be.
- *
- * @param onLeadingButtonClick called when the leading button is clicked
- * @param onTrailingButtonClick called when the trailing button is clicked
- * @param leadingContent the content to be placed inside the leading button. A container will be
- *   provided internally to offer the standard design and style for a [ElevatedSplitButton].
- * @param trailingContent the content to be placed inside the trailing button. A container is
- *   provided internally to ensure the standard design and style of a [ElevatedSplitButton]. The
- *   container corner radius morphs to full when the [checked] state changes to `true`.
- * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
- *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
- * @param modifier the [Modifier] to be applied to this split button.
- * @param enabled controls the enabled state of the split button. When `false`, this component will
- *   not respond to user input, and it will appear visually disabled and disabled to accessibility
- *   services.
- * @param innerCornerSize The size for leading button's end corners and trailing button's start
- *   corners
- * @param spacing The spacing between the leading and trailing buttons
- * @see FilledSplitButton for a high-emphasis split button without a shadow.
- * @see OutlinedSplitButton for a medium-emphasis split button with a border.
- * @see TonalSplitButton for a middle ground between [OutlinedSplitButton] and [FilledSplitButton].
- *
- * The default text style for internal [Text] components will be set to [Typography.labelLarge].
- */
-@ExperimentalMaterial3ExpressiveApi
-@Composable
-fun ElevatedSplitButton(
-    onLeadingButtonClick: () -> Unit,
-    onTrailingButtonClick: () -> Unit,
-    leadingContent: @Composable RowScope.() -> Unit,
-    trailingContent: @Composable RowScope.() -> Unit,
-    checked: Boolean,
-    modifier: Modifier = Modifier,
-    enabled: Boolean = true,
-    innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
-    spacing: Dp = SplitButtonDefaults.Spacing
-) {
-    SplitButton(
-        leadingButton = {
-            ElevatedLeadingButton(
-                onClick = onLeadingButtonClick,
-                modifier = Modifier,
-                enabled = enabled,
-                shapes = SplitButtonDefaults.leadingButtonShapes(endCornerSize = innerCornerSize),
-                content = leadingContent,
-            )
-        },
-        trailingButton = {
-            ElevatedTrailingButton(
-                onClick = onTrailingButtonClick,
-                modifier = Modifier,
-                enabled = enabled,
-                checked = checked,
-                shapes =
-                    SplitButtonDefaults.trailingButtonShapes(startCornerSize = innerCornerSize),
-                content = trailingContent
-            )
-        },
-        modifier = modifier,
-        spacing = spacing
-    )
-}
-
-/**
- * A [SplitButton] let user define a button group consisting of 2 buttons. The leading button
- * performs a primary action, and the trailing button performs a secondary action that is
- * contextually related to the primary action.
- *
- * Outlined split buttons are medium-emphasis split buttons that are essentially
- * [OutlinedSplitButton]s with a shadow. They contain actions that are important, but aren’t the
- * primary action in an app.
- *
- * @sample androidx.compose.material3.samples.OutlinedSplitButtonSample
- *
- * Choose the best split button for an action based on the amount of emphasis it needs. The more
- * important an action is, the higher emphasis its button should be.
- *
- * @param onLeadingButtonClick called when the leading button is clicked
- * @param onTrailingButtonClick called when the trailing button is clicked
- * @param leadingContent the content to be placed inside the leading button. A container will be
- *   provided internally to offer the standard design and style for a [OutlinedSplitButton].
- * @param trailingContent the content to be placed inside the trailing button. A container is
- *   provided internally to ensure the standard design and style of a [OutlinedSplitButton]. The
- *   container corner radius morphs to full when the [checked] state changes to `true`.
- * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
- *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
- * @param modifier the [Modifier] to be applied to this split button.
- * @param enabled controls the enabled state of the split button. When `false`, this component will
- *   not respond to user input, and it will appear visually disabled and disabled to accessibility
- *   services.
- * @param innerCornerSize The size for leading button's end corners and trailing button's start
- *   corners
- * @param spacing The spacing between the leading and trailing buttons
- * @see FilledSplitButton for a high-emphasis split button without a shadow.
- * @see TonalSplitButton for a middle ground between [OutlinedSplitButton] and [FilledSplitButton].
- * @see ElevatedSplitButton for an [TonalSplitButton] with a shadow.
- *
- * The default text style for internal [Text] components will be set to [Typography.labelLarge].
- */
-@ExperimentalMaterial3ExpressiveApi
-@Composable
-fun OutlinedSplitButton(
-    onLeadingButtonClick: () -> Unit,
-    onTrailingButtonClick: () -> Unit,
-    leadingContent: @Composable RowScope.() -> Unit,
-    trailingContent: @Composable RowScope.() -> Unit,
-    checked: Boolean,
-    modifier: Modifier = Modifier,
-    enabled: Boolean = true,
-    innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
-    spacing: Dp = SplitButtonDefaults.Spacing
-) {
-    SplitButton(
-        leadingButton = {
-            OutlinedLeadingButton(
-                onClick = onLeadingButtonClick,
-                modifier = Modifier,
-                enabled = enabled,
-                shapes = SplitButtonDefaults.leadingButtonShapes(innerCornerSize),
-                content = leadingContent,
-            )
-        },
-        trailingButton = {
-            OutlinedTrailingButton(
-                onClick = onTrailingButtonClick,
-                modifier = Modifier,
-                enabled = enabled,
-                shapes = SplitButtonDefaults.trailingButtonShapes(innerCornerSize),
-                checked = checked,
-                content = trailingContent
-            )
-        },
-        modifier = modifier,
-        spacing = spacing
-    )
-}
-
-@Composable
-private fun SplitButtonLayout(
-    leadingButton: @Composable () -> Unit,
-    trailingButton: @Composable () -> Unit,
-    spacing: Dp,
-    modifier: Modifier = Modifier,
-) {
     Layout(
         {
             // Override min component size enforcement to avoid create extra padding internally
@@ -420,7 +128,7 @@
                 )
             }
         },
-        modifier,
+        modifier.minimumInteractiveComponentSize(),
         measurePolicy = { measurables, constraints ->
             val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
 
@@ -462,7 +170,7 @@
     )
 }
 
-/** Contains default values used by [SplitButton] and its style variants. */
+/** Contains default values used by [SplitButtonLayout] and its style variants. */
 @ExperimentalMaterial3ExpressiveApi
 object SplitButtonDefaults {
     /** Default icon size for the leading button */
@@ -641,9 +349,7 @@
     }
 
     /**
-     * Creates a `trailing` button that has the same visual as a Filled[Button]. When [checked] is
-     * updated from `false` to `true`, the buttons corners will morph to `full` by default. Pressed
-     * shape and checked shape can be customized via [shapes] param.
+     * Creates a `trailing` button that has the same visual as a [Button].
      *
      * To create a `tonal`, `outlined`, or `elevated` version, the default value of [Button] params
      * can be passed in. For example, [ElevatedButton].
@@ -651,8 +357,6 @@
      * The default text style for internal [Text] components will be set to [Typography.labelLarge].
      *
      * @param onClick called when the button is clicked
-     * @param checked indicates whether the button is toggled to a `checked` state. This will
-     *   trigger the corner morphing animation to reflect the updated state.
      * @param modifier the [Modifier] to be applied to this button.
      * @param enabled controls the enabled state of the split button. When `false`, this component
      *   will not respond to user input, and it will appear visually disabled and disabled to
@@ -678,7 +382,6 @@
     @ExperimentalMaterial3ExpressiveApi
     fun TrailingButton(
         onClick: () -> Unit,
-        checked: Boolean,
         modifier: Modifier = Modifier,
         enabled: Boolean = true,
         shapes: SplitButtonShapes = trailingButtonShapes(),
@@ -696,24 +399,11 @@
         val defaultAnimationSpec = MotionSchemeKeyTokens.DefaultEffects.value<Float>()
         val pressed by interactionSource.collectIsPressedAsState()
 
-        val density = LocalDensity.current
-        val shape = shapeByInteraction(shapes, pressed, checked, defaultAnimationSpec)
+        val shape = shapeByInteraction(shapes, pressed, false, defaultAnimationSpec)
 
         Surface(
             onClick = onClick,
-            modifier =
-                modifier
-                    .drawWithContent {
-                        drawContent()
-                        if (checked) {
-                            drawOutline(
-                                outline = shape.createOutline(size, layoutDirection, density),
-                                color = colors.contentColor,
-                                alpha = TrailingButtonStateLayerAlpha
-                            )
-                        }
-                    }
-                    .semantics { role = Role.Button },
+            modifier = modifier.semantics { role = Role.Button },
             enabled = enabled,
             shape = shape,
             color = colors.containerColor,
@@ -757,138 +447,488 @@
             }
         }
     }
-}
 
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-private fun TonalLeadingButton(
-    onClick: () -> Unit,
-    modifier: Modifier,
-    enabled: Boolean,
-    shapes: SplitButtonShapes,
-    content: @Composable RowScope.() -> Unit
-) {
-    SplitButtonDefaults.LeadingButton(
-        modifier = modifier,
-        onClick = onClick,
-        enabled = enabled,
-        shapes = shapes,
-        colors = ButtonDefaults.filledTonalButtonColors(),
-        elevation = ButtonDefaults.filledTonalButtonElevation(),
-        border = null,
-        content = content,
-    )
-}
+    /**
+     * Creates a `trailing` button that has the same visual as a [Button]. When [checked] is updated
+     * from `false` to `true`, the buttons corners will morph to `full` by default. Pressed shape
+     * and checked shape can be customized via [shapes] param.
+     *
+     * To create a `tonal`, `outlined`, or `elevated` version, the default value of [Button] params
+     * can be passed in. For example, [ElevatedButton].
+     *
+     * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+     *
+     * @param checked indicates whether the button is toggled to a `checked` state. This will
+     *   trigger the corner morphing animation to reflect the updated state.
+     * @param onCheckedChange called when the button is clicked
+     * @param modifier the [Modifier] to be applied to this button.
+     * @param enabled controls the enabled state of the split button. When `false`, this component
+     *   will not respond to user input, and it will appear visually disabled and disabled to
+     *   accessibility services.
+     * @param shapes the [SplitButtonShapes] that the trailing button will morph between depending
+     *   on the user's interaction with the button.
+     * @param colors [ButtonColors] that will be used to resolve the colors for this button in
+     *   different states. See [ButtonDefaults.buttonColors].
+     * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
+     *   states. This controls the size of the shadow below the button. See
+     *   [ButtonElevation.shadowElevation].
+     * @param border the border to draw around the container of this button contentPadding the
+     *   spacing values to apply internally between the container and the content
+     * @param contentPadding the spacing values to apply internally between the container and the
+     *   content
+     * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+     *   emitting [Interaction]s for this button. You can use this to change the button's appearance
+     *   or preview the button in different states. Note that if `null` is provided, interactions
+     *   will still happen internally.
+     * @param content the content to be placed in the button
+     */
+    @Composable
+    @ExperimentalMaterial3ExpressiveApi
+    fun TrailingButton(
+        checked: Boolean,
+        onCheckedChange: (Boolean) -> Unit,
+        modifier: Modifier = Modifier,
+        enabled: Boolean = true,
+        shapes: SplitButtonShapes = trailingButtonShapes(),
+        colors: ButtonColors = ButtonDefaults.buttonColors(),
+        elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
+        border: BorderStroke? = null,
+        contentPadding: PaddingValues = TrailingButtonContentPadding,
+        interactionSource: MutableInteractionSource? = null,
+        content: @Composable RowScope.() -> Unit
+    ) {
+        @Suppress("NAME_SHADOWING")
+        val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
 
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-private fun TonalTrailingButton(
-    onClick: () -> Unit,
-    checked: Boolean,
-    modifier: Modifier,
-    enabled: Boolean,
-    shapes: SplitButtonShapes,
-    content: @Composable RowScope.() -> Unit
-) {
-    SplitButtonDefaults.TrailingButton(
-        modifier = modifier,
-        onClick = onClick,
-        enabled = enabled,
-        shapes = shapes,
-        checked = checked,
-        colors = ButtonDefaults.filledTonalButtonColors(),
-        elevation = ButtonDefaults.filledTonalButtonElevation(),
-        border = null,
-        content = content
-    )
-}
+        // TODO Load the motionScheme tokens from the component tokens file
+        val defaultAnimationSpec = MotionSchemeKeyTokens.DefaultEffects.value<Float>()
+        val pressed by interactionSource.collectIsPressedAsState()
 
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-private fun OutlinedLeadingButton(
-    onClick: () -> Unit,
-    modifier: Modifier,
-    enabled: Boolean,
-    shapes: SplitButtonShapes,
-    content: @Composable RowScope.() -> Unit
-) {
-    SplitButtonDefaults.LeadingButton(
-        modifier = modifier,
-        onClick = onClick,
-        enabled = enabled,
-        shapes = shapes,
-        colors = ButtonDefaults.outlinedButtonColors(),
-        elevation = null,
-        border = ButtonDefaults.outlinedButtonBorder(enabled),
-        content = content
-    )
-}
+        val density = LocalDensity.current
+        val shape = shapeByInteraction(shapes, pressed, checked, defaultAnimationSpec)
 
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-private fun OutlinedTrailingButton(
-    onClick: () -> Unit,
-    checked: Boolean,
-    modifier: Modifier,
-    enabled: Boolean,
-    shapes: SplitButtonShapes,
-    content: @Composable RowScope.() -> Unit
-) {
-    SplitButtonDefaults.TrailingButton(
-        modifier = modifier,
-        onClick = onClick,
-        enabled = enabled,
-        shapes = shapes,
-        checked = checked,
-        colors = ButtonDefaults.outlinedButtonColors(),
-        elevation = null,
-        border = ButtonDefaults.outlinedButtonBorder(enabled),
-        content = content
-    )
-}
+        Surface(
+            checked = checked,
+            onCheckedChange = onCheckedChange,
+            modifier =
+                modifier
+                    .drawWithContent {
+                        drawContent()
+                        if (checked) {
+                            drawOutline(
+                                outline = shape.createOutline(size, layoutDirection, density),
+                                color = colors.contentColor,
+                                alpha = TrailingButtonStateLayerAlpha
+                            )
+                        }
+                    }
+                    .semantics { role = Role.Checkbox },
+            enabled = enabled,
+            shape = shape,
+            color = colors.containerColor,
+            contentColor = colors.contentColor,
+            shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+            border = border,
+            interactionSource = interactionSource
+        ) {
+            ProvideContentColorTextStyle(
+                contentColor = colors.contentColor,
+                textStyle = MaterialTheme.typography.labelLarge
+            ) {
+                Row(
+                    Modifier.defaultMinSize(
+                            minWidth = TrailingButtonMinWidth,
+                            minHeight = MinHeight
+                        )
+                        .then(
+                            when (shape) {
+                                is ShapeWithOpticalCentering -> {
+                                    Modifier.opticalCentering(
+                                        shape = shape,
+                                        basePadding = contentPadding
+                                    )
+                                }
+                                is CornerBasedShape -> {
+                                    Modifier.opticalCentering(
+                                        shape = shape,
+                                        basePadding = contentPadding
+                                    )
+                                }
+                                else -> {
+                                    Modifier.padding(contentPadding)
+                                }
+                            }
+                        ),
+                    horizontalArrangement = Arrangement.Center,
+                    verticalAlignment = Alignment.CenterVertically,
+                    content = content
+                )
+            }
+        }
+    }
 
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-private fun ElevatedLeadingButton(
-    onClick: () -> Unit,
-    modifier: Modifier,
-    enabled: Boolean,
-    shapes: SplitButtonShapes,
-    content: @Composable RowScope.() -> Unit
-) {
-    SplitButtonDefaults.LeadingButton(
-        modifier = modifier,
-        onClick = onClick,
-        enabled = enabled,
-        shapes = shapes,
-        colors = ButtonDefaults.elevatedButtonColors(),
-        elevation = ButtonDefaults.elevatedButtonElevation(),
-        border = null,
-        content = content
-    )
-}
+    /**
+     * Create a tonal `leading` button that has the same visual as a Tonal[Button]. To create a
+     * `filled`, `outlined`, or `elevated` version, the default value of [Button] params can be
+     * passed in. For example, [ElevatedButton].
+     *
+     * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+     *
+     * @param onClick called when the button is clicked
+     * @param modifier the [Modifier] to be applied to this button.
+     * @param enabled controls the enabled state of the split button. When `false`, this component
+     *   will not respond to user input, and it will appear visually disabled and disabled to
+     *   accessibility services.
+     * @param shapes the [SplitButtonShapes] that the trailing button will morph between depending
+     *   on the user's interaction with the button.
+     * @param colors [ButtonColors] that will be used to resolve the colors for this button in
+     *   different states. See [ButtonDefaults.buttonColors].
+     * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
+     *   states. This controls the size of the shadow below the button. See
+     *   [ButtonElevation.shadowElevation].
+     * @param border the border to draw around the container of this button contentPadding the
+     *   spacing values to apply internally between the container and the content
+     * @param contentPadding the spacing values to apply internally between the container and the
+     *   content
+     * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+     *   emitting [Interaction]s for this button. You can use this to change the button's appearance
+     *   or preview the button in different states. Note that if `null` is provided, interactions
+     *   will still happen internally.
+     * @param content the content for the button.
+     */
+    @ExperimentalMaterial3ExpressiveApi
+    @Composable
+    fun TonalLeadingButton(
+        onClick: () -> Unit,
+        modifier: Modifier = Modifier,
+        enabled: Boolean = true,
+        shapes: SplitButtonShapes = leadingButtonShapes(),
+        colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
+        elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
+        border: BorderStroke? = null,
+        contentPadding: PaddingValues = LeadingButtonContentPadding,
+        interactionSource: MutableInteractionSource? = null,
+        content: @Composable RowScope.() -> Unit
+    ) {
+        LeadingButton(
+            modifier = modifier,
+            onClick = onClick,
+            enabled = enabled,
+            shapes = shapes,
+            colors = colors,
+            elevation = elevation,
+            border = border,
+            contentPadding = contentPadding,
+            interactionSource = interactionSource,
+            content = content,
+        )
+    }
 
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-private fun ElevatedTrailingButton(
-    onClick: () -> Unit,
-    checked: Boolean,
-    modifier: Modifier,
-    enabled: Boolean,
-    shapes: SplitButtonShapes,
-    content: @Composable RowScope.() -> Unit
-) {
-    SplitButtonDefaults.TrailingButton(
-        modifier = modifier,
-        onClick = onClick,
-        enabled = enabled,
-        shapes = shapes,
-        checked = checked,
-        colors = ButtonDefaults.elevatedButtonColors(),
-        elevation = ButtonDefaults.elevatedButtonElevation(),
-        border = null,
-        content = content
-    )
+    /**
+     * Creates a tonal `trailing` button that has the same visual as a [FilledTonalButton]. When
+     * [checked] is updated from `false` to `true`, the buttons corners will morph to `full` by
+     * default. Pressed shape and checked shape can be customized via [shapes] param.
+     *
+     * To create a `tonal`, `outlined`, or `elevated` version, the default value of [Button] params
+     * can be passed in. For example, [ElevatedButton].
+     *
+     * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+     *
+     * @param checked indicates whether the button is toggled to a `checked` state. This will
+     *   trigger the corner morphing animation to reflect the updated state.
+     * @param onCheckedChange called when the button is clicked
+     * @param modifier the [Modifier] to be applied to this button.
+     * @param enabled controls the enabled state of the split button. When `false`, this component
+     *   will not respond to user input, and it will appear visually disabled and disabled to
+     *   accessibility services.
+     * @param shapes the [SplitButtonShapes] that the trailing button will morph between depending
+     *   on the user's interaction with the button.
+     * @param colors [ButtonColors] that will be used to resolve the colors for this button in
+     *   different states. See [ButtonDefaults.buttonColors].
+     * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
+     *   states. This controls the size of the shadow below the button. See
+     *   [ButtonElevation.shadowElevation].
+     * @param border the border to draw around the container of this button contentPadding the
+     *   spacing values to apply internally between the container and the content
+     * @param contentPadding the spacing values to apply internally between the container and the
+     *   content
+     * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+     *   emitting [Interaction]s for this button. You can use this to change the button's appearance
+     *   or preview the button in different states. Note that if `null` is provided, interactions
+     *   will still happen internally.
+     * @param content the content to be placed in the button
+     */
+    @ExperimentalMaterial3ExpressiveApi
+    @Composable
+    fun TonalTrailingButton(
+        checked: Boolean,
+        onCheckedChange: (Boolean) -> Unit,
+        modifier: Modifier = Modifier,
+        enabled: Boolean = true,
+        shapes: SplitButtonShapes = trailingButtonShapes(),
+        colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
+        elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
+        border: BorderStroke? = null,
+        contentPadding: PaddingValues = TrailingButtonContentPadding,
+        interactionSource: MutableInteractionSource? = null,
+        content: @Composable RowScope.() -> Unit
+    ) {
+        TrailingButton(
+            checked = checked,
+            onCheckedChange = onCheckedChange,
+            modifier = modifier,
+            enabled = enabled,
+            shapes = shapes,
+            colors = colors,
+            elevation = elevation,
+            border = border,
+            contentPadding = contentPadding,
+            interactionSource = interactionSource,
+            content = content
+        )
+    }
+
+    /**
+     * Create a elevated `leading` button that has the same visual as a [ElevatedButton]. To create
+     * a `filled`, `outlined`, or `elevated` version, the default value of [Button] params can be
+     * passed in. For example, [ElevatedButton].
+     *
+     * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+     *
+     * @param onClick called when the button is clicked
+     * @param modifier the [Modifier] to be applied to this button.
+     * @param enabled controls the enabled state of the split button. When `false`, this component
+     *   will not respond to user input, and it will appear visually disabled and disabled to
+     *   accessibility services.
+     * @param shapes the [SplitButtonShapes] that the trailing button will morph between depending
+     *   on the user's interaction with the button.
+     * @param colors [ButtonColors] that will be used to resolve the colors for this button in
+     *   different states. See [ButtonDefaults.buttonColors].
+     * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
+     *   states. This controls the size of the shadow below the button. See
+     *   [ButtonElevation.shadowElevation].
+     * @param border the border to draw around the container of this button contentPadding the
+     *   spacing values to apply internally between the container and the content
+     * @param contentPadding the spacing values to apply internally between the container and the
+     *   content
+     * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+     *   emitting [Interaction]s for this button. You can use this to change the button's appearance
+     *   or preview the button in different states. Note that if `null` is provided, interactions
+     *   will still happen internally.
+     * @param content the content for the button.
+     */
+    @ExperimentalMaterial3ExpressiveApi
+    @Composable
+    fun OutlinedLeadingButton(
+        onClick: () -> Unit,
+        modifier: Modifier = Modifier,
+        enabled: Boolean = true,
+        shapes: SplitButtonShapes = leadingButtonShapes(),
+        colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
+        elevation: ButtonElevation? = null,
+        border: BorderStroke? = ButtonDefaults.outlinedButtonBorder(enabled),
+        contentPadding: PaddingValues = LeadingButtonContentPadding,
+        interactionSource: MutableInteractionSource? = null,
+        content: @Composable RowScope.() -> Unit
+    ) {
+        LeadingButton(
+            modifier = modifier,
+            onClick = onClick,
+            enabled = enabled,
+            shapes = shapes,
+            colors = colors,
+            elevation = elevation,
+            border = border,
+            contentPadding = contentPadding,
+            interactionSource = interactionSource,
+            content = content,
+        )
+    }
+
+    /**
+     * Creates a outlined `trailing` button that has the same visual as a [OutlinedButton]. When
+     * [checked] is updated from `false` to `true`, the buttons corners will morph to `full` by
+     * default. Pressed shape and checked shape can be customized via [shapes] param.
+     *
+     * To create a `tonal`, `outlined`, or `elevated` version, the default value of [Button] params
+     * can be passed in. For example, [ElevatedButton].
+     *
+     * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+     *
+     * @param checked indicates whether the button is toggled to a `checked` state. This will
+     *   trigger the corner morphing animation to reflect the updated state.
+     * @param onCheckedChange called when the button is clicked
+     * @param modifier the [Modifier] to be applied to this button.
+     * @param enabled controls the enabled state of the split button. When `false`, this component
+     *   will not respond to user input, and it will appear visually disabled and disabled to
+     *   accessibility services.
+     * @param shapes the [SplitButtonShapes] that the trailing button will morph between depending
+     *   on the user's interaction with the button.
+     * @param colors [ButtonColors] that will be used to resolve the colors for this button in
+     *   different states. See [ButtonDefaults.buttonColors].
+     * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
+     *   states. This controls the size of the shadow below the button. See
+     *   [ButtonElevation.shadowElevation].
+     * @param border the border to draw around the container of this button contentPadding the
+     *   spacing values to apply internally between the container and the content
+     * @param contentPadding the spacing values to apply internally between the container and the
+     *   content
+     * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+     *   emitting [Interaction]s for this button. You can use this to change the button's appearance
+     *   or preview the button in different states. Note that if `null` is provided, interactions
+     *   will still happen internally.
+     * @param content the content to be placed in the button
+     */
+    @ExperimentalMaterial3ExpressiveApi
+    @Composable
+    fun OutlinedTrailingButton(
+        checked: Boolean,
+        onCheckedChange: (Boolean) -> Unit,
+        modifier: Modifier = Modifier,
+        enabled: Boolean = true,
+        shapes: SplitButtonShapes = trailingButtonShapes(),
+        colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
+        elevation: ButtonElevation? = null,
+        border: BorderStroke? = ButtonDefaults.outlinedButtonBorder(enabled),
+        contentPadding: PaddingValues = TrailingButtonContentPadding,
+        interactionSource: MutableInteractionSource? = null,
+        content: @Composable RowScope.() -> Unit
+    ) {
+        TrailingButton(
+            checked = checked,
+            onCheckedChange = onCheckedChange,
+            modifier = modifier,
+            enabled = enabled,
+            shapes = shapes,
+            colors = colors,
+            elevation = elevation,
+            border = border,
+            contentPadding = contentPadding,
+            interactionSource = interactionSource,
+            content = content
+        )
+    }
+
+    /**
+     * Create a elevated `leading` button that has the same visual as a [ElevatedButton]. To create
+     * a `filled`, `outlined`, or `elevated` version, the default value of [Button] params can be
+     * passed in. For example, [ElevatedButton].
+     *
+     * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+     *
+     * @param onClick called when the button is clicked
+     * @param modifier the [Modifier] to be applied to this button.
+     * @param enabled controls the enabled state of the split button. When `false`, this component
+     *   will not respond to user input, and it will appear visually disabled and disabled to
+     *   accessibility services.
+     * @param shapes the [SplitButtonShapes] that the trailing button will morph between depending
+     *   on the user's interaction with the button.
+     * @param colors [ButtonColors] that will be used to resolve the colors for this button in
+     *   different states. See [ButtonDefaults.buttonColors].
+     * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
+     *   states. This controls the size of the shadow below the button. See
+     *   [ButtonElevation.shadowElevation].
+     * @param border the border to draw around the container of this button contentPadding the
+     *   spacing values to apply internally between the container and the content
+     * @param contentPadding the spacing values to apply internally between the container and the
+     *   content
+     * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+     *   emitting [Interaction]s for this button. You can use this to change the button's appearance
+     *   or preview the button in different states. Note that if `null` is provided, interactions
+     *   will still happen internally.
+     * @param content the content for the button.
+     */
+    @ExperimentalMaterial3ExpressiveApi
+    @Composable
+    fun ElevatedLeadingButton(
+        onClick: () -> Unit,
+        modifier: Modifier = Modifier,
+        enabled: Boolean = true,
+        shapes: SplitButtonShapes = leadingButtonShapes(),
+        colors: ButtonColors = ButtonDefaults.elevatedButtonColors(),
+        elevation: ButtonElevation? = ButtonDefaults.elevatedButtonElevation(),
+        border: BorderStroke? = null,
+        contentPadding: PaddingValues = LeadingButtonContentPadding,
+        interactionSource: MutableInteractionSource? = null,
+        content: @Composable RowScope.() -> Unit
+    ) {
+        LeadingButton(
+            modifier = modifier,
+            onClick = onClick,
+            enabled = enabled,
+            shapes = shapes,
+            colors = colors,
+            elevation = elevation,
+            border = border,
+            contentPadding = contentPadding,
+            interactionSource = interactionSource,
+            content = content,
+        )
+    }
+
+    /**
+     * Creates a elevated `trailing` button that has the same visual as a [ElevatedButton]. When
+     * [checked] is updated from `false` to `true`, the buttons corners will morph to `full` by
+     * default. Pressed shape and checked shape can be customized via [shapes] param.
+     *
+     * To create a `tonal`, `outlined`, or `elevated` version, the default value of [Button] params
+     * can be passed in. For example, [ElevatedButton].
+     *
+     * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+     *
+     * @param checked indicates whether the button is toggled to a `checked` state. This will
+     *   trigger the corner morphing animation to reflect the updated state.
+     * @param onCheckedChange called when the button is clicked
+     * @param modifier the [Modifier] to be applied to this button.
+     * @param enabled controls the enabled state of the split button. When `false`, this component
+     *   will not respond to user input, and it will appear visually disabled and disabled to
+     *   accessibility services.
+     * @param shapes the [SplitButtonShapes] that the trailing button will morph between depending
+     *   on the user's interaction with the button.
+     * @param colors [ButtonColors] that will be used to resolve the colors for this button in
+     *   different states. See [ButtonDefaults.buttonColors].
+     * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
+     *   states. This controls the size of the shadow below the button. See
+     *   [ButtonElevation.shadowElevation].
+     * @param border the border to draw around the container of this button contentPadding the
+     *   spacing values to apply internally between the container and the content
+     * @param contentPadding the spacing values to apply internally between the container and the
+     *   content
+     * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+     *   emitting [Interaction]s for this button. You can use this to change the button's appearance
+     *   or preview the button in different states. Note that if `null` is provided, interactions
+     *   will still happen internally.
+     * @param content the content to be placed in the button
+     */
+    @ExperimentalMaterial3ExpressiveApi
+    @Composable
+    fun ElevatedTrailingButton(
+        checked: Boolean,
+        onCheckedChange: (Boolean) -> Unit,
+        modifier: Modifier = Modifier,
+        enabled: Boolean = true,
+        shapes: SplitButtonShapes = trailingButtonShapes(),
+        colors: ButtonColors = ButtonDefaults.elevatedButtonColors(),
+        elevation: ButtonElevation? = ButtonDefaults.elevatedButtonElevation(),
+        border: BorderStroke? = null,
+        contentPadding: PaddingValues = TrailingButtonContentPadding,
+        interactionSource: MutableInteractionSource? = null,
+        content: @Composable RowScope.() -> Unit
+    ) {
+        TrailingButton(
+            checked = checked,
+            onCheckedChange = onCheckedChange,
+            modifier = modifier,
+            enabled = enabled,
+            shapes = shapes,
+            colors = colors,
+            elevation = elevation,
+            border = border,
+            contentPadding = contentPadding,
+            interactionSource = interactionSource,
+            content = content
+        )
+    }
 }
 
 @Composable
@@ -912,7 +952,7 @@
 }
 
 /**
- * The shapes that will be used in [SplitButton]. Split button will morph between these shapes
+ * The shapes that will be used in [SplitButtonLayout]. Split button will morph between these shapes
  * depending on the interaction of the buttons, assuming all of the shapes are [CornerBasedShape]s.
  *
  * @property shape is the default shape.
diff --git a/compose/runtime/runtime/proguard-rules.pro b/compose/runtime/runtime/proguard-rules.pro
index f32c782..a0d0c23 100644
--- a/compose/runtime/runtime/proguard-rules.pro
+++ b/compose/runtime/runtime/proguard-rules.pro
@@ -20,7 +20,9 @@
 -keep,allowshrinking,allowobfuscation class androidx.compose.runtime.** {
     # java.lang.Void == methods that return Nothing
     static void throw*Exception(...);
+    static void throw*ExceptionForNullCheck(...);
     static java.lang.Void throw*Exception(...);
+    static java.lang.Void throw*ExceptionForNullCheck(...);
 
     static void compose*RuntimeError(...);
     static java.lang.Void compose*RuntimeError(...);
diff --git a/compose/ui/ui-graphics/build.gradle b/compose/ui/ui-graphics/build.gradle
index 75bba20..842022a 100644
--- a/compose/ui/ui-graphics/build.gradle
+++ b/compose/ui/ui-graphics/build.gradle
@@ -129,4 +129,7 @@
 android {
     compileSdk 35
     namespace "androidx.compose.ui.graphics"
+    buildTypes.configureEach {
+        consumerProguardFiles("proguard-rules.pro")
+    }
 }
diff --git a/compose/ui/ui-graphics/proguard-rules.pro b/compose/ui/ui-graphics/proguard-rules.pro
new file mode 100644
index 0000000..67d118b
--- /dev/null
+++ b/compose/ui/ui-graphics/proguard-rules.pro
@@ -0,0 +1,24 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Keep all the functions created to throw an exception. We don't want these functions to be
+# inlined in any way, which R8 will do by default. The whole point of these functions is to
+# reduce the amount of code generated at the call site.
+-keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+    static void throw*Exception(...);
+    static void throw*ExceptionForNullCheck(...);
+    # For methods returning Nothing
+    static java.lang.Void throw*Exception(...);
+    static java.lang.Void throw*ExceptionForNullCheck(...);
+}
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
index 15437fd..fe448fb 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
@@ -40,7 +40,7 @@
 // and because we use a fast approximation of cbrt(). The epsilon we use here is
 // slightly larger than the max error of fastCbrt() in the -1f..1f range
 // (8.3446500e-7f) but smaller than 1.0f.ulp * 10.
-private const val FloatEpsilon = 1e-6f
+private const val FloatEpsilon = 1.05e-6f
 
 /**
  * Evaluate the specified [segment] at position [t] and returns the X coordinate of the segment's
diff --git a/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/DeviceConfigurationOverrideSamples.kt b/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/DeviceConfigurationOverrideSamples.kt
index 084fbb4..d345fcb 100644
--- a/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/DeviceConfigurationOverrideSamples.kt
+++ b/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/DeviceConfigurationOverrideSamples.kt
@@ -20,13 +20,13 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.roundToAndroidXInsets
 import androidx.compose.foundation.layout.safeDrawingPadding
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.test.DarkMode
 import androidx.compose.ui.test.DeviceConfigurationOverride
 import androidx.compose.ui.test.FontScale
@@ -40,8 +40,10 @@
 import androidx.compose.ui.text.intl.LocaleList
 import androidx.compose.ui.unit.DpRect
 import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.roundToIntRect
 import androidx.core.view.WindowInsetsCompat
 
 @Sampled
@@ -115,16 +117,22 @@
 @Sampled
 @Composable
 fun DeviceConfigurationOverrideWindowInsetsSample() {
+    fun IntRect.toAndroidXInsets() = androidx.core.graphics.Insets.of(left, top, right, bottom)
+
     DeviceConfigurationOverride(
         DeviceConfigurationOverride.WindowInsets(
             WindowInsetsCompat.Builder()
                 .setInsets(
                     WindowInsetsCompat.Type.captionBar(),
-                    DpRect(0.dp, 64.dp, 0.dp, 0.dp).roundToAndroidXInsets(),
+                    with(LocalDensity.current) { DpRect(0.dp, 64.dp, 0.dp, 0.dp).toRect() }
+                        .roundToIntRect()
+                        .toAndroidXInsets()
                 )
                 .setInsets(
                     WindowInsetsCompat.Type.navigationBars(),
-                    DpRect(24.dp, 0.dp, 48.dp, 24.dp).roundToAndroidXInsets(),
+                    with(LocalDensity.current) { DpRect(24.dp, 0.dp, 48.dp, 24.dp).toRect() }
+                        .roundToIntRect()
+                        .toAndroidXInsets()
                 )
                 .build()
         )
diff --git a/compose/ui/ui-tooling-data/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/data/OffsetInformationTest.kt b/compose/ui/ui-tooling-data/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/data/OffsetInformationTest.kt
index 3350a5f1..ebb0263 100644
--- a/compose/ui/ui-tooling-data/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/data/OffsetInformationTest.kt
+++ b/compose/ui/ui-tooling-data/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/data/OffsetInformationTest.kt
@@ -50,11 +50,9 @@
                 "Text" to 1680,
                 "Greeting" to 1845,
                 "Text" to 2509,
-                "<get-shapes>" to 1927,
                 "Surface" to 1877,
                 "Button" to 1959,
                 "Text" to 1982,
-                "<get-shapes>" to 2070,
                 "Surface" to 2021,
                 "TextButton" to 2102,
                 "Row" to 2185
@@ -87,11 +85,9 @@
                 "Text" to false,
                 "Greeting" to false,
                 "Text" to false,
-                "<get-shapes>" to false,
                 "Surface" to false,
                 "Button" to false,
                 "Text" to false,
-                "<get-shapes>" to false,
                 "Surface" to false,
                 "TextButton" to false,
                 "Row" to true
diff --git a/compose/ui/ui-unit/build.gradle b/compose/ui/ui-unit/build.gradle
index 91140c3..9cd6331 100644
--- a/compose/ui/ui-unit/build.gradle
+++ b/compose/ui/ui-unit/build.gradle
@@ -118,4 +118,7 @@
 
 android {
     namespace "androidx.compose.ui.unit"
+    buildTypes.configureEach {
+        consumerProguardFiles("proguard-rules.pro")
+    }
 }
diff --git a/compose/ui/ui-unit/proguard-rules.pro b/compose/ui/ui-unit/proguard-rules.pro
new file mode 100644
index 0000000..67d118b
--- /dev/null
+++ b/compose/ui/ui-unit/proguard-rules.pro
@@ -0,0 +1,24 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Keep all the functions created to throw an exception. We don't want these functions to be
+# inlined in any way, which R8 will do by default. The whole point of these functions is to
+# reduce the amount of code generated at the call site.
+-keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+    static void throw*Exception(...);
+    static void throw*ExceptionForNullCheck(...);
+    # For methods returning Nothing
+    static java.lang.Void throw*Exception(...);
+    static java.lang.Void throw*ExceptionForNullCheck(...);
+}
diff --git a/compose/ui/ui-util/build.gradle b/compose/ui/ui-util/build.gradle
index 2ce5e6f..b1788829 100644
--- a/compose/ui/ui-util/build.gradle
+++ b/compose/ui/ui-util/build.gradle
@@ -107,4 +107,7 @@
 
 android {
     namespace "androidx.compose.ui.util"
+    buildTypes.configureEach {
+        consumerProguardFiles("proguard-rules.pro")
+    }
 }
diff --git a/compose/ui/ui-util/proguard-rules.pro b/compose/ui/ui-util/proguard-rules.pro
new file mode 100644
index 0000000..67d118b
--- /dev/null
+++ b/compose/ui/ui-util/proguard-rules.pro
@@ -0,0 +1,24 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Keep all the functions created to throw an exception. We don't want these functions to be
+# inlined in any way, which R8 will do by default. The whole point of these functions is to
+# reduce the amount of code generated at the call site.
+-keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+    static void throw*Exception(...);
+    static void throw*ExceptionForNullCheck(...);
+    # For methods returning Nothing
+    static java.lang.Void throw*Exception(...);
+    static java.lang.Void throw*ExceptionForNullCheck(...);
+}
diff --git a/compose/ui/ui/proguard-rules.pro b/compose/ui/ui/proguard-rules.pro
index 29f2984..f5603a7 100644
--- a/compose/ui/ui/proguard-rules.pro
+++ b/compose/ui/ui/proguard-rules.pro
@@ -37,8 +37,10 @@
 # reduce the amount of code generated at the call site.
 -keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
     static void throw*Exception(...);
+    static void throw*ExceptionForNullCheck(...);
     # For methods returning Nothing
     static java.lang.Void throw*Exception(...);
+    static java.lang.Void throw*ExceptionForNullCheck(...);
 }
 
 # When pointer input modifier nodes are added dynamically and have the same keys (common when
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
index b208f50..bfeca00 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
@@ -24,7 +24,6 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredSize
-import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
@@ -1549,52 +1548,6 @@
             assertThat(postFlingCount).isGreaterThan(0)
         }
     }
-
-    @Test
-    fun modifierIsRemoved_shouldKeepInfoAboutPreviousParent() {
-        val innerDispatcher = NestedScrollDispatcher()
-        val outerDispatcher = NestedScrollDispatcher()
-        var keepAround by mutableStateOf(true)
-
-        rule.setContent {
-            Column(
-                modifier =
-                    Modifier.nestedScroll(
-                        dispatcher = outerDispatcher,
-                        connection = object : NestedScrollConnection {}
-                    )
-            ) {
-                Box(
-                    Modifier.size(400.dp)
-                        .then(
-                            if (keepAround)
-                                Modifier.nestedScroll(
-                                    dispatcher = innerDispatcher,
-                                    connection = object : NestedScrollConnection {}
-                                )
-                            else Modifier
-                        )
-                )
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(innerDispatcher.lastKnownValidParentNode).isNull()
-            assertThat(innerDispatcher.nestedScrollNode?.parentNestedScrollNode)
-                .isEqualTo(outerDispatcher.nestedScrollNode)
-        }
-
-        rule.runOnIdle {
-            keepAround = false // remove inner node
-        }
-
-        rule.runOnIdle {
-            // the inner node's parent is the outer node
-            assertThat(innerDispatcher.lastKnownValidParentNode)
-                .isEqualTo(outerDispatcher.nestedScrollNode)
-            assertThat(innerDispatcher.nestedScrollNode?.parentNestedScrollNode).isNull()
-        }
-    }
 }
 
 private fun Offset.customEquals(other: Offset): Boolean {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
index 5e82355..b8d1e64 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
@@ -106,9 +106,6 @@
 
     internal var nestedScrollNode: NestedScrollNode? = null
 
-    // caches last known parent for fling clean up use.
-    internal var lastKnownValidParentNode: NestedScrollNode? = null
-
     // lambda to calculate the most outer nested scroll scope for this dispatcher on demand
     internal var calculateNestedScrollScope: () -> CoroutineScope? = { scope }
 
@@ -145,9 +142,7 @@
 
     /**
      * Parent to be set when attached to nested scrolling chain. `null` is valid and means there no
-     * nested scrolling parent above. The last known attached parent might be used in case this
-     * dispatcher is not attached to any node, that is [nestedScrollNode?.parentNestedScrollNode] is
-     * null.
+     * nested scrolling parent above
      */
     internal val parent: NestedScrollConnection?
         get() = nestedScrollNode?.parentNestedScrollNode
@@ -209,17 +204,7 @@
      * @return velocity that has been consumed by all the ancestors
      */
     suspend fun dispatchPostFling(consumed: Velocity, available: Velocity): Velocity {
-        // lastKnownValidParentNode can be used to send clean up signals.
-        // If this dispatcher's regular parent is not present it means either it never attached or
-        // it was detached. If it was detached we have information about its last known parent so
-        // we use it to send the post fling signal. We don't need to do the same for the other
-        // methods because the problem with parity in this API comes from a node that detaches
-        // during a fling.
-        return if (parent == null) {
-            lastKnownValidParentNode?.onPostFling(consumed, available) ?: Velocity.Zero
-        } else {
-            parent?.onPostFling(consumed, available) ?: Velocity.Zero
-        }
+        return parent?.onPostFling(consumed, available) ?: Velocity.Zero
     }
 }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt
index 680033b..432daec 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollNode.kt
@@ -21,7 +21,6 @@
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.TraversableNode
 import androidx.compose.ui.node.findNearestAncestor
-import androidx.compose.ui.node.traverseAncestors
 import androidx.compose.ui.unit.Velocity
 import kotlinx.coroutines.CoroutineScope
 
@@ -50,8 +49,6 @@
         resolvedDispatcher = dispatcher ?: NestedScrollDispatcher() // Resolve null dispatcher
     }
 
-    internal var lastKnownValidParentNode: NestedScrollNode? = null
-
     internal val parentNestedScrollNode: NestedScrollNode?
         get() = if (isAttached) findNearestAncestor() else null
 
@@ -97,12 +94,11 @@
     }
 
     override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+
         val selfConsumed = connection.onPostFling(consumed, available)
-        // if we receive an onPostFling after detaching this node, use the last known parent
-        // if this parent is also detached it will send the signal through the detached parents
-        val parent = if (isAttached) parentConnection else lastKnownValidParentNode
         val parentConsumed =
-            parent?.onPostFling(consumed + selfConsumed, available - selfConsumed) ?: Velocity.Zero
+            parentConnection?.onPostFling(consumed + selfConsumed, available - selfConsumed)
+                ?: Velocity.Zero
         return selfConsumed + parentConsumed
     }
 
@@ -132,9 +128,6 @@
     }
 
     override fun onDetach() {
-        // cache parent for detached clean up access in the dispatcher and in this node.
-        lastKnownValidParentNode = findNearestAttachedAncestor()
-        resolvedDispatcher.lastKnownValidParentNode = lastKnownValidParentNode
         resetDispatcherFields()
     }
 
@@ -144,9 +137,6 @@
      */
     private fun updateDispatcherFields() {
         resolvedDispatcher.nestedScrollNode = this
-        // reset lastKnownValidParentNodes
-        resolvedDispatcher.lastKnownValidParentNode = null
-        lastKnownValidParentNode = null
         resolvedDispatcher.calculateNestedScrollScope = { nestedCoroutineScope }
         resolvedDispatcher.scope = coroutineScope
     }
@@ -165,16 +155,3 @@
         updateDispatcher(dispatcher)
     }
 }
-
-private fun <T : TraversableNode> T.findNearestAttachedAncestor(): T? {
-    var node: T? = null
-    traverseAncestors {
-        if (it.node.isAttached) {
-            node = it
-            false
-        } else {
-            true
-        }
-    }
-    return node
-}
diff --git a/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt b/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt
index c96cd6d..545a3d6 100644
--- a/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt
+++ b/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt
@@ -63,32 +63,15 @@
               }
             }
           ],
-          variants: [
+          "dependencyConstraints": [
             {
-              "name": "releaseVariantReleaseApiPublication",
-              "dependencyConstraints": [
-                {
-                   "group": "org.jetbrains.kotlin",
-                   "module": "kotlin-stdlib",
-                   "version": {
-                     "requires": "1.8.22"
-                   }
-                }
-              ],
-             },
-             {
-              "name": "releaseVariantReleaseRuntimePublication",
-              "dependencyConstraints": [
-                {
-                  "group": "androidx.preference",
-                  "module": "preference-ktx",
-                  "version": {
-                    "requires": "1.3.0-alpha01"
-                  }
-                }
-              ],
-             }
-          ]
+              "group": "androidx.preference",
+              "module": "preference-ktx",
+              "version": {
+                "requires": "1.3.0-alpha01"
+              }
+            }
+          ],
           "files": [
             {
               "name": "preference-1.3.0-alpha01.aar",
@@ -102,7 +85,7 @@
     }
 
     private fun getConstraintVersion(metadata: String, groupId: String, artifact: String): String? =
-        getDependencyConstraints(metadata).let {
+        getDependencyConstraints(metadata)?.let {
             Regex(
                     "\"group\": \"$groupId\",\\s+\"module\": " +
                         "\"$artifact\",\\s+\"version\": \\{\\s+\"requires\": \"(.+?)\""
@@ -114,8 +97,8 @@
         }
 
     private fun getDependencyConstraints(moduleJson: String) =
-        Regex("(?s)\"dependencyConstraints\": \\[(.+?)]").findAll(moduleJson).joinToString("\n") {
-            it.groups.get(1)?.value.orEmpty()
+        moduleJson.let {
+            Regex("(?s)\"dependencyConstraints\": \\[(.+?)]").find(it)?.groups?.get(1)?.value
         }
 
     // Yes, I know https://stackoverflow.com/a/1732454/258688, but it's just a test...
diff --git a/datastore/datastore-core-okio/build.gradle b/datastore/datastore-core-okio/build.gradle
index 6fae6c4..76f36b5 100644
--- a/datastore/datastore-core-okio/build.gradle
+++ b/datastore/datastore-core-okio/build.gradle
@@ -22,8 +22,6 @@
  * modifying its settings.
  */
 
-
-import androidx.build.KotlinTarget
 import androidx.build.LibraryType
 import androidx.build.PlatformIdentifier
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -103,6 +101,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android DataStore Core Okio- contains APIs to use datastore-core in multiplatform via okio"
-    kotlinTarget = KotlinTarget.KOTLIN_1_9
     metalavaK2UastEnabled = false
 }
diff --git a/datastore/datastore-guava/build.gradle b/datastore/datastore-guava/build.gradle
index 9b5e7d5..ed7a9bf 100644
--- a/datastore/datastore-guava/build.gradle
+++ b/datastore/datastore-guava/build.gradle
@@ -21,8 +21,6 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
-
-import androidx.build.KotlinTarget
 import androidx.build.LibraryType
 
 plugins {
@@ -62,6 +60,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2024"
     description = "Android DataStore Guava - contains wrappers for using DataStore using ListenableFuture"
-    kotlinTarget = KotlinTarget.KOTLIN_1_9
     metalavaK2UastEnabled = true
 }
diff --git a/datastore/datastore-rxjava2/build.gradle b/datastore/datastore-rxjava2/build.gradle
index 2403a7c..db861b7 100644
--- a/datastore/datastore-rxjava2/build.gradle
+++ b/datastore/datastore-rxjava2/build.gradle
@@ -21,8 +21,6 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
-
-import androidx.build.KotlinTarget
 import androidx.build.LibraryType
 
 plugins {
@@ -65,6 +63,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
-    kotlinTarget = KotlinTarget.KOTLIN_1_9
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/datastore/datastore-rxjava3/build.gradle b/datastore/datastore-rxjava3/build.gradle
index 9d2e9cc..58fe0d7 100644
--- a/datastore/datastore-rxjava3/build.gradle
+++ b/datastore/datastore-rxjava3/build.gradle
@@ -21,8 +21,6 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
-
-import androidx.build.KotlinTarget
 import androidx.build.LibraryType
 
 plugins {
@@ -65,6 +63,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
-    kotlinTarget = KotlinTarget.KOTLIN_1_9
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/datastore/datastore/build.gradle b/datastore/datastore/build.gradle
index 8113d77..6bdb7be 100644
--- a/datastore/datastore/build.gradle
+++ b/datastore/datastore/build.gradle
@@ -21,8 +21,6 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
-
-import androidx.build.KotlinTarget
 import androidx.build.LibraryType
 import androidx.build.PlatformIdentifier
 
@@ -114,6 +112,5 @@
     inceptionYear = "2020"
     description = "Android DataStore - contains the underlying store used by each serialization " +
             "method along with components that require an Android dependency"
-    kotlinTarget = KotlinTarget.KOTLIN_1_9
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt b/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
index cccd79d..185d6ac 100644
--- a/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
+++ b/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
@@ -40,6 +40,7 @@
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
+import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
 import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetAttribute
 import org.jetbrains.kotlin.konan.target.KonanTarget
 
@@ -428,6 +429,25 @@
                 }
             }
 
+            val js =
+                KOTlIN_USAGES.map { kotlinUsage ->
+                    createConfiguration(*dependencies) {
+                        attributes.apply {
+                            attribute(Category.CATEGORY_ATTRIBUTE, Category.LIBRARY)
+                            attribute(
+                                TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
+                                "non-jvm"
+                            )
+                            attribute(Usage.USAGE_ATTRIBUTE, kotlinUsage)
+                            attribute(
+                                KotlinJsCompilerAttribute.jsCompilerAttribute,
+                                KotlinJsCompilerAttribute.ir
+                            )
+                            attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
+                        }
+                    }
+                }
+
             val commonArtifacts = KOTlIN_USAGES.map { kotlinUsage ->
                 createConfiguration(*dependencies) {
                     attributes.apply {
@@ -437,7 +457,7 @@
                     }
                 }
             }
-            return jvmAndAndroid + wasmJs + konanTargetConfigurations + commonArtifacts
+            return jvmAndAndroid + wasmJs + js + konanTargetConfigurations + commonArtifacts
         }
 
         private fun createKonanTargetConfiguration(
diff --git a/development/update_kotlin.sh b/development/update_kotlin.sh
index eff00fd..b6f492a 100755
--- a/development/update_kotlin.sh
+++ b/development/update_kotlin.sh
@@ -2,12 +2,14 @@
 set -e
 
 KOTLIN_VERSION="$1"
-KSP_VERSION="$2"
 
-if [[ $# -eq 0 ]] ; then
-    echo "Usage ./development/update_kotlin.sh <kotlin_version> [<ksp_version>]"
-    exit 1
-fi
+ALLOW_JETBRAINS_DEV=""
+for arg in "$@"
+do
+    if [ "$arg" == "--allow-jetbrains-dev" ]; then
+      ALLOW_JETBRAINS_DEV="--allow-jetbrains-dev"
+    fi
+done
 
 # Download maven artifacts
 ARTIFACTS_TO_DOWNLOAD="org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION,"
@@ -36,39 +38,7 @@
 ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-native-prebuilt:$KOTLIN_VERSION:[email protected],"
 ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-native-prebuilt:$KOTLIN_VERSION:[email protected],"
 
-if [ "$KSP_VERSION" ]; then
-    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing:$KSP_VERSION,"
-    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-api:$KSP_VERSION,"
-    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-cmdline:$KSP_VERSION,"
-    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-gradle-plugin:$KSP_VERSION,"
-    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-aa-embeddable:$KSP_VERSION,"
-fi
+./development/importMaven/importMaven.sh "$ALLOW_JETBRAINS_DEV" "$ARTIFACTS_TO_DOWNLOAD"
 
-./development/importMaven/importMaven.sh "$ARTIFACTS_TO_DOWNLOAD"
+ ./development/importMaven/importMaven.sh import-konan-binaries --konan-compiler-version "$KOTLIN_VERSION"
 
-# symlink native compiler prebuilt archives from prebuilts/androidx/external to prebuilts/androidx/konan
-# to make KonanPrebuiltsSetup.kt work.
-rm -fr "../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases"
-
-REAL_NATIVE_PREBUILT_DIR="../../../../../external/org/jetbrains/kotlin/kotlin-native-prebuilt/$KOTLIN_VERSION/"
-
-LINUX_DIR="../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases/$KOTLIN_VERSION/linux-x86_64"
-mkdir -p "$LINUX_DIR"
-ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-linux-x86_64.tar.gz" \
-    "$LINUX_DIR/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz"
-ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-linux-x86_64.tar.gz.asc" \
-    "$LINUX_DIR/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz.asc"
-
-MAC_ARM_DIR="../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases/$KOTLIN_VERSION/macos-aarch64"
-mkdir -p "$MAC_ARM_DIR"
-ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-aarch64.tar.gz" \
-    "$MAC_ARM_DIR/kotlin-native-prebuilt-macos-aarch64-$KOTLIN_VERSION.tar.gz"
-ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-aarch64.tar.gz.asc" \
-    "$MAC_ARM_DIR/kotlin-native-prebuilt-macos-aarch64-$KOTLIN_VERSION.tar.gz.asc"
-
-MAC_X86_DIR="../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases/$KOTLIN_VERSION/macos-x86_64"
-mkdir -p "$MAC_X86_DIR"
-ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-x86_64.tar.gz" \
-    "$MAC_X86_DIR/kotlin-native-prebuilt-macos-x86_64-$KOTLIN_VERSION.tar.gz"
-ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-x86_64.tar.gz.asc" \
-    "$MAC_X86_DIR/kotlin-native-prebuilt-macos-x86_64-$KOTLIN_VERSION.tar.gz.asc"
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index c082e75..822623e 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -366,6 +366,7 @@
     docs(project(":wear:protolayout:protolayout-material"))
     docs(project(":wear:protolayout:protolayout-material-core"))
     docs(project(":wear:protolayout:protolayout-material3"))
+    samples(project(":wear:protolayout:protolayout-material3-samples"))
     docs(project(":wear:protolayout:protolayout-renderer"))
     docs(project(":wear:protolayout:protolayout-testing"))
     docs(project(":wear:tiles:tiles"))
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 3a9fe44..66db4f6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -45,7 +45,7 @@
 kotlin17 = "1.7.10"
 kotlin18 = "1.8.22"
 kotlin19 = "1.9.24"
-kotlin = "2.0.21-RC"
+kotlin = "2.0.10"
 kotlinBenchmark = "0.4.11"
 kotlinGradlePluginAnnotations = "1.9.24"
 kotlinGradlePluginApi = "1.9.24"
@@ -54,7 +54,7 @@
 kotlinNativeUtils = "1.9.24"
 kotlinSerialization = "1.6.3"
 kotlinToolingCore = "1.9.24"
-ksp = "2.0.20-1.0.25"
+ksp = "2.0.10-1.0.24"
 ktfmt = "0.50"
 leakcanary = "2.13"
 media3 = "1.4.1"
@@ -62,7 +62,7 @@
 mockito = "2.25.0"
 moshi = "1.13.0"
 node = "16.20.2"
-protobuf = "3.22.3"
+protobuf = "4.28.2"
 skiko = "0.7.7"
 spdxGradlePlugin = "0.6.0"
 sqldelight = "1.3.0"
@@ -276,7 +276,6 @@
 protobufCompiler = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
 protobufGradlePlugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version = "0.9.4" }
 protobufLite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobuf" }
-protobufKotlin = { module = "com.google.protobuf:protobuf-kotlin", version.ref = "protobuf" }
 reactiveStreams = { module = "org.reactivestreams:reactive-streams", version = "1.0.0" }
 retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
 retrofitConverterWire = { module = "com.squareup.retrofit2:converter-wire", version.ref = "retrofit" }
diff --git a/health/connect/connect-testing/src/main/java/androidx/health/connect/client/testing/RecordTestUtils.kt b/health/connect/connect-testing/src/main/java/androidx/health/connect/client/testing/RecordTestUtils.kt
index 3c3dd99..74f741d 100644
--- a/health/connect/connect-testing/src/main/java/androidx/health/connect/client/testing/RecordTestUtils.kt
+++ b/health/connect/connect-testing/src/main/java/androidx/health/connect/client/testing/RecordTestUtils.kt
@@ -37,22 +37,22 @@
         if (proto.hasInstantTimeMillis()) {
             val time =
                 LocalDateTime.ofInstant(proto.time, proto.zoneOffset ?: ZoneId.systemDefault())
-            return !time.isBefore(timeRangeFilter.localStartTime) &&
+            return !time.isBefore(timeRangeFilter.localStartTime!!) &&
                 timeRangeFilter.localEndTime!!.isAfter(time)
         }
         val startTime =
             LocalDateTime.ofInstant(proto.startTime, proto.zoneOffset ?: ZoneId.systemDefault())
-        return !startTime.isBefore(timeRangeFilter.localStartTime) &&
+        return !startTime.isBefore(timeRangeFilter.localStartTime!!) &&
             timeRangeFilter.localEndTime!!.isAfter(startTime)
     }
 
     if (proto.hasInstantTimeMillis()) {
-        return proto.time >= timeRangeFilter.startTime && // Inclusive
-            proto.time.isBefore(timeRangeFilter.endTime) // Exclusive
+        return proto.time >= timeRangeFilter.startTime!! && // Inclusive
+            proto.time.isBefore(timeRangeFilter.endTime!!) // Exclusive
     }
 
-    return proto.startTime >= timeRangeFilter.startTime && // Inclusive
-        proto.endTime.isBefore(timeRangeFilter.endTime) // Exclusive
+    return proto.startTime >= timeRangeFilter.startTime!! && // Inclusive
+        proto.endTime.isBefore(timeRangeFilter.endTime!!) // Exclusive
 }
 
 private fun TimeRangeFilter.sanitize(clock: Clock): TimeRangeFilter {
diff --git a/libraryversions.toml b/libraryversions.toml
index 9da553b..8a6f498 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -1,5 +1,5 @@
 [versions]
-ACTIVITY = "1.10.0-alpha02"
+ACTIVITY = "1.10.0-alpha03"
 ANNOTATION = "1.9.0-rc01"
 ANNOTATION_EXPERIMENTAL = "1.5.0-alpha01"
 APPCOMPAT = "1.8.0-alpha01"
@@ -7,7 +7,7 @@
 ARCH_CORE = "2.3.0-alpha01"
 ASYNCLAYOUTINFLATER = "1.1.0-alpha02"
 AUTOFILL = "1.3.0-beta01"
-BENCHMARK = "1.4.0-alpha02"
+BENCHMARK = "1.4.0-alpha03"
 BIOMETRIC = "1.4.0-alpha02"
 BLUETOOTH = "1.0.0-alpha02"
 BROWSER = "1.9.0-alpha01"
@@ -19,12 +19,12 @@
 CAMERA_VIEWFINDER = "1.4.0-alpha09"
 CARDVIEW = "1.1.0-alpha01"
 CAR_APP = "1.7.0-beta02"
-COLLECTION = "1.5.0-alpha03"
-COMPOSE = "1.8.0-alpha03"
+COLLECTION = "1.5.0-alpha04"
+COMPOSE = "1.8.0-alpha04"
 COMPOSE_MATERIAL3 = "1.4.0-alpha01"
 COMPOSE_MATERIAL3_ADAPTIVE = "1.1.0-alpha04"
 COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
-COMPOSE_RUNTIME = "1.8.0-alpha03"
+COMPOSE_RUNTIME = "1.8.0-alpha04"
 CONSTRAINTLAYOUT = "2.2.0-beta01"
 CONSTRAINTLAYOUT_COMPOSE = "1.1.0-beta01"
 CONSTRAINTLAYOUT_CORE = "1.1.0-beta01"
@@ -91,7 +91,7 @@
 LEANBACK_TAB = "1.1.0-beta01"
 LEGACY = "1.1.0-alpha01"
 LIBYUV = "0.1.0-dev01"
-LIFECYCLE = "2.9.0-alpha04"
+LIFECYCLE = "2.9.0-alpha05"
 LIFECYCLE_EXTENSIONS = "2.2.0"
 LINT = "1.0.0-alpha02"
 LOADER = "1.2.0-alpha01"
@@ -117,9 +117,9 @@
 RECYCLERVIEW_SELECTION = "1.2.0-alpha02"
 REMOTECALLBACK = "1.0.0-alpha02"
 RESOURCEINSPECTION = "1.1.0-alpha01"
-ROOM = "2.7.0-alpha09"
+ROOM = "2.7.0-alpha10"
 SAFEPARCEL = "1.0.0-alpha01"
-SAVEDSTATE = "1.3.0-alpha02"
+SAVEDSTATE = "1.3.0-alpha03"
 SECURITY = "1.1.0-alpha07"
 SECURITY_APP_AUTHENTICATOR = "1.0.0-rc01"
 SECURITY_APP_AUTHENTICATOR_TESTING = "1.0.0-rc01"
@@ -133,7 +133,7 @@
 SLICE_BUILDERS_KTX = "1.0.0-alpha09"
 SLICE_REMOTECALLBACK = "1.0.0-alpha01"
 SLIDINGPANELAYOUT = "1.3.0-alpha01"
-SQLITE = "2.5.0-alpha09"
+SQLITE = "2.5.0-alpha10"
 SQLITE_INSPECTOR = "2.1.0-alpha01"
 STABLE_AIDL = "1.0.0-alpha01"
 STARTUP = "1.2.0-rc01"
@@ -174,7 +174,7 @@
 WINDOW_EXTENSIONS = "1.4.0-beta01"
 WINDOW_EXTENSIONS_CORE = "1.1.0-alpha01"
 WINDOW_SIDECAR = "1.0.0-rc01"
-WORK = "2.10.0-beta01"
+WORK = "2.10.0-rc01"
 XR = "1.0.0-alpha01"
 
 [groups]
diff --git a/navigation/navigation-runtime-lint/build.gradle b/navigation/navigation-runtime-lint/build.gradle
index 8baa413..575793e 100644
--- a/navigation/navigation-runtime-lint/build.gradle
+++ b/navigation/navigation-runtime-lint/build.gradle
@@ -38,7 +38,6 @@
     compileOnly(libs.androidToolsCommon)
     compileOnly(libs.intellijCore)
     compileOnly(libs.uast)
-    bundleInside(project(":navigation:navigation-common-lint"))
     bundleInside(project(":navigation:navigation-lint-common"))
 
     testImplementation(libs.kotlinStdlib)
diff --git a/paging/paging-common/bcv/native/current.txt b/paging/paging-common/bcv/native/current.txt
index 61b670d..64890c9 100644
--- a/paging/paging-common/bcv/native/current.txt
+++ b/paging/paging-common/bcv/native/current.txt
@@ -70,6 +70,8 @@
     final fun unregisterInvalidatedCallback(kotlin/Function0<kotlin/Unit>) // androidx.paging/PagingSource.unregisterInvalidatedCallback|unregisterInvalidatedCallback(kotlin.Function0<kotlin.Unit>){}[0]
 
     sealed class <#A1: kotlin/Any, #B1: kotlin/Any> LoadResult { // androidx.paging/PagingSource.LoadResult|null[0]
+        constructor <init>() // androidx.paging/PagingSource.LoadResult.<init>|<init>(){}[0]
+
         final class <#A2: kotlin/Any, #B2: kotlin/Any> Error : androidx.paging/PagingSource.LoadResult<#A2, #B2> { // androidx.paging/PagingSource.LoadResult.Error|null[0]
             constructor <init>(kotlin/Throwable) // androidx.paging/PagingSource.LoadResult.Error.<init>|<init>(kotlin.Throwable){}[0]
 
@@ -123,6 +125,8 @@
     }
 
     sealed class <#A1: kotlin/Any> LoadParams { // androidx.paging/PagingSource.LoadParams|null[0]
+        constructor <init>(kotlin/Int, kotlin/Boolean) // androidx.paging/PagingSource.LoadParams.<init>|<init>(kotlin.Int;kotlin.Boolean){}[0]
+
         abstract val key // androidx.paging/PagingSource.LoadParams.key|{}key[0]
             abstract fun <get-key>(): #A1? // androidx.paging/PagingSource.LoadParams.key.<get-key>|<get-key>(){}[0]
         final val loadSize // androidx.paging/PagingSource.LoadParams.loadSize|{}loadSize[0]
@@ -168,6 +172,8 @@
     }
 
     sealed class MediatorResult { // androidx.paging/RemoteMediator.MediatorResult|null[0]
+        constructor <init>() // androidx.paging/RemoteMediator.MediatorResult.<init>|<init>(){}[0]
+
         final class Error : androidx.paging/RemoteMediator.MediatorResult { // androidx.paging/RemoteMediator.MediatorResult.Error|null[0]
             constructor <init>(kotlin/Throwable) // androidx.paging/RemoteMediator.MediatorResult.Error.<init>|<init>(kotlin.Throwable){}[0]
 
@@ -336,6 +342,8 @@
 }
 
 sealed class <#A: kotlin/Any> androidx.paging/PagingDataEvent { // androidx.paging/PagingDataEvent|null[0]
+    constructor <init>() // androidx.paging/PagingDataEvent.<init>|<init>(){}[0]
+
     final class <#A1: kotlin/Any> Append : androidx.paging/PagingDataEvent<#A1> { // androidx.paging/PagingDataEvent.Append|null[0]
         constructor <init>(kotlin/Int, kotlin.collections/List<#A1>, kotlin/Int, kotlin/Int) // androidx.paging/PagingDataEvent.Append.<init>|<init>(kotlin.Int;kotlin.collections.List<1:0>;kotlin.Int;kotlin.Int){}[0]
 
@@ -415,6 +423,8 @@
 }
 
 sealed class androidx.paging/LoadState { // androidx.paging/LoadState|null[0]
+    constructor <init>(kotlin/Boolean) // androidx.paging/LoadState.<init>|<init>(kotlin.Boolean){}[0]
+
     final val endOfPaginationReached // androidx.paging/LoadState.endOfPaginationReached|{}endOfPaginationReached[0]
         final fun <get-endOfPaginationReached>(): kotlin/Boolean // androidx.paging/LoadState.endOfPaginationReached.<get-endOfPaginationReached>|<get-endOfPaginationReached>(){}[0]
 
diff --git a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
index ab10603..ac0ae4c 100644
--- a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
+++ b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
@@ -262,11 +262,6 @@
     ): View? {
         super.onCreateView(inflater, container, savedInstanceState)
         this.container = container
-        if (!hasContents && delayedContentsAvailable == null) {
-            if (savedInstanceState != null) {
-                restoreContents(savedInstanceState)
-            }
-        }
 
         pdfViewer = inflater.inflate(R.layout.pdf_viewer_container, container, false) as FrameLayout
         pdfViewer?.isScrollContainer = true
@@ -313,7 +308,9 @@
                         // and no means to load it. The best way is to just kill the service which
                         // will restart on the next onStart.
                         pdfLoader?.disconnect()
+                        return@PdfLoaderCallbacksImpl true
                     }
+                    return@PdfLoaderCallbacksImpl false
                 },
                 onDocumentLoaded = {
                     documentLoaded = true
@@ -354,6 +351,12 @@
             }
         }
 
+        if (!hasContents && delayedContentsAvailable == null) {
+            if (savedInstanceState != null) {
+                restoreContents(savedInstanceState)
+            }
+        }
+
         return pdfViewer
     }
 
@@ -402,7 +405,6 @@
         if (onScreen) {
             onExit()
         }
-        onScreen = false
         started = false
         super.onStop()
     }
@@ -688,6 +690,7 @@
                 fileData?.let {
                     localUri = it.uri
                     postContentsAvailable(it)
+                    postEnter()
                 }
             } catch (e: Exception) {
                 // This can happen if the data is an instance of StreamOpenable, and the client
@@ -735,6 +738,7 @@
 
         pdfLoaderCallbacks?.searchModel = null
 
+        pdfLoader?.disconnect()
         pdfLoader = null
         documentLoaded = false
     }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
index 3a8b773..7b18834 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
@@ -109,10 +109,13 @@
 
     @NonNull
     protected String buildContentDescription(@Nullable String pageText, int pageNum) {
-        return (pageText != null)
-                ? pageText
-                : getContext()
-                        .getString(androidx.pdf.R.string.desc_page, (mPageNum + 1));
+        if (pageText != null) {
+            if (pageText.trim().isEmpty()) {
+                return getContext().getString(androidx.pdf.R.string.desc_empty_page);
+            }
+            return pageText;
+        }
+        return getContext().getString(androidx.pdf.R.string.desc_page, (mPageNum + 1));
     }
 
     /** Returns true if we have data about any links on the page. */
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
index d91d660..99ad13f 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
@@ -28,6 +28,7 @@
 import androidx.pdf.models.SelectionBoundary;
 import androidx.pdf.select.SelectionActionMode;
 import androidx.pdf.select.SelectionModel;
+import androidx.pdf.util.Accessibility;
 import androidx.pdf.util.Preconditions;
 import androidx.pdf.widget.ZoomView;
 import androidx.pdf.widget.ZoomableSelectionHandles;
@@ -45,6 +46,7 @@
     private SelectionBoundary mDragging;
 
     private SelectionActionMode mSelectionActionMode;
+    private Boolean mIsSelectionHandlesVisible;
 
     public PdfSelectionHandles(
             @NonNull PdfSelectionModel selectionModel, @NonNull ZoomView zoomView,
@@ -62,10 +64,17 @@
     protected void updateHandles() {
         if (mSelection == null || mPdfView.getViewAt(mSelection.getPage()) == null) {
             hideHandles();
+            mIsSelectionHandlesVisible = false;
         } else {
             View pageView = mPdfView.getViewAt(mSelection.getPage()).asView();
             showHandle(mStartHandle, pageView, mSelection.getStart(), false);
             showHandle(mStopHandle, pageView, mSelection.getStop(), true);
+
+            if (!mIsSelectionHandlesVisible) {
+                Accessibility.get().announce(mZoomView.getContext(), mZoomView,
+                        mSelection.getText());
+                mIsSelectionHandlesVisible = true;
+            }
         }
     }
 
@@ -96,6 +105,8 @@
     protected void onDragHandleUp() {
         mSelectionActionMode.resume();
         mPdfView.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE);
+        Accessibility.get().announce(mZoomView.getContext(), mZoomView,
+                mSelection.getText());
     }
 
     @NonNull
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
index df72681..a86c12b 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
@@ -74,6 +74,11 @@
         this.mOnConnectFailure = onConnectFailure;
     }
 
+    /** Checks if Connection to PdfDocumentService is established */
+    public boolean isConnected() {
+        return mConnected;
+    }
+
     /**
      * Returns a {@link PdfDocumentRemote} if the service is bound. It could be still initializing
      * (see {@link #setDocumentLoaded}).
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
index cf49ad7..4506163 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
@@ -139,7 +139,26 @@
 
     /** Schedule task to load a PdfDocument. */
     public void reloadDocument() {
-        mExecutor.schedule(new LoadDocumentTask(mLoadedPassword));
+        if (isConnected()) {
+            mExecutor.schedule(new LoadDocumentTask(mLoadedPassword));
+        } else {
+            /*
+            *  For password protected files we kill the service if the app goes into
+            *  background before the document is loaded hence here we just register a
+            *  task which will be executed once the service is reconnected onStart
+            */
+            mConnection.setOnConnectInitializer(
+                    () -> mExecutor.schedule(new LoadDocumentTask(mLoadedPassword)));
+            mConnection.setConnectionFailureHandler(
+                    () -> mCallbacks.documentNotLoaded(PdfStatus.NONE));
+        }
+    }
+
+    /**
+     * Check if PdfLoader is connected to PdfDocumentService
+     */
+    public boolean isConnected() {
+        return mConnection.isConnected();
     }
 
     /**
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
index 7b2d1b0..2f5210b 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
@@ -67,7 +67,7 @@
     private var isTextSearchActive: Boolean,
     private var viewState: ExposedValue<ViewState>,
     private val fragmentContainerView: View?,
-    private val onRequestPassword: (Boolean) -> Unit,
+    private val onRequestPassword: (Boolean) -> Boolean,
     private val onDocumentLoaded: () -> Unit,
     private val onDocumentLoadFailure: (Throwable) -> Unit,
     private var eventCallback: EventCallback?,
@@ -148,39 +148,39 @@
 
     override fun requestPassword(incorrect: Boolean) {
         eventCallback?.onViewerReset()
-        onRequestPassword(onScreen)
+        if (onRequestPassword(onScreen)) return
 
         if (viewState.get() != ViewState.NO_VIEW) {
             var passwordDialog = currentPasswordDialog(fragmentManager)
             if (passwordDialog == null) {
                 passwordDialog = PdfPasswordDialog()
-                passwordDialog.setListener(
-                    object : PdfPasswordDialog.PasswordDialogEventsListener {
-                        override fun onPasswordTextChange(password: String) {
-                            pdfLoader?.applyPassword(password)
-                        }
-
-                        override fun onDialogCancelled() {
-                            val retryCallback = Runnable { requestPassword(false) }
-                            val snackbar =
-                                fragmentContainerView?.let {
-                                    Snackbar.make(
-                                        it,
-                                        R.string.password_not_entered,
-                                        Snackbar.LENGTH_INDEFINITE
-                                    )
-                                }
-                            val mResolveClickListener =
-                                View.OnClickListener { _: View? -> retryCallback.run() }
-                            snackbar?.setAction(R.string.retry_button_text, mResolveClickListener)
-                            snackbar?.show()
-                        }
-                    }
-                )
-
                 passwordDialog.show(fragmentManager, PASSWORD_DIALOG_TAG)
             }
 
+            passwordDialog.setListener(
+                object : PdfPasswordDialog.PasswordDialogEventsListener {
+                    override fun onPasswordTextChange(password: String) {
+                        pdfLoader?.applyPassword(password)
+                    }
+
+                    override fun onDialogCancelled() {
+                        val retryCallback = Runnable { requestPassword(false) }
+                        val snackbar =
+                            fragmentContainerView?.let {
+                                Snackbar.make(
+                                    it,
+                                    R.string.password_not_entered,
+                                    Snackbar.LENGTH_INDEFINITE
+                                )
+                            }
+                        val mResolveClickListener =
+                            View.OnClickListener { _: View? -> retryCallback.run() }
+                        snackbar?.setAction(R.string.retry_button_text, mResolveClickListener)
+                        snackbar?.show()
+                    }
+                }
+            )
+
             if (incorrect) {
                 passwordDialog.retry()
             }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
index a994a5d..d887f00 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
@@ -63,6 +63,7 @@
     private int mBlueColor;
     private int mTextErrorColor;
     private AlertDialog mPasswordDialog;
+    private static final String PASSWORD_INPUT = "password_input";
 
     private boolean mIncorrect;
     private boolean mFinishOnCancel;
@@ -92,6 +93,11 @@
         final EditText passwordField = (EditText) view.findViewById(R.id.password);
         setupPasswordField(passwordField);
 
+        if (savedInstanceState != null) {
+            String savedPassword = savedInstanceState.getString(PASSWORD_INPUT, "");
+            passwordField.setText(savedPassword);
+        }
+
         // Hijack the positive button to NOT dismiss the dialog immediately.
         dialog.setOnShowListener(
                 new OnShowListener() {
@@ -146,6 +152,17 @@
         return dialog;
     }
 
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+        // Find the EditText by its ID
+        EditText editTextPassword = (EditText) mPasswordDialog.findViewById(R.id.password);
+        // Get the text from the EditText and store it in passwordInput
+        String passwordInput = editTextPassword.getText().toString();
+        // Save the input to the outState bundle
+        outState.putString(PASSWORD_INPUT, passwordInput);
+    }
+
     private void setupPasswordField(final EditText passwordField) {
         passwordField.setFocusable(true);
         passwordField.requestFocus();
diff --git a/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml b/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml
index d58540a..0e33f06 100644
--- a/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml
+++ b/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml
@@ -57,41 +57,37 @@
 
     <ImageButton
         android:id="@+id/find_prev_btn"
-        android:layout_width="34dp"
-        android:layout_height="34dp"
+        android:layout_width="@dimen/find_in_file_button_size"
+        android:layout_height="@dimen/find_in_file_button_size"
         android:background="@drawable/shape_oval"
         android:src="@drawable/keyboard_up"
         app:tint="?attr/colorOnSurfaceVariant"
         android:cropToPadding="true"
         android:padding="3dp"
         android:scaleType="centerInside"
-        android:layout_margin="5dp"
         android:contentDescription = "@string/previous_button_description"/>
 
     <ImageButton
         android:id="@+id/find_next_btn"
-        android:layout_width="34dp"
-        android:layout_height="34dp"
+        android:layout_width="@dimen/find_in_file_button_size"
+        android:layout_height="@dimen/find_in_file_button_size"
         android:background="@drawable/shape_oval"
         android:src="@drawable/keyboard_down"
         app:tint="?attr/colorOnSurfaceVariant"
         android:cropToPadding="true"
         android:padding="3dp"
         android:scaleType="centerInside"
-        android:layout_margin="5dp"
         android:contentDescription = "@string/next_button_description"/>
     <ImageButton
         android:id="@+id/close_btn"
-        android:layout_width="34dp"
-        android:layout_height="34dp"
+        android:layout_width="@dimen/find_in_file_button_size"
+        android:layout_height="@dimen/find_in_file_button_size"
         android:background="@drawable/shape_oval"
         android:src="@drawable/close_button"
         app:tint="?attr/colorOnSurfaceVariant"
         android:cropToPadding="true"
         android:padding="5dp"
         android:scaleType="centerInside"
-        android:layout_marginVertical="5dp"
-        android:layout_marginLeft="5dp"
         android:layout_marginRight="10dp"
         android:contentDescription = "@string/close_button_description"/>
 
diff --git a/pdf/pdf-viewer/src/main/res/menu/context_menu.xml b/pdf/pdf-viewer/src/main/res/menu/context_menu.xml
index 9557c52..156ad91 100644
--- a/pdf/pdf-viewer/src/main/res/menu/context_menu.xml
+++ b/pdf/pdf-viewer/src/main/res/menu/context_menu.xml
@@ -21,10 +21,12 @@
         android:id="@+id/action_copy"
         android:icon="?android:attr/actionModeCopyDrawable"
         android:title="@android:string/copy"
-        android:alphabeticShortcut="c" />
+        android:alphabeticShortcut="c"
+        android:contentDescription="@android:string/copy"/>
     <item
         android:id="@+id/action_selectAll"
         android:icon="?android:attr/actionModeSelectAllDrawable"
         android:title="@android:string/selectAll"
-        android:alphabeticShortcut="a" />
+        android:alphabeticShortcut="a"
+        android:contentDescription="@android:string/selectAll"/>
 </menu>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/res/values/dimensions.xml b/pdf/pdf-viewer/src/main/res/values/dimensions.xml
index 584fda6..ab5c220 100644
--- a/pdf/pdf-viewer/src/main/res/values/dimensions.xml
+++ b/pdf/pdf-viewer/src/main/res/values/dimensions.xml
@@ -23,4 +23,7 @@
     <dimen name="viewer_edit_fab_margin_bottom">16dp</dimen>
     <dimen name="viewer_edit_fab_margin_right">16dp</dimen>
 
+    <!--Find in file button size -->
+    <dimen name="find_in_file_button_size">48dp</dimen>
+
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values/strings.xml b/pdf/pdf-viewer/src/main/res/values/strings.xml
index c6ad40f..20aa27e 100644
--- a/pdf/pdf-viewer/src/main/res/values/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values/strings.xml
@@ -57,6 +57,9 @@
     <!-- Content description for a page of a paginated document, eg "page 3". [CHAR LIMIT=50] -->
     <string name="desc_page">page <xliff:g example="3" id="page">%1$d</xliff:g></string>
 
+    <!-- Content description for an empty page of a paginated document -->
+    <string name="desc_empty_page">Empty page</string>
+
     <!-- Error message when file format isn't valid PDF. [CHAR LIMIT=60] -->
     <string name="error_file_format_pdf">Cannot display PDF ("<xliff:g example="Treasure Island" id="title">%1$s</xliff:g>" is of invalid format)</string>
 
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
index 6f711e5..68add33 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
@@ -24,7 +24,6 @@
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runKaptTest
 import androidx.room.compiler.processing.util.runProcessorTest
-import org.junit.Ignore
 import org.junit.Test
 
 class KotlinMetadataTest {
@@ -49,7 +48,6 @@
         }
     }
 
-    @Ignore("b/360398921")
     @Test
     fun inlineReifiedFunctionAndKAPT4() {
         val source =
diff --git a/room/room-migration/bcv/native/current.txt b/room/room-migration/bcv/native/current.txt
index 9b3bd15..0b1314c 100644
--- a/room/room-migration/bcv/native/current.txt
+++ b/room/room-migration/bcv/native/current.txt
@@ -337,6 +337,9 @@
 }
 
 sealed class androidx.room.migration.bundle/BaseEntityBundle { // androidx.room.migration.bundle/BaseEntityBundle|null[0]
+    constructor <init>() // androidx.room.migration.bundle/BaseEntityBundle.<init>|<init>(){}[0]
+    constructor <init>(kotlin/Int, kotlinx.serialization.internal/SerializationConstructorMarker?) // androidx.room.migration.bundle/BaseEntityBundle.<init>|<init>(kotlin.Int;kotlinx.serialization.internal.SerializationConstructorMarker?){}[0]
+
     abstract val createSql // androidx.room.migration.bundle/BaseEntityBundle.createSql|{}createSql[0]
         abstract fun <get-createSql>(): kotlin/String // androidx.room.migration.bundle/BaseEntityBundle.createSql.<get-createSql>|<get-createSql>(){}[0]
     abstract val fields // androidx.room.migration.bundle/BaseEntityBundle.fields|{}fields[0]
diff --git a/settings.gradle b/settings.gradle
index 867951c..e9a417f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1011,6 +1011,7 @@
 includeProject(":wear:protolayout:protolayout-material", [BuildType.MAIN, BuildType.WEAR])
 includeProject(":wear:protolayout:protolayout-material-core", [BuildType.MAIN, BuildType.WEAR])
 includeProject(":wear:protolayout:protolayout-material3", [BuildType.MAIN, BuildType.WEAR])
+includeProject(":wear:protolayout:protolayout-material3-samples", "wear/protolayout/protolayout-material3/samples", [BuildType.MAIN, BuildType.WEAR])
 includeProject(":wear:protolayout:protolayout-proto", [BuildType.MAIN, BuildType.WEAR])
 includeProject(":wear:protolayout:protolayout-renderer", [BuildType.MAIN, BuildType.WEAR])
 includeProject(":wear:protolayout:protolayout-lint", [BuildType.MAIN, BuildType.WEAR])
diff --git a/wear/compose/compose-foundation/api/current.txt b/wear/compose/compose-foundation/api/current.txt
index 42ea898..5cc082e 100644
--- a/wear/compose/compose-foundation/api/current.txt
+++ b/wear/compose/compose-foundation/api/current.txt
@@ -326,6 +326,7 @@
     method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.compose.foundation.ScrollState state);
     method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.lazy.LazyColumnState state);
     method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.lazy.ScalingLazyListState state);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.pager.PagerState state, androidx.compose.foundation.gestures.Orientation orientation);
   }
 
   @kotlin.jvm.JvmInline public final value class SwipeDirection {
@@ -397,11 +398,12 @@
     method public androidx.compose.ui.Modifier transformedHeight(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress,java.lang.Integer> heightProvider);
   }
 
-  public sealed interface LazyColumnItemScrollProgress {
+  @kotlin.jvm.JvmInline public final value class LazyColumnItemScrollProgress {
+    ctor public LazyColumnItemScrollProgress(float topOffsetFraction, float bottomOffsetFraction);
     method public float getBottomOffsetFraction();
     method public float getTopOffsetFraction();
-    property public abstract float bottomOffsetFraction;
-    property public abstract float topOffsetFraction;
+    property public final float bottomOffsetFraction;
+    property public final float topOffsetFraction;
   }
 
   public final class LazyColumnKt {
@@ -448,13 +450,13 @@
     method public int getIndex();
     method public Object getKey();
     method public int getOffset();
-    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress getScrollProgress();
+    method public long getScrollProgress();
     property public abstract Object? contentType;
     property public abstract int height;
     property public abstract int index;
     property public abstract Object key;
     property public abstract int offset;
-    property public abstract androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress scrollProgress;
+    property public abstract long scrollProgress;
   }
 
   public final class ScalingLazyColumnDefaults {
@@ -595,14 +597,15 @@
 package androidx.wear.compose.foundation.pager {
 
   public final class PagerDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior(androidx.wear.compose.foundation.pager.PagerState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.TargetedFlingBehavior snapFlingBehavior(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.pager.PagerSnapDistance pagerSnapDistance, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional @FloatRange(from=0.0, to=1.0) float snapPositionalThreshold);
+    field public static final int BeyondViewportPageCount = 0; // 0x0
     field public static final androidx.wear.compose.foundation.pager.PagerDefaults INSTANCE;
     field public static final float SwipeToDismissEdgeZoneFraction = 0.15f;
   }
 
   public final class PagerKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void HorizontalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional @FloatRange(from=0.0, to=1.0) float swipeToDismissEdgeZoneFraction, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void VerticalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional @FloatRange(from=0.0, to=1.0) float swipeToDismissEdgeZoneFraction, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void VerticalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
   }
 
   public abstract class PagerState extends androidx.compose.foundation.pager.PagerState {
diff --git a/wear/compose/compose-foundation/api/restricted_current.txt b/wear/compose/compose-foundation/api/restricted_current.txt
index 42ea898..5cc082e 100644
--- a/wear/compose/compose-foundation/api/restricted_current.txt
+++ b/wear/compose/compose-foundation/api/restricted_current.txt
@@ -326,6 +326,7 @@
     method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.compose.foundation.ScrollState state);
     method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.lazy.LazyColumnState state);
     method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.lazy.ScalingLazyListState state);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.pager.PagerState state, androidx.compose.foundation.gestures.Orientation orientation);
   }
 
   @kotlin.jvm.JvmInline public final value class SwipeDirection {
@@ -397,11 +398,12 @@
     method public androidx.compose.ui.Modifier transformedHeight(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress,java.lang.Integer> heightProvider);
   }
 
-  public sealed interface LazyColumnItemScrollProgress {
+  @kotlin.jvm.JvmInline public final value class LazyColumnItemScrollProgress {
+    ctor public LazyColumnItemScrollProgress(float topOffsetFraction, float bottomOffsetFraction);
     method public float getBottomOffsetFraction();
     method public float getTopOffsetFraction();
-    property public abstract float bottomOffsetFraction;
-    property public abstract float topOffsetFraction;
+    property public final float bottomOffsetFraction;
+    property public final float topOffsetFraction;
   }
 
   public final class LazyColumnKt {
@@ -448,13 +450,13 @@
     method public int getIndex();
     method public Object getKey();
     method public int getOffset();
-    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress getScrollProgress();
+    method public long getScrollProgress();
     property public abstract Object? contentType;
     property public abstract int height;
     property public abstract int index;
     property public abstract Object key;
     property public abstract int offset;
-    property public abstract androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress scrollProgress;
+    property public abstract long scrollProgress;
   }
 
   public final class ScalingLazyColumnDefaults {
@@ -595,14 +597,15 @@
 package androidx.wear.compose.foundation.pager {
 
   public final class PagerDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior(androidx.wear.compose.foundation.pager.PagerState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.TargetedFlingBehavior snapFlingBehavior(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.pager.PagerSnapDistance pagerSnapDistance, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional @FloatRange(from=0.0, to=1.0) float snapPositionalThreshold);
+    field public static final int BeyondViewportPageCount = 0; // 0x0
     field public static final androidx.wear.compose.foundation.pager.PagerDefaults INSTANCE;
     field public static final float SwipeToDismissEdgeZoneFraction = 0.15f;
   }
 
   public final class PagerKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void HorizontalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional @FloatRange(from=0.0, to=1.0) float swipeToDismissEdgeZoneFraction, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void VerticalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional @FloatRange(from=0.0, to=1.0) float swipeToDismissEdgeZoneFraction, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void VerticalPager(androidx.wear.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional int beyondViewportPageCount, optional androidx.compose.foundation.gestures.TargetedFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
   }
 
   public abstract class PagerState extends androidx.compose.foundation.pager.PagerState {
diff --git a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/PagerSamples.kt b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/PagerSamples.kt
index 074cfe6..aa2d1cf 100644
--- a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/PagerSamples.kt
+++ b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/PagerSamples.kt
@@ -25,12 +25,10 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.TextStyle
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
 import androidx.wear.compose.foundation.pager.HorizontalPager
 import androidx.wear.compose.foundation.pager.VerticalPager
 import androidx.wear.compose.foundation.pager.rememberPagerState
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Sampled
 @Composable
 fun SimpleHorizontalPagerSample() {
@@ -45,7 +43,6 @@
     }
 }
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Sampled
 @Composable
 fun SimpleVerticalPagerSample() {
diff --git a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/lazy/LazyColumnItemScrollProgressTest.kt b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/lazy/LazyColumnItemScrollProgressTest.kt
new file mode 100644
index 0000000..54511d3
--- /dev/null
+++ b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/lazy/LazyColumnItemScrollProgressTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LazyColumnScrollProgressTest {
+    @Test
+    fun negativeValuesPackedCorrectly() {
+        val progress =
+            LazyColumnItemScrollProgress(topOffsetFraction = -0.5f, bottomOffsetFraction = 0.2f)
+        assertEquals(progress.topOffsetFraction, -0.5f, 1e-2f)
+        assertEquals(progress.bottomOffsetFraction, 0.2f, 1e-2f)
+    }
+
+    @Test
+    fun zeroValuesPackedCorrectly() {
+        val progress =
+            LazyColumnItemScrollProgress(topOffsetFraction = 0f, bottomOffsetFraction = 0f)
+        assertEquals(progress.topOffsetFraction, 0f, 1e-2f)
+        assertEquals(progress.bottomOffsetFraction, 0f, 1e-2f)
+    }
+
+    @Test
+    fun normalValuesPackedCorrectly() {
+        val progress =
+            LazyColumnItemScrollProgress(topOffsetFraction = 0.3f, bottomOffsetFraction = 0.7f)
+        assertEquals(progress.topOffsetFraction, 0.3f, 1e-2f)
+        assertEquals(progress.bottomOffsetFraction, 0.7f, 1e-2f)
+    }
+
+    @Test
+    fun nanValuesPackedCorrectly() {
+        val progress =
+            LazyColumnItemScrollProgress(
+                topOffsetFraction = Float.NaN,
+                bottomOffsetFraction = Float.NaN
+            )
+        assertEquals(progress.topOffsetFraction, Float.NaN)
+        assertEquals(progress.bottomOffsetFraction, Float.NaN)
+    }
+
+    @Test
+    fun infValuesPackedCorrectly() {
+        val progress =
+            LazyColumnItemScrollProgress(
+                topOffsetFraction = Float.NEGATIVE_INFINITY,
+                bottomOffsetFraction = Float.POSITIVE_INFINITY
+            )
+        assertEquals(progress.topOffsetFraction, Float.NEGATIVE_INFINITY)
+        assertEquals(progress.bottomOffsetFraction, Float.POSITIVE_INFINITY)
+    }
+}
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt
index 2fe51fe..8b16a45 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt
@@ -17,6 +17,7 @@
 package androidx.wear.compose.foundation
 
 import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.LazyListState
@@ -26,6 +27,10 @@
 import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
 import androidx.wear.compose.foundation.lazy.startOffset
+import androidx.wear.compose.foundation.pager.HorizontalPager
+import androidx.wear.compose.foundation.pager.PagerState
+import androidx.wear.compose.foundation.pager.VerticalPager
+import kotlin.math.absoluteValue
 
 /**
  * An interface for providing scroll information for different scrollable containers, such lists.
@@ -103,6 +108,24 @@
 fun ScrollInfoProvider(state: ScrollState): ScrollInfoProvider =
     ScrollStateScrollInfoProvider(state)
 
+/**
+ * Function for creating a [ScrollInfoProvider] from a [PagerState], for use with [HorizontalPager]
+ * and [VerticalPager]
+ * - used to coordinate when to fade out the PageIndicator and [TimeText]. The PageIndicator fades
+ *   out when when scrolling is finished and the screen is in an idle state. For [VerticalPager] the
+ *   [TimeText] is scrolled away as you page, for [HorizontalPager] the [TimeText] remains present
+ *   as you page.
+ *
+ * @param state the [PagerState] to use as the base for creating the [ScrollInfoProvider]
+ * @param orientation a parameter used to specify whether the Pager is Horizontal or Vertical
+ */
+fun ScrollInfoProvider(state: PagerState, orientation: Orientation): ScrollInfoProvider =
+    if (orientation == Orientation.Horizontal) {
+        HorizontalPagerStateScrollInfoProvider(state)
+    } else {
+        VerticalPagerStateScrollInfoProvider(state)
+    }
+
 // Implementation of [ScrollInfoProvider] for [ScalingLazyColumn].
 // Being in Foundation, this implementation has access to the ScalingLazyListState
 // auto-centering params, which are internal.
@@ -270,3 +293,47 @@
             "lastItemOffset=$lastItemOffset)"
     }
 }
+
+// Implementation of [ScrollInfoProvider] for [HorizontalPager].
+private class HorizontalPagerStateScrollInfoProvider(val state: PagerState) : ScrollInfoProvider {
+    override val isScrollAwayValid: Boolean
+        get() = false
+
+    override val isScrollable: Boolean
+        get() = state.canScrollBackward || state.canScrollForward
+
+    override val isScrollInProgress: Boolean
+        get() = state.isScrollInProgress
+
+    override val anchorItemOffset: Float
+        get() = Float.NaN
+
+    override val lastItemOffset: Float
+        get() = 0f
+}
+
+// Implementation of [ScrollInfoProvider] for [VerticalPager].
+private class VerticalPagerStateScrollInfoProvider(val state: PagerState) : ScrollInfoProvider {
+    override val isScrollAwayValid: Boolean
+        get() = state.pageCount > 1
+
+    override val isScrollable: Boolean
+        get() = state.canScrollBackward || state.canScrollForward
+
+    override val isScrollInProgress: Boolean
+        get() = state.isScrollInProgress
+
+    override val anchorItemOffset: Float
+        get() {
+            val offset =
+                when (state.currentPage) {
+                    0 -> state.currentPageOffsetFraction.absoluteValue
+                    1 -> 1 - state.currentPageOffsetFraction.absoluteValue
+                    else -> Float.NaN
+                }
+            return offset * state.layoutInfo.pageSize
+        }
+
+    override val lastItemOffset: Float
+        get() = 0f
+}
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnLayoutInfo.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnLayoutInfo.kt
index cf4df5a..573aa798 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnLayoutInfo.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnLayoutInfo.kt
@@ -17,29 +17,72 @@
 package androidx.wear.compose.foundation.lazy
 
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.util.packFloats
+import androidx.compose.ui.util.unpackFloat1
+import androidx.compose.ui.util.unpackFloat2
 
 /**
  * Scroll progress of an item in a [LazyColumn] before any modifications to the item's height are
  * applied (using [LazyColumnItemScope.transformedHeight] modifier).
  */
-sealed interface LazyColumnItemScrollProgress {
+@JvmInline
+value class LazyColumnItemScrollProgress internal constructor(private val packedValue: Long) {
     /**
-     * The offset as a fraction of the top of the item relative to the list container. Is within
-     * (0, 1) when item is inside the screen and could be negative if the top of the item is off the
-     * screen. Value is calculated from the top of the container. This value is calculated before
-     * any height modifications are applied (using [LazyColumnItemScope.transformedHeight]
-     * modifier).
+     * The top offset (between the top of the list container and the top of the item) as a fraction
+     * of the height of the list container. Is within (0, 1) when item is inside the screen and
+     * could be negative if the top of the item is off the screen. Value is calculated from the top
+     * of the container. This value is calculated before any height modifications are applied (using
+     * [LazyColumnItemScope.transformedHeight] modifier).
      */
     val topOffsetFraction: Float
+        get() = unpackFloat1(packedValue)
 
     /**
-     * The offset as a fraction of the bottom of the item relative to the list container. Is within
-     * (0, 1) when item is inside the screen and could exceed 1 when the bottom of item is off the
-     * screen. Value is calculated from the top of the container. This value is calculated before
-     * any height modifications are applied (using [LazyColumnItemScope.transformedHeight]
-     * modifier).
+     * The bottom offset (between the top of the list container and the bottom of the item) as a
+     * fraction of the height of the list container. Is within (0, 1) when item is inside the screen
+     * and could exceed 1 when the bottom of item is off the screen. Value is calculated from the
+     * top of the container. This value is calculated before any height modifications are applied
+     * (using [LazyColumnItemScope.transformedHeight] modifier).
      */
     val bottomOffsetFraction: Float
+        get() = unpackFloat2(packedValue)
+
+    /**
+     * Constructs a [LazyColumnItemScrollProgress] with two offset fraction [Float] values.
+     *
+     * @param topOffsetFraction The top offset (between the top of the list container and the top of
+     *   the item) as a fraction of the height of the list container.
+     * @param bottomOffsetFraction The bottom offset (between the top of the list container and the
+     *   bottom of the item) as a fraction of the height of the list container.
+     */
+    constructor(
+        topOffsetFraction: Float,
+        bottomOffsetFraction: Float
+    ) : this(packFloats(topOffsetFraction, bottomOffsetFraction))
+
+    internal companion object {
+        internal val Zero = LazyColumnItemScrollProgress(0f, 0f)
+
+        internal fun bottomItemScrollProgress(
+            offset: Int,
+            height: Int,
+            containerHeight: Int
+        ): LazyColumnItemScrollProgress =
+            LazyColumnItemScrollProgress(
+                topOffsetFraction = offset.toFloat() / containerHeight.toFloat(),
+                bottomOffsetFraction = (offset + height).toFloat() / containerHeight.toFloat(),
+            )
+
+        internal fun topItemScrollProgress(
+            offset: Int,
+            height: Int,
+            containerHeight: Int
+        ): LazyColumnItemScrollProgress =
+            LazyColumnItemScrollProgress(
+                topOffsetFraction = (offset - height).toFloat() / containerHeight.toFloat(),
+                bottomOffsetFraction = offset / containerHeight.toFloat(),
+            )
+    }
 }
 
 /** Represents an item that is visible in the [LazyColumn] component. */
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasuredItem.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasuredItem.kt
index f783788..47014ab 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasuredItem.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasuredItem.kt
@@ -20,6 +20,7 @@
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress.Companion.topItemScrollProgress
 
 /** Represents a placeable item in the [LazyColumn] layout. */
 internal data class LazyColumnMeasuredItem(
@@ -32,7 +33,7 @@
     /** The vertical offset of the item from the top of the list after transformations applied. */
     override var offset: Int,
     /** Scroll progress of the item used to calculate transformations applied. */
-    override val scrollProgress: LazyColumnItemScrollProgress,
+    override var scrollProgress: LazyColumnItemScrollProgress,
     /** The horizontal alignment to apply during placement. */
     val horizontalAlignment: Alignment.Horizontal,
     /** The [LayoutDirection] of the `Layout`. */
@@ -40,11 +41,27 @@
     override val key: Any,
     override val contentType: Any?,
 ) : LazyColumnVisibleItemInfo {
+    private var lastMeasuredHeight = Int.MIN_VALUE
+    private var lastMeasuredOffset = Int.MIN_VALUE
+    private var lastMeasuredScrollProgress = LazyColumnItemScrollProgress.Zero
+
     /** The height of the item after transformations applied. */
-    override val height =
-        (placeable.parentData as? HeightProviderParentData)?.let {
-            it.heightProvider(placeable.height, scrollProgress)
-        } ?: placeable.height
+    override val height: Int
+        get() {
+            if (
+                lastMeasuredHeight == Int.MIN_VALUE ||
+                    lastMeasuredOffset != offset ||
+                    lastMeasuredScrollProgress != scrollProgress
+            ) {
+                lastMeasuredHeight =
+                    (placeable.parentData as? HeightProviderParentData)?.let {
+                        it.heightProvider(placeable.height, scrollProgress)
+                    } ?: placeable.height
+                lastMeasuredOffset = offset
+                lastMeasuredScrollProgress = scrollProgress
+            }
+            return lastMeasuredHeight
+        }
 
     fun place(scope: Placeable.PlacementScope) =
         with(scope) {
@@ -57,4 +74,14 @@
                 offset
             )
         }
+
+    fun pinToCenter() {
+        scrollProgress =
+            topItemScrollProgress(
+                containerConstraints.maxHeight / 2 - height / 2,
+                placeable.height,
+                containerConstraints.maxHeight
+            )
+        offset = containerConstraints.maxHeight / 2 - height / 2
+    }
 }
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasurement.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasurement.kt
index c3c64f1..e5fff610 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasurement.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasurement.kt
@@ -29,6 +29,8 @@
 import androidx.compose.ui.unit.constrainHeight
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.util.fastForEach
+import androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress.Companion.bottomItemScrollProgress
+import androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress.Companion.topItemScrollProgress
 import kotlin.math.abs
 import kotlin.math.roundToInt
 
@@ -111,14 +113,14 @@
             centerItem.offset + centerItem.height / 2 >= containerConstraints.maxHeight / 2
     ) {
         canScrollBackward = false
-        centerItem.offset = containerConstraints.maxHeight / 2 - centerItem.height / 2
+        centerItem.pinToCenter()
     }
     if (
         centerItem.index == itemsCount - 1 &&
             centerItem.offset + centerItem.height / 2 <= containerConstraints.maxHeight / 2
     ) {
         canScrollForward = false
-        centerItem.offset = containerConstraints.maxHeight / 2 - centerItem.height / 2
+        centerItem.pinToCenter()
     }
 
     visibleItems.add(centerItem)
@@ -177,31 +179,6 @@
     )
 }
 
-private fun bottomItemScrollProgress(
-    offset: Int,
-    height: Int,
-    containerHeight: Int
-): LazyColumnItemScrollProgress =
-    LazyColumnItemScrollProgressImpl(
-        topOffsetFraction = offset.toFloat() / containerHeight.toFloat(),
-        bottomOffsetFraction = (offset + height).toFloat() / containerHeight.toFloat(),
-    )
-
-private fun topItemScrollProgress(
-    offset: Int,
-    height: Int,
-    containerHeight: Int
-): LazyColumnItemScrollProgress =
-    LazyColumnItemScrollProgressImpl(
-        topOffsetFraction = (offset - height).toFloat() / containerHeight.toFloat(),
-        bottomOffsetFraction = offset / containerHeight.toFloat(),
-    )
-
-internal data class LazyColumnItemScrollProgressImpl(
-    override val topOffsetFraction: Float,
-    override val bottomOffsetFraction: Float
-) : LazyColumnItemScrollProgress
-
 @Composable
 @OptIn(ExperimentalFoundationApi::class)
 internal fun rememberLazyColumnMeasurePolicy(
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/pager/Pager.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/pager/Pager.kt
index 019f683..680f343 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/pager/Pager.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/pager/Pager.kt
@@ -17,8 +17,10 @@
 package androidx.wear.compose.foundation.pager
 
 import androidx.annotation.FloatRange
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.DecayAnimationSpec
 import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.animation.rememberSplineBasedDecay
 import androidx.compose.foundation.gestures.TargetedFlingBehavior
 import androidx.compose.foundation.gestures.awaitEachGesture
 import androidx.compose.foundation.gestures.awaitFirstDown
@@ -49,7 +51,6 @@
 import androidx.compose.ui.semantics.horizontalScrollAxisRange
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
 import androidx.wear.compose.foundation.HierarchicalFocusCoordinator
 import kotlinx.coroutines.coroutineScope
 
@@ -90,14 +91,13 @@
  * @param content A composable function that defines the content of each page displayed by the
  *   Pager. This is where the UI elements that should appear within each page should be placed.
  */
-@ExperimentalWearFoundationApi
 @Composable
 fun HorizontalPager(
     state: PagerState,
     modifier: Modifier = Modifier,
     contentPadding: PaddingValues = PaddingValues(0.dp),
-    beyondViewportPageCount: Int = ComposePagerDefaults.BeyondViewportPageCount,
-    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
+    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
+    flingBehavior: TargetedFlingBehavior = PagerDefaults.snapFlingBehavior(state = state),
     userScrollEnabled: Boolean = true,
     reverseLayout: Boolean = false,
     key: ((index: Int) -> Any)? = null,
@@ -153,8 +153,6 @@
             userScrollEnabled = userScrollEnabled && allowPaging,
             reverseLayout = reverseLayout,
             key = key,
-            pageNestedScrollConnection =
-                ComposePagerDefaults.pageNestedScrollConnection(state, Orientation.Horizontal),
             snapPosition = SnapPosition.Start,
         ) { page ->
             CustomTouchSlopProvider(newTouchSlop = originalTouchSlop) {
@@ -195,14 +193,13 @@
  * @param content A composable function that defines the content of each page displayed by the
  *   Pager. This is where the UI elements that should appear within each page should be placed.
  */
-@ExperimentalWearFoundationApi
 @Composable
 fun VerticalPager(
     state: PagerState,
     modifier: Modifier = Modifier,
     contentPadding: PaddingValues = PaddingValues(0.dp),
-    beyondViewportPageCount: Int = ComposePagerDefaults.BeyondViewportPageCount,
-    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
+    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
+    flingBehavior: TargetedFlingBehavior = PagerDefaults.snapFlingBehavior(state = state),
     userScrollEnabled: Boolean = true,
     reverseLayout: Boolean = false,
     key: ((index: Int) -> Any)? = null,
@@ -220,29 +217,68 @@
         userScrollEnabled = userScrollEnabled,
         reverseLayout = reverseLayout,
         key = key,
-        pageNestedScrollConnection =
-            ComposePagerDefaults.pageNestedScrollConnection(state, Orientation.Vertical),
         snapPosition = SnapPosition.Start,
     ) { page ->
         FocusedPageContent(page = page, pagerState = state, content = { content(page) })
     }
 }
 
-/** Convenience fling behavior optimised for Wear. */
+/** Contains the default values used by [Pager]. These are optimised for Wear. */
 object PagerDefaults {
+    /**
+     * Default fling behavior for pagers on Wear, snaps at most one page at a time.
+     *
+     * @param state The [PagerState] that controls the [Pager] to which this FlingBehavior will be
+     *   applied to.
+     * @param pagerSnapDistance A way to control the snapping destination for this [Pager]. Use
+     *   [PagerSnapDistance.atMost] to define a maximum number of pages this [Pager] is allowed to
+     *   fling after scrolling is finished and fling has started.
+     * @param decayAnimationSpec The animation spec used to approach the target offset. When the
+     *   fling velocity is large enough. Large enough means large enough to naturally decay. For
+     *   single page snapping this usually never happens since there won't be enough space to run a
+     *   decay animation.
+     * @param snapAnimationSpec The animation spec used to finally snap to the position. This
+     *   animation will be often used in 2 cases: 1) There was enough space to an approach
+     *   animation, the Pager will use [snapAnimationSpec] in the last step of the animation to
+     *   settle the page into position. 2) There was not enough space to run the approach animation.
+     * @param snapPositionalThreshold If the fling has a low velocity (e.g. slow scroll), this fling
+     *   behavior will use this snap threshold in order to determine if the pager should snap back
+     *   or move forward. Use a number between 0 and 1 as a fraction of the page size that needs to
+     *   be scrolled before the Pager considers it should move to the next page. For instance, if
+     *   snapPositionalThreshold = 0.35, it means if this pager is scrolled with a slow velocity and
+     *   the Pager scrolls more than 35% of the page size, then will jump to the next page, if not
+     *   it scrolls back. Note that any fling that has high enough velocity will *always* move to
+     *   the next page in the direction of the fling.
+     */
     @Composable
-    fun flingBehavior(
+    fun snapFlingBehavior(
         state: PagerState,
+        pagerSnapDistance: PagerSnapDistance = PagerSnapDistance.atMost(1),
+        decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
+        snapAnimationSpec: AnimationSpec<Float> = tween(150, 0),
+        @FloatRange(from = 0.0, to = 1.0) snapPositionalThreshold: Float = 0.5f
     ): TargetedFlingBehavior {
         return ComposePagerDefaults.flingBehavior(
             state = state,
-            pagerSnapDistance = PagerSnapDistance.atMost(1),
-            snapAnimationSpec = tween(150, 0),
+            pagerSnapDistance = pagerSnapDistance,
+            decayAnimationSpec = decayAnimationSpec,
+            snapAnimationSpec = snapAnimationSpec,
+            snapPositionalThreshold = snapPositionalThreshold,
         )
     }
 
-    /** Configure Swipe To Dismiss behaviour, only applies to [HorizontalPager]. */
+    /**
+     * The default value of swipeToDismissEdgeZoneFraction used to configure the size of the edge
+     * zone in a [HorizontalPager].
+     */
     const val SwipeToDismissEdgeZoneFraction: Float = 0.15f
+
+    /**
+     * The default value of beyondViewportPageCount used to specify the number of pages to compose
+     * and layout before and after the visible pages. It does not include the pages automatically
+     * composed and laid out by the pre-fetcher in the direction of the scroll during scroll events.
+     */
+    const val BeyondViewportPageCount = 0
 }
 
 @Composable
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 3a4d82c..17e1f37 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -628,16 +628,20 @@
   }
 
   public final class ListHeaderDefaults {
-    method public androidx.compose.foundation.layout.PaddingValues getHeaderContentPadding();
-    method public androidx.compose.foundation.layout.PaddingValues getSubheaderContentPadding();
-    property public final androidx.compose.foundation.layout.PaddingValues HeaderContentPadding;
-    property public final androidx.compose.foundation.layout.PaddingValues SubheaderContentPadding;
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public long getSubHeaderContentColor();
+    method public androidx.compose.foundation.layout.PaddingValues getSubHeaderContentPadding();
+    property @androidx.compose.runtime.Composable public final long ContentColor;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues SubHeaderContentPadding;
+    property @androidx.compose.runtime.Composable public final long subHeaderContentColor;
     field public static final androidx.wear.compose.material3.ListHeaderDefaults INSTANCE;
   }
 
   public final class ListHeaderKt {
     method @androidx.compose.runtime.Composable public static void ListHeader(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void ListSubheader(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void ListSubHeader(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
   }
 
   public final class MaterialTheme {
@@ -663,17 +667,12 @@
     method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> fastSpatialSpec();
     method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowEffectsSpec();
     method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowSpatialSpec();
+    field public static final androidx.wear.compose.material3.MotionScheme.Companion Companion;
   }
 
-  public final class MotionSchemeKt {
-    method public static androidx.wear.compose.material3.MotionScheme expressiveMotionScheme();
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultEffectsSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultSpatialSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastEffectsSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastSpatialSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowEffectsSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowSpatialSpec(androidx.wear.compose.material3.MotionScheme);
-    method public static androidx.wear.compose.material3.MotionScheme standardMotionScheme();
+  public static final class MotionScheme.Companion {
+    method public androidx.wear.compose.material3.MotionScheme expressive();
+    method public androidx.wear.compose.material3.MotionScheme standard();
   }
 
   public final class OpenOnPhoneDialogColors {
@@ -719,6 +718,17 @@
     method @androidx.compose.runtime.Composable public static void VerticalPageIndicator(androidx.wear.compose.foundation.pager.PagerState pagerState, optional androidx.compose.ui.Modifier modifier, optional long selectedColor, optional long unselectedColor, optional long backgroundColor);
   }
 
+  public final class PagerScaffoldDefaults {
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getFadeOutAnimation();
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> FadeOutAnimation;
+    field public static final androidx.wear.compose.material3.PagerScaffoldDefaults INSTANCE;
+  }
+
+  public final class PagerScaffoldKt {
+    method @androidx.compose.runtime.Composable public static void HorizontalPagerScaffold(androidx.wear.compose.foundation.pager.PagerState pagerState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? pageIndicator, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? pageIndicatorAnimationSpec, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void VerticalPagerScaffold(androidx.wear.compose.foundation.pager.PagerState pagerState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? pageIndicator, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? pageIndicatorAnimationSpec, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+  }
+
   public final class PickerDefaults {
     method public float getGradientRatio();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.foundation.rotary.RotaryScrollableBehavior rotarySnapBehavior(androidx.wear.compose.material3.PickerState state);
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 3a4d82c..17e1f37 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -628,16 +628,20 @@
   }
 
   public final class ListHeaderDefaults {
-    method public androidx.compose.foundation.layout.PaddingValues getHeaderContentPadding();
-    method public androidx.compose.foundation.layout.PaddingValues getSubheaderContentPadding();
-    property public final androidx.compose.foundation.layout.PaddingValues HeaderContentPadding;
-    property public final androidx.compose.foundation.layout.PaddingValues SubheaderContentPadding;
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public long getSubHeaderContentColor();
+    method public androidx.compose.foundation.layout.PaddingValues getSubHeaderContentPadding();
+    property @androidx.compose.runtime.Composable public final long ContentColor;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues SubHeaderContentPadding;
+    property @androidx.compose.runtime.Composable public final long subHeaderContentColor;
     field public static final androidx.wear.compose.material3.ListHeaderDefaults INSTANCE;
   }
 
   public final class ListHeaderKt {
     method @androidx.compose.runtime.Composable public static void ListHeader(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void ListSubheader(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
+    method @androidx.compose.runtime.Composable public static void ListSubHeader(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? icon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label);
   }
 
   public final class MaterialTheme {
@@ -663,17 +667,12 @@
     method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> fastSpatialSpec();
     method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowEffectsSpec();
     method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowSpatialSpec();
+    field public static final androidx.wear.compose.material3.MotionScheme.Companion Companion;
   }
 
-  public final class MotionSchemeKt {
-    method public static androidx.wear.compose.material3.MotionScheme expressiveMotionScheme();
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultEffectsSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultSpatialSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastEffectsSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastSpatialSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowEffectsSpec(androidx.wear.compose.material3.MotionScheme);
-    method @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowSpatialSpec(androidx.wear.compose.material3.MotionScheme);
-    method public static androidx.wear.compose.material3.MotionScheme standardMotionScheme();
+  public static final class MotionScheme.Companion {
+    method public androidx.wear.compose.material3.MotionScheme expressive();
+    method public androidx.wear.compose.material3.MotionScheme standard();
   }
 
   public final class OpenOnPhoneDialogColors {
@@ -719,6 +718,17 @@
     method @androidx.compose.runtime.Composable public static void VerticalPageIndicator(androidx.wear.compose.foundation.pager.PagerState pagerState, optional androidx.compose.ui.Modifier modifier, optional long selectedColor, optional long unselectedColor, optional long backgroundColor);
   }
 
+  public final class PagerScaffoldDefaults {
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getFadeOutAnimation();
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> FadeOutAnimation;
+    field public static final androidx.wear.compose.material3.PagerScaffoldDefaults INSTANCE;
+  }
+
+  public final class PagerScaffoldKt {
+    method @androidx.compose.runtime.Composable public static void HorizontalPagerScaffold(androidx.wear.compose.foundation.pager.PagerState pagerState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? pageIndicator, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? pageIndicatorAnimationSpec, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void VerticalPagerScaffold(androidx.wear.compose.foundation.pager.PagerState pagerState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? pageIndicator, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? pageIndicatorAnimationSpec, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
+  }
+
   public final class PickerDefaults {
     method public float getGradientRatio();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.foundation.rotary.RotaryScrollableBehavior rotarySnapBehavior(androidx.wear.compose.material3.PickerState state);
diff --git a/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ListHeaderBenchmark.kt b/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ListHeaderBenchmark.kt
index c0795a3..43bb317 100644
--- a/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ListHeaderBenchmark.kt
+++ b/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ListHeaderBenchmark.kt
@@ -22,7 +22,7 @@
 import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
 import androidx.test.filters.LargeTest
 import androidx.wear.compose.material3.ListHeader
-import androidx.wear.compose.material3.ListSubheader
+import androidx.wear.compose.material3.ListSubHeader
 import androidx.wear.compose.material3.MaterialTheme
 import androidx.wear.compose.material3.Text
 import org.junit.Rule
@@ -55,7 +55,7 @@
     override fun MeasuredContent() {
         when (type) {
             ListHeaderType.ListHeader -> ListHeader { Text("Header") }
-            ListHeaderType.ListSubheader -> ListSubheader { Text("Subheader") }
+            ListHeaderType.ListSubheader -> ListSubHeader { Text("SubHeader") }
         }
     }
 
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
index e5e8942..fb76b908 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
@@ -35,7 +35,7 @@
 import androidx.wear.compose.material3.CompactButton
 import androidx.wear.compose.material3.FilledTonalButton
 import androidx.wear.compose.material3.ListHeader
-import androidx.wear.compose.material3.ListSubheader
+import androidx.wear.compose.material3.ListSubHeader
 import androidx.wear.compose.material3.OutlinedButton
 import androidx.wear.compose.material3.Text
 import androidx.wear.compose.material3.samples.ButtonExtraLargeIconSample
@@ -62,9 +62,9 @@
     // to the content slot.
     ScalingLazyDemo {
         item { ListHeader { Text("Base Button") } }
-        item { ListSubheader { Text("Default alignment") } }
+        item { ListSubHeader { Text("Default alignment") } }
         item { Button(onClick = {}, modifier = Modifier.fillMaxWidth()) { Text("Base Button") } }
-        item { ListSubheader { Text("Top Alignment") } }
+        item { ListSubHeader { Text("Top Alignment") } }
         item {
             Button(onClick = {}, modifier = Modifier.fillMaxWidth()) {
                 Text("Base Button", modifier = Modifier.align(Alignment.Top))
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt
index 6cdcf30..4134ebb 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt
@@ -18,14 +18,14 @@
 
 import androidx.compose.runtime.Composable
 import androidx.wear.compose.material3.samples.ListHeaderSample
-import androidx.wear.compose.material3.samples.ListSubheaderSample
-import androidx.wear.compose.material3.samples.ListSubheaderWithIconSample
+import androidx.wear.compose.material3.samples.ListSubHeaderSample
+import androidx.wear.compose.material3.samples.ListSubHeaderWithIconSample
 
 @Composable
 fun ListHeaderDemo() {
     ScalingLazyDemo {
         item { ListHeaderSample() }
-        item { ListSubheaderSample() }
-        item { ListSubheaderWithIconSample() }
+        item { ListSubHeaderSample() }
+        item { ListSubHeaderWithIconSample() }
     }
 }
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/RobotoFlexTypography.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/RobotoFlexTypography.kt
new file mode 100644
index 0000000..a79a509
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/RobotoFlexTypography.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
+package androidx.wear.compose.material3.demos
+
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontVariation
+import androidx.compose.ui.text.font.toFontFamily
+import androidx.wear.compose.material3.Typography
+import androidx.wear.compose.material3.tokens.TypographyVariableFontsTokens
+
+@OptIn(ExperimentalTextApi::class)
+fun createRobotoFlexTextStyle(variationSettings: FontVariation.Settings) =
+    TextStyle(
+        fontFamily =
+            Font(R.font.robotoflex_variable, variationSettings = variationSettings).toFontFamily()
+    )
+
+val RobotoFlexTypography =
+    Typography(
+        arcLarge =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.ArcLargeVariationSettings),
+        arcMedium =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.ArcMediumVariationSettings),
+        arcSmall =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.ArcSmallVariationSettings),
+        bodyExtraSmall =
+            createRobotoFlexTextStyle(
+                TypographyVariableFontsTokens.BodyExtraSmallVariationSettings
+            ),
+        bodySmall =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.BodySmallVariationSettings),
+        bodyMedium =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.BodyMediumVariationSettings),
+        bodyLarge =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.BodyLargeVariationSettings),
+        displaySmall =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.DisplaySmallVariationSettings),
+        displayMedium =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.DisplayMediumVariationSettings),
+        displayLarge =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.DisplayLargeVariationSettings),
+        labelSmall =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.LabelSmallVariationSettings),
+        labelMedium =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.LabelMediumVariationSettings),
+        labelLarge =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.LabelLargeVariationSettings),
+        titleSmall =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.TitleSmallVariationSettings),
+        titleMedium =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.TitleMediumVariationSettings),
+        titleLarge =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.TitleLargeVariationSettings),
+        numeralExtraSmall =
+            createRobotoFlexTextStyle(
+                TypographyVariableFontsTokens.NumeralExtraSmallVariationSettings
+            ),
+        numeralSmall =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.NumeralSmallVariationSettings),
+        numeralMedium =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.NumeralMediumVariationSettings),
+        numeralLarge =
+            createRobotoFlexTextStyle(TypographyVariableFontsTokens.NumeralLargeVariationSettings),
+        numeralExtraLarge =
+            createRobotoFlexTextStyle(
+                TypographyVariableFontsTokens.NumeralExtraLargeVariationSettings
+            ),
+    )
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScaffoldDemos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScaffoldDemos.kt
new file mode 100644
index 0000000..1aec2b4
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScaffoldDemos.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.foundation.pager.rememberPagerState
+import androidx.wear.compose.material3.AppScaffold
+import androidx.wear.compose.material3.HorizontalPagerScaffold
+import androidx.wear.compose.material3.PagerScaffoldDefaults
+import androidx.wear.compose.material3.ScreenScaffold
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.VerticalPagerScaffold
+
+@Composable
+fun HorizontalPagerScaffoldFadeOutIndicatorDemo() {
+    AppScaffold {
+        val pagerState = rememberPagerState(pageCount = { 10 })
+
+        HorizontalPagerScaffold(
+            pagerState = pagerState,
+            pageIndicatorAnimationSpec = PagerScaffoldDefaults.FadeOutAnimation
+        ) { page ->
+            ScreenScaffold {
+                Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                    Text("Page $page")
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun VerticalPagerScaffoldFadeOutIndicatorDemo() {
+    AppScaffold {
+        val pagerState = rememberPagerState(pageCount = { 10 })
+
+        VerticalPagerScaffold(
+            pagerState = pagerState,
+            pageIndicatorAnimationSpec = PagerScaffoldDefaults.FadeOutAnimation
+        ) { page ->
+            ScreenScaffold {
+                Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                    Text("Page $page")
+                }
+            }
+        }
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index 395cf6e..f0baac8 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -31,6 +31,7 @@
 import androidx.wear.compose.material3.samples.EdgeSwipeForSwipeToDismiss
 import androidx.wear.compose.material3.samples.FixedFontSize
 import androidx.wear.compose.material3.samples.HorizontalPageIndicatorWithPagerSample
+import androidx.wear.compose.material3.samples.HorizontalPagerScaffoldSample
 import androidx.wear.compose.material3.samples.LazyColumnScalingMorphingEffectSample
 import androidx.wear.compose.material3.samples.LazyColumnTargetMorphingHeightSample
 import androidx.wear.compose.material3.samples.ScaffoldSample
@@ -40,159 +41,180 @@
 import androidx.wear.compose.material3.samples.SwipeToRevealSample
 import androidx.wear.compose.material3.samples.SwipeToRevealSingleActionCardSample
 import androidx.wear.compose.material3.samples.VerticalPageIndicatorWithPagerSample
+import androidx.wear.compose.material3.samples.VerticalPagerScaffoldSample
 
 val WearMaterial3Demos =
     Material3DemoCategory(
         "Material 3",
         listOf(
-            ComposableDemo("Haptics") { Centralize { HapticsDemos() } },
-            Material3DemoCategory(title = "Typography", TypographyDemos),
-            Material3DemoCategory(
-                "Button",
-                listOf(
-                    ComposableDemo("Base Button") { BaseButtonDemo() },
-                    ComposableDemo("Filled Button") { ButtonDemo() },
-                    ComposableDemo("Filled Tonal Button") { FilledTonalButtonDemo() },
-                    ComposableDemo("Filled Variant Button") { FilledVariantButtonDemo() },
-                    ComposableDemo("Outlined Button") { OutlinedButtonDemo() },
-                    ComposableDemo("Child Button") { ChildButtonDemo() },
-                    ComposableDemo("Multiline Button") { MultilineButtonDemo() },
-                    ComposableDemo("App Button") { AppButtonDemo() },
-                    ComposableDemo("Avatar Button") { AvatarButtonDemo() },
-                    ComposableDemo("Button (Image Background)") { ButtonBackgroundImageDemo() },
-                )
-            ),
-            ComposableDemo("Color Scheme") { ColorSchemeDemos() },
-            ComposableDemo("Dynamic Color Scheme") { DynamicColorSchemeDemos() },
-            Material3DemoCategory("Curved Text", CurvedTextDemos),
-            Material3DemoCategory("Alert Dialog", AlertDialogs),
-            Material3DemoCategory("Confirmation", Comfirmations),
-            Material3DemoCategory("Open on phone Dialog", OpenOnPhoneDialogDemos),
-            ComposableDemo("Scaffold") { ScaffoldSample() },
-            Material3DemoCategory("ScrollAway", ScrollAwayDemos),
-            ComposableDemo("Compact Button") { CompactButtonDemo() },
-            ComposableDemo("Icon Button") { IconButtonDemo() },
-            ComposableDemo("Image Button") { ImageButtonDemo() },
-            ComposableDemo("Text Button") { TextButtonDemo() },
-            Material3DemoCategory(
-                "Edge Button",
-                listOf(
-                    ComposableDemo("Simple Edge Button") { EdgeButtonSample() },
-                    ComposableDemo("Sizes and Colors") { EdgeButtonMultiDemo() },
-                    ComposableDemo("Configurable") { EdgeButtonConfigurableDemo() },
-                    ComposableDemo("Simple Edge Button below SLC") { EdgeButtonListSample() },
-                    ComposableDemo("Edge Button Below LC") { EdgeButtonBelowLazyColumnDemo() },
-                    ComposableDemo("Edge Button Below SLC") {
-                        EdgeButtonBelowScalingLazyColumnDemo()
-                    },
-                )
-            ),
-            Material3DemoCategory(
-                "Button Group",
-                listOf(
-                    ComposableDemo("Two buttons") { ButtonGroupSample() },
-                    ComposableDemo("Three buttons") { ButtonGroupDemo() },
-                )
-            ),
-            ComposableDemo("List Header") { Centralize { ListHeaderDemo() } },
-            Material3DemoCategory("Time Text", TimeTextDemos),
-            ComposableDemo("Card") { CardDemo() },
-            ComposableDemo("Animated Shape Buttons") { AnimatedShapeButtonDemo() },
-            ComposableDemo("Animated Shape Toggle Buttons") { AnimatedShapeToggleButtonDemo() },
-            ComposableDemo("Text Toggle Button") { TextToggleButtonDemo() },
-            ComposableDemo("Icon Toggle Button") { IconToggleButtonDemo() },
-            ComposableDemo("Checkbox Button") { CheckboxButtonDemo() },
-            ComposableDemo("Split Checkbox Button") { SplitCheckboxButtonDemo() },
-            ComposableDemo("Radio Button") { RadioButtonDemo() },
-            ComposableDemo("Split Radio Button") { SplitRadioButtonDemo() },
-            ComposableDemo("Switch Button") { SwitchButtonDemo() },
-            ComposableDemo("Split Switch Button") { SplitSwitchButtonDemo() },
-            Material3DemoCategory("Stepper", StepperDemos),
-            Material3DemoCategory("Slider", SliderDemos),
-            Material3DemoCategory("Picker", PickerDemos),
-            // Requires API level 26 or higher due to java.time dependency.
-            *(if (Build.VERSION.SDK_INT >= 26)
-                arrayOf(
-                    Material3DemoCategory("TimePicker", TimePickerDemos),
-                    Material3DemoCategory("DatePicker", DatePickerDemos)
-                )
-            else emptyArray<Material3DemoCategory>()),
-            Material3DemoCategory("Progress Indicator", ProgressIndicatorDemos),
-            Material3DemoCategory("Scroll Indicator", ScrollIndicatorDemos),
-            Material3DemoCategory("Placeholder", PlaceholderDemos),
-            ComposableDemo(title = "Fixed Font Size") { Centralize { FixedFontSize() } },
-            Material3DemoCategory(
-                title = "Swipe To Dismiss",
-                listOf(
-                    ComposableDemo("Simple") { SimpleSwipeToDismissBox(it.navigateBack) },
-                    ComposableDemo("Stateful") { StatefulSwipeToDismissBox() },
-                    ComposableDemo("Edge swipe") { EdgeSwipeForSwipeToDismiss(it.navigateBack) },
-                )
-            ),
-            Material3DemoCategory(
-                title = "Page Indicator",
-                listOf(
-                    ComposableDemo("HorizontalPageIndicator") {
-                        HorizontalPageIndicatorWithPagerSample()
-                    },
-                    ComposableDemo("VerticalPageIndicator") {
-                        VerticalPageIndicatorWithPagerSample()
-                    },
-                )
-            ),
-            Material3DemoCategory(
-                title = "Swipe to Reveal",
-                listOf(
-                    ComposableDemo("Bi-directional / Non-anchoring") {
-                        Centralize { SwipeToRevealBothDirectionsNonAnchoring() }
-                    },
-                    ComposableDemo("Bi-directional Two Actions") {
-                        Centralize { SwipeToRevealBothDirections() }
-                    },
-                    ComposableDemo("Two Actions") { Centralize { SwipeToRevealSample() } },
-                    ComposableDemo("Two Undo Actions") {
-                        Centralize { SwipeToRevealTwoActionsWithUndo() }
-                    },
-                    ComposableDemo("Single action with Card") {
-                        Centralize { SwipeToRevealSingleActionCardSample() }
-                    },
-                    ComposableDemo("In a list") { Centralize { SwipeToRevealInList() } },
-                    ComposableDemo("Non-anchoring") {
-                        Centralize { SwipeToRevealNonAnchoredSample() }
-                    }
-                )
-            ),
-            Material3DemoCategory(title = "Typography", TypographyDemos),
-            Material3DemoCategory(
-                "Animated Text",
-                if (Build.VERSION.SDK_INT > 31) {
+                ComposableDemo("Haptics") { Centralize { HapticsDemos() } },
+                Material3DemoCategory(title = "Typography", TypographyDemos),
+                Material3DemoCategory(
+                    "Button",
                     listOf(
-                        ComposableDemo("Simple animation") { Centralize { AnimatedTextSample() } },
-                        ComposableDemo("Animation with button click") {
-                            Centralize { AnimatedTextSampleButtonResponse() }
+                        ComposableDemo("Base Button") { BaseButtonDemo() },
+                        ComposableDemo("Filled Button") { ButtonDemo() },
+                        ComposableDemo("Filled Tonal Button") { FilledTonalButtonDemo() },
+                        ComposableDemo("Filled Variant Button") { FilledVariantButtonDemo() },
+                        ComposableDemo("Outlined Button") { OutlinedButtonDemo() },
+                        ComposableDemo("Child Button") { ChildButtonDemo() },
+                        ComposableDemo("Multiline Button") { MultilineButtonDemo() },
+                        ComposableDemo("App Button") { AppButtonDemo() },
+                        ComposableDemo("Avatar Button") { AvatarButtonDemo() },
+                        ComposableDemo("Button (Image Background)") { ButtonBackgroundImageDemo() },
+                    )
+                ),
+                ComposableDemo("Color Scheme") { ColorSchemeDemos() },
+                ComposableDemo("Dynamic Color Scheme") { DynamicColorSchemeDemos() },
+                Material3DemoCategory("Curved Text", CurvedTextDemos),
+                Material3DemoCategory("Alert Dialog", AlertDialogs),
+                Material3DemoCategory("Confirmation", Comfirmations),
+                Material3DemoCategory("Open on phone Dialog", OpenOnPhoneDialogDemos),
+                Material3DemoCategory(
+                    "Scaffold",
+                    listOf(
+                        ComposableDemo("Screen Scaffold") { ScaffoldSample() },
+                        ComposableDemo("Horizontal Pager Scaffold") {
+                            HorizontalPagerScaffoldSample()
                         },
-                        ComposableDemo("Shared Font Registry") {
-                            Centralize { AnimatedTextSampleSharedFontRegistry() }
+                        ComposableDemo("Horizontal Pager Scaffold (Fade Out Indicator)") {
+                            HorizontalPagerScaffoldFadeOutIndicatorDemo()
+                        },
+                        ComposableDemo("Vertical Pager Scaffold") { VerticalPagerScaffoldSample() },
+                        ComposableDemo("Vertical Pager Scaffold (Fade Out Indicator)") {
+                            VerticalPagerScaffoldFadeOutIndicatorDemo()
                         },
                     )
-                } else {
-                    emptyList()
-                }
-            ),
-            ComposableDemo("Settings Demo") { SettingsDemo() },
-            Material3DemoCategory(
-                title = "LazyColumn",
-                listOf(
-                    ComposableDemo("Notifications") { LazyColumnNotificationsDemo() },
-                    ComposableDemo("Scaling Morphing Effect Sample") {
-                        LazyColumnScalingMorphingEffectSample()
-                    },
-                    ComposableDemo("Target Morphing Height Sample") {
-                        LazyColumnTargetMorphingHeightSample()
+                ),
+                Material3DemoCategory("ScrollAway", ScrollAwayDemos),
+                ComposableDemo("Compact Button") { CompactButtonDemo() },
+                ComposableDemo("Icon Button") { IconButtonDemo() },
+                ComposableDemo("Image Button") { ImageButtonDemo() },
+                ComposableDemo("Text Button") { TextButtonDemo() },
+                Material3DemoCategory(
+                    "Edge Button",
+                    listOf(
+                        ComposableDemo("Simple Edge Button") { EdgeButtonSample() },
+                        ComposableDemo("Sizes and Colors") { EdgeButtonMultiDemo() },
+                        ComposableDemo("Configurable") { EdgeButtonConfigurableDemo() },
+                        ComposableDemo("Simple Edge Button below SLC") { EdgeButtonListSample() },
+                        ComposableDemo("Edge Button Below LC") { EdgeButtonBelowLazyColumnDemo() },
+                        ComposableDemo("Edge Button Below SLC") {
+                            EdgeButtonBelowScalingLazyColumnDemo()
+                        },
+                    )
+                ),
+                Material3DemoCategory(
+                    "Button Group",
+                    listOf(
+                        ComposableDemo("Two buttons") { ButtonGroupSample() },
+                        ComposableDemo("Three buttons") { ButtonGroupDemo() },
+                    )
+                ),
+                ComposableDemo("List Header") { Centralize { ListHeaderDemo() } },
+                Material3DemoCategory("Time Text", TimeTextDemos),
+                ComposableDemo("Card") { CardDemo() },
+                ComposableDemo("Animated Shape Buttons") { AnimatedShapeButtonDemo() },
+                ComposableDemo("Animated Shape Toggle Buttons") { AnimatedShapeToggleButtonDemo() },
+                ComposableDemo("Text Toggle Button") { TextToggleButtonDemo() },
+                ComposableDemo("Icon Toggle Button") { IconToggleButtonDemo() },
+                ComposableDemo("Checkbox Button") { CheckboxButtonDemo() },
+                ComposableDemo("Split Checkbox Button") { SplitCheckboxButtonDemo() },
+                ComposableDemo("Radio Button") { RadioButtonDemo() },
+                ComposableDemo("Split Radio Button") { SplitRadioButtonDemo() },
+                ComposableDemo("Switch Button") { SwitchButtonDemo() },
+                ComposableDemo("Split Switch Button") { SplitSwitchButtonDemo() },
+                Material3DemoCategory("Stepper", StepperDemos),
+                Material3DemoCategory("Slider", SliderDemos),
+                Material3DemoCategory("Picker", PickerDemos),
+                // Requires API level 26 or higher due to java.time dependency.
+                *(if (Build.VERSION.SDK_INT >= 26)
+                    arrayOf(
+                        Material3DemoCategory("TimePicker", TimePickerDemos),
+                        Material3DemoCategory("DatePicker", DatePickerDemos)
+                    )
+                else emptyArray<Material3DemoCategory>()),
+                Material3DemoCategory("Progress Indicator", ProgressIndicatorDemos),
+                Material3DemoCategory("Scroll Indicator", ScrollIndicatorDemos),
+                Material3DemoCategory("Placeholder", PlaceholderDemos),
+                ComposableDemo(title = "Fixed Font Size") { Centralize { FixedFontSize() } },
+                Material3DemoCategory(
+                    title = "Swipe To Dismiss",
+                    listOf(
+                        ComposableDemo("Simple") { SimpleSwipeToDismissBox(it.navigateBack) },
+                        ComposableDemo("Stateful") { StatefulSwipeToDismissBox() },
+                        ComposableDemo("Edge swipe") {
+                            EdgeSwipeForSwipeToDismiss(it.navigateBack)
+                        },
+                    )
+                ),
+                Material3DemoCategory(
+                    title = "Page Indicator",
+                    listOf(
+                        ComposableDemo("HorizontalPageIndicator") {
+                            HorizontalPageIndicatorWithPagerSample()
+                        },
+                        ComposableDemo("VerticalPageIndicator") {
+                            VerticalPageIndicatorWithPagerSample()
+                        },
+                    )
+                ),
+                Material3DemoCategory(
+                    title = "Swipe to Reveal",
+                    listOf(
+                        ComposableDemo("Bi-directional / Non-anchoring") {
+                            Centralize { SwipeToRevealBothDirectionsNonAnchoring() }
+                        },
+                        ComposableDemo("Bi-directional Two Actions") {
+                            Centralize { SwipeToRevealBothDirections() }
+                        },
+                        ComposableDemo("Two Actions") { Centralize { SwipeToRevealSample() } },
+                        ComposableDemo("Two Undo Actions") {
+                            Centralize { SwipeToRevealTwoActionsWithUndo() }
+                        },
+                        ComposableDemo("Single action with Card") {
+                            Centralize { SwipeToRevealSingleActionCardSample() }
+                        },
+                        ComposableDemo("In a list") { Centralize { SwipeToRevealInList() } },
+                        ComposableDemo("Non-anchoring") {
+                            Centralize { SwipeToRevealNonAnchoredSample() }
+                        }
+                    )
+                ),
+                Material3DemoCategory(title = "Typography", TypographyDemos),
+                Material3DemoCategory(
+                    "Animated Text",
+                    if (Build.VERSION.SDK_INT > 31) {
+                        listOf(
+                            ComposableDemo("Simple animation") {
+                                Centralize { AnimatedTextSample() }
+                            },
+                            ComposableDemo("Animation with button click") {
+                                Centralize { AnimatedTextSampleButtonResponse() }
+                            },
+                            ComposableDemo("Shared Font Registry") {
+                                Centralize { AnimatedTextSampleSharedFontRegistry() }
+                            },
+                        )
+                    } else {
+                        emptyList()
                     }
+                ),
+                ComposableDemo("Settings Demo") { SettingsDemo() },
+                Material3DemoCategory(
+                    title = "LazyColumn",
+                    listOf(
+                        ComposableDemo("Notifications") { LazyColumnNotificationsDemo() },
+                        ComposableDemo("Scaling Morphing Effect Sample") {
+                            LazyColumnScalingMorphingEffectSample()
+                        },
+                        ComposableDemo("Target Morphing Height Sample") {
+                            LazyColumnTargetMorphingHeightSample()
+                        }
+                    )
                 )
             )
-        )
+            .sortedBy { it.title }
     )
 
 internal fun showOnClickToast(context: Context) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/res/font/robotoflex_variable.ttf b/wear/compose/compose-material3/integration-tests/src/main/res/font/robotoflex_variable.ttf
new file mode 100644
index 0000000..c39aafb
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/res/font/robotoflex_variable.ttf
Binary files differ
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt
index abb6f9f..baf20de 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt
@@ -22,7 +22,7 @@
 import androidx.compose.runtime.Composable
 import androidx.wear.compose.material3.Icon
 import androidx.wear.compose.material3.ListHeader
-import androidx.wear.compose.material3.ListSubheader
+import androidx.wear.compose.material3.ListSubHeader
 import androidx.wear.compose.material3.Text
 
 @Sampled
@@ -33,15 +33,15 @@
 
 @Sampled
 @Composable
-fun ListSubheaderSample() {
-    ListSubheader { Text("Subheader") }
+fun ListSubHeaderSample() {
+    ListSubHeader { Text("SubHeader") }
 }
 
 @Sampled
 @Composable
-fun ListSubheaderWithIconSample() {
-    ListSubheader(
-        label = { Text(text = "Subheader") },
+fun ListSubHeaderWithIconSample() {
+    ListSubHeader(
+        label = { Text(text = "SubHeader") },
         icon = { Icon(imageVector = Icons.Outlined.Home, "home") }
     )
 }
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PagerScaffoldSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PagerScaffoldSample.kt
new file mode 100644
index 0000000..08c15c9
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/PagerScaffoldSample.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.foundation.pager.rememberPagerState
+import androidx.wear.compose.material3.AppScaffold
+import androidx.wear.compose.material3.HorizontalPagerScaffold
+import androidx.wear.compose.material3.ScreenScaffold
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.VerticalPagerScaffold
+
+@Sampled
+@Composable
+fun HorizontalPagerScaffoldSample() {
+    AppScaffold {
+        val pagerState = rememberPagerState(pageCount = { 10 })
+
+        HorizontalPagerScaffold(pagerState = pagerState) { page ->
+            ScreenScaffold {
+                Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                    Text("Page $page")
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun VerticalPagerScaffoldSample() {
+    AppScaffold {
+        val pagerState = rememberPagerState(pageCount = { 10 })
+
+        VerticalPagerScaffold(pagerState = pagerState) { page ->
+            ScreenScaffold {
+                Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                    Text("Page $page")
+                }
+            }
+        }
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderScreenshotTest.kt
index 1f3ba09..6209574 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderScreenshotTest.kt
@@ -54,7 +54,7 @@
             methodName = testName.methodName,
             screenshotRule = screenshotRule,
         ) {
-            ListSubheader(modifier = Modifier.testTag(TEST_TAG)) { Text("Subheader") }
+            ListSubHeader(modifier = Modifier.testTag(TEST_TAG)) { Text("Subheader") }
         }
 
     @Test
@@ -64,7 +64,7 @@
             screenshotRule = screenshotRule,
             layoutDirection = LayoutDirection.Rtl,
         ) {
-            ListSubheader(modifier = Modifier.testTag(TEST_TAG)) { Text("Subheader") }
+            ListSubHeader(modifier = Modifier.testTag(TEST_TAG)) { Text("Subheader") }
         }
 
     @Test
@@ -74,7 +74,7 @@
             screenshotRule = screenshotRule,
             layoutDirection = LayoutDirection.Ltr,
         ) {
-            ListSubheader(
+            ListSubHeader(
                 modifier = Modifier.testTag(TEST_TAG),
                 label = { Text(text = "Subheader") },
                 icon = { Icon(imageVector = Icons.Outlined.Home, "home") }
@@ -88,7 +88,7 @@
             screenshotRule = screenshotRule,
             layoutDirection = LayoutDirection.Rtl
         ) {
-            ListSubheader(
+            ListSubHeader(
                 modifier = Modifier.testTag(TEST_TAG),
                 label = { Text(text = "Subheader") },
                 icon = { Icon(imageVector = Icons.Outlined.Home, "home") }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt
index 167473c..3a34438 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt
@@ -62,7 +62,7 @@
     @Test
     fun listSubheader_has_semantic_heading_property() {
         rule.setContentWithTheme {
-            ListSubheader(modifier = Modifier.testTag(TEST_TAG)) { Text("Subheader") }
+            ListSubHeader(modifier = Modifier.testTag(TEST_TAG)) { Text("Subheader") }
         }
 
         rule.assertNodeIsHeading(TEST_TAG)
@@ -103,7 +103,7 @@
 
         rule
             .setContentWithThemeForSizeAssertions {
-                ListSubheader(modifier = Modifier.testTag(TEST_TAG)) {
+                ListSubHeader(modifier = Modifier.testTag(TEST_TAG)) {
                     Text(
                         "Header with multiple lines of text to exceed" +
                             " the minimum height, should adjust"
@@ -120,7 +120,7 @@
 
         rule.setContentWithTheme {
             expectedTextStyle = MaterialTheme.typography.titleMedium
-            ListSubheader { actualTextStyle = LocalTextStyle.current }
+            ListSubHeader { actualTextStyle = LocalTextStyle.current }
         }
 
         Assert.assertEquals(expectedTextStyle, actualTextStyle)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt
index b5e9a6c..bcfac15 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt
@@ -142,15 +142,15 @@
     val isLargeScreen = LocalConfiguration.current.screenWidthDp > 225
     val labelTextStyle =
         if (isLargeScreen) {
-            DatePickerTokens.PickerLabelLargeTypography.value
+            DatePickerTokens.LabelLargeTypography.value
         } else {
-            DatePickerTokens.PickerLabelTypography.value
+            DatePickerTokens.LabelTypography.value
         }
     val optionTextStyle =
         if (isLargeScreen) {
-            DatePickerTokens.PickerContentLargeTypography.value
+            DatePickerTokens.ContentLargeTypography.value
         } else {
-            DatePickerTokens.PickerContentTypography.value
+            DatePickerTokens.ContentTypography.value
         }
     val optionHeight = if (isLargeScreen) 48.dp else 36.dp
 
@@ -528,10 +528,10 @@
             return defaultDatePickerColorsCached
                 ?: DatePickerColors(
                         selectedPickerContentColor =
-                            fromToken(DatePickerTokens.SelectedPickerContentColor),
+                            fromToken(DatePickerTokens.SelectedContentColor),
                         unselectedPickerContentColor =
-                            fromToken(DatePickerTokens.UnselectedPickerContentColor),
-                        pickerLabelColor = fromToken(DatePickerTokens.PickerLabelColor),
+                            fromToken(DatePickerTokens.UnselectedContentColor),
+                        pickerLabelColor = fromToken(DatePickerTokens.LabelColor),
                         nextButtonContentColor = fromToken(DatePickerTokens.NextButtonContentColor),
                         nextButtonContainerColor =
                             fromToken(DatePickerTokens.NextButtonContainerColor),
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconToggleButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconToggleButton.kt
index 39f2537..8fb2afe 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconToggleButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconToggleButton.kt
@@ -229,7 +229,7 @@
         checkedCornerSize: CornerSize = CheckedCornerSize,
         pressedCornerSize: CornerSize = PressedCornerSize,
         onPressAnimationSpec: FiniteAnimationSpec<Float> =
-            MaterialTheme.motionScheme.rememberFastSpatialSpec(),
+            MaterialTheme.motionScheme.fastSpatialSpec(),
         onReleaseAnimationSpec: FiniteAnimationSpec<Float> =
             MaterialTheme.motionScheme.slowSpatialSpec(),
     ): Shape {
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt
index 2e63601..db65f78 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt
@@ -55,15 +55,13 @@
  * @param contentPadding The spacing values to apply internally between the container and the
  *   content.
  * @param content Slot for [ListHeader] content, expected to be a single line of text.
- *
- * TODO(b/261838497) Add Material3 UX guidance links
  */
 @Composable
 fun ListHeader(
     modifier: Modifier = Modifier,
     backgroundColor: Color = Color.Transparent,
-    contentColor: Color = ListHeaderTokens.ContentColor.value,
-    contentPadding: PaddingValues = ListHeaderDefaults.HeaderContentPadding,
+    contentColor: Color = ListHeaderDefaults.ContentColor,
+    contentPadding: PaddingValues = ListHeaderDefaults.ContentPadding,
     content: @Composable RowScope.() -> Unit
 ) {
     Row(
@@ -87,32 +85,30 @@
 }
 
 /**
- * A two slot based composable for creating a list subheader item. [ListSubheader]s offer slots for
+ * A two slot based composable for creating a list sub-header item. [ListSubHeader]s offer slots for
  * an icon and for a text label. The contents will be start and end padded.
  *
- * Example of a [ListSubheader]:
+ * Example of a [ListSubHeader]:
  *
- * @sample androidx.wear.compose.material3.samples.ListSubheaderSample
+ * @sample androidx.wear.compose.material3.samples.ListSubHeaderSample
  *
- * Example of a [ListSubheader] with an icon:
+ * Example of a [ListSubHeader] with an icon:
  *
- * @sample androidx.wear.compose.material3.samples.ListSubheaderWithIconSample
- * @param modifier The modifier for the [ListSubheader].
+ * @sample androidx.wear.compose.material3.samples.ListSubHeaderWithIconSample
+ * @param modifier The modifier for the [ListSubHeader].
  * @param backgroundColor The background color to apply - typically Color.Transparent
  * @param contentColor The color to apply to content.
  * @param contentPadding The spacing values to apply internally between the container and the
  *   content.
- * @param icon A slot for providing icon to the [ListSubheader].
- * @param label A slot for providing label to the [ListSubheader].
- *
- * TODO(b/261838497) Add Material3 UX guidance links
+ * @param icon A slot for providing icon to the [ListSubHeader].
+ * @param label A slot for providing label to the [ListSubHeader].
  */
 @Composable
-fun ListSubheader(
+fun ListSubHeader(
     modifier: Modifier = Modifier,
     backgroundColor: Color = Color.Transparent,
-    contentColor: Color = ListSubHeaderTokens.ContentColor.value,
-    contentPadding: PaddingValues = ListHeaderDefaults.SubheaderContentPadding,
+    contentColor: Color = ListHeaderDefaults.subHeaderContentColor,
+    contentPadding: PaddingValues = ListHeaderDefaults.SubHeaderContentPadding,
     icon: (@Composable BoxScope.() -> Unit)? = null,
     label: @Composable RowScope.() -> Unit,
 ) {
@@ -147,12 +143,23 @@
 
 object ListHeaderDefaults {
     private val TopPadding = 16.dp
-    private val SubheaderBottomPadding = 8.dp
+    private val SubHeaderBottomPadding = 8.dp
     private val HeaderBottomPadding = 12.dp
     private val HorizontalPadding = 14.dp
 
-    val HeaderContentPadding =
+    /** The default content padding for ListHeader */
+    val ContentPadding =
         PaddingValues(HorizontalPadding, TopPadding, HorizontalPadding, HeaderBottomPadding)
-    val SubheaderContentPadding =
-        PaddingValues(HorizontalPadding, TopPadding, HorizontalPadding, SubheaderBottomPadding)
+
+    /** The default content padding for ListSubHeader */
+    val SubHeaderContentPadding =
+        PaddingValues(HorizontalPadding, TopPadding, HorizontalPadding, SubHeaderBottomPadding)
+
+    /** The default color for ListHeader */
+    val ContentColor: Color
+        @Composable get() = ListHeaderTokens.ContentColor.value
+
+    /** The default color for ListSubHeader */
+    val subHeaderContentColor: Color
+        @Composable get() = ListSubHeaderTokens.ContentColor.value
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MotionScheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MotionScheme.kt
index d3d3e89..25711f6 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MotionScheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MotionScheme.kt
@@ -20,9 +20,7 @@
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.TwoWayConverter
 import androidx.compose.animation.core.spring
-import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.staticCompositionLocalOf
 
 /**
@@ -30,8 +28,8 @@
  *
  * Motion schemes are designed to create a harmonious motion for components in the app.
  *
- * There are two built-in schemes, a [standardMotionScheme] and an [expressiveMotionScheme], that
- * can be used as-is or customized.
+ * There are two built-in schemes, a [standard] and an [expressive], that can be used as-is or
+ * customized.
  */
 @Immutable
 interface MotionScheme {
@@ -44,9 +42,6 @@
      *
      * [T] is the generic data type that will be animated by the system, as long as the appropriate
      * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
-     *
-     * When called from a Composable, use [rememberDefaultSpatialSpec] extension to ensure that the
-     * returned animation spec is remembered across compositions.
      */
     fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T>
 
@@ -59,9 +54,6 @@
      *
      * [T] is the generic data type that will be animated by the system, as long as the appropriate
      * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
-     *
-     * When called from a Composable, use [rememberFastSpatialSpec] extension to ensure that the
-     * returned animation spec is remembered across compositions.
      */
     fun <T> fastSpatialSpec(): FiniteAnimationSpec<T>
 
@@ -74,9 +66,6 @@
      *
      * [T] is the generic data type that will be animated by the system, as long as the appropriate
      * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
-     *
-     * When called from a Composable, use [rememberSlowSpatialSpec] extension to ensure that the
-     * returned animation spec is remembered across compositions.
      */
     fun <T> slowSpatialSpec(): FiniteAnimationSpec<T>
 
@@ -88,9 +77,6 @@
      *
      * [T] is the generic data type that will be animated by the system, as long as the appropriate
      * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
-     *
-     * When called from a Composable, use [rememberDefaultEffectsSpec] extension to ensure that the
-     * returned animation spec is remembered across compositions.
      */
     fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T>
 
@@ -102,9 +88,6 @@
      *
      * [T] is the generic data type that will be animated by the system, as long as the appropriate
      * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
-     *
-     * When called from a Composable, use [rememberFastEffectsSpec] extension to ensure that the
-     * returned animation spec is remembered across compositions.
      */
     fun <T> fastEffectsSpec(): FiniteAnimationSpec<T>
 
@@ -116,174 +99,160 @@
      *
      * [T] is the generic data type that will be animated by the system, as long as the appropriate
      * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
-     *
-     * When called from a Composable, use [rememberSlowEffectsSpec] extension to ensure that the
-     * returned animation spec is remembered across compositions.
      */
     fun <T> slowEffectsSpec(): FiniteAnimationSpec<T>
+
+    companion object {
+        /**
+         * Returns a standard Material motion scheme.
+         *
+         * The standard scheme is Material's basic motion scheme for utilitarian UI elements and
+         * recurring interactions. It provides a linear motion feel.
+         */
+        @Suppress("UNCHECKED_CAST")
+        fun standard(): MotionScheme =
+            object : MotionScheme {
+                private val defaultSpatialSpec =
+                    spring<Any>(
+                        dampingRatio = StandardSpatialDampingRatio,
+                        stiffness = StandardDefaultStiffness
+                    )
+
+                private val fastSpatialSpec =
+                    spring<Any>(
+                        dampingRatio = StandardSpatialDampingRatio,
+                        stiffness = StandardFastStiffness
+                    )
+
+                private val slowSpatialSpec =
+                    spring<Any>(
+                        dampingRatio = StandardSpatialDampingRatio,
+                        stiffness = StandardSlowStiffness
+                    )
+
+                private val defaultEffectsSpec =
+                    spring<Any>(
+                        dampingRatio = EffectsDampingRatio,
+                        stiffness = EffectsDefaultStiffness
+                    )
+
+                private val fastEffectsSpec =
+                    spring<Any>(
+                        dampingRatio = EffectsDampingRatio,
+                        stiffness = EffectsFastStiffness
+                    )
+
+                private val slowEffectsSpec =
+                    spring<Any>(
+                        dampingRatio = EffectsDampingRatio,
+                        stiffness = EffectsSlowStiffness
+                    )
+
+                override fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T> {
+                    return defaultSpatialSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> fastSpatialSpec(): FiniteAnimationSpec<T> {
+                    return fastSpatialSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> slowSpatialSpec(): FiniteAnimationSpec<T> {
+                    return slowSpatialSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T> {
+                    return defaultEffectsSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> fastEffectsSpec(): FiniteAnimationSpec<T> {
+                    return fastEffectsSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> slowEffectsSpec(): FiniteAnimationSpec<T> {
+                    return slowEffectsSpec as FiniteAnimationSpec<T>
+                }
+            }
+
+        /**
+         * Returns an expressive Material motion scheme.
+         *
+         * The expressive scheme is Material's recommended motion scheme for prominent UI elements
+         * and hero interactions. It provides a visually engaging motion feel.
+         */
+        @Suppress("UNCHECKED_CAST")
+        fun expressive(): MotionScheme =
+            object : MotionScheme {
+                private val defaultSpatialSpec =
+                    spring<Any>(
+                        dampingRatio = ExpressiveDefaultDamping,
+                        stiffness = ExpressiveDefaultStiffness
+                    )
+
+                private val fastSpatialSpec =
+                    spring<Any>(
+                        dampingRatio = ExpressiveFastDamping,
+                        stiffness = ExpressiveFastStiffness
+                    )
+
+                private val slowSpatialSpec =
+                    spring<Any>(
+                        dampingRatio = ExpressiveSlowDamping,
+                        stiffness = ExpressiveSlowStiffness
+                    )
+
+                private val defaultEffectsSpec =
+                    spring<Any>(
+                        dampingRatio = EffectsDampingRatio,
+                        stiffness = EffectsDefaultStiffness
+                    )
+
+                private val fastEffectsSpec =
+                    spring<Any>(
+                        dampingRatio = EffectsDampingRatio,
+                        stiffness = EffectsFastStiffness
+                    )
+
+                private val slowEffectsSpec =
+                    spring<Any>(
+                        dampingRatio = EffectsDampingRatio,
+                        stiffness = EffectsSlowStiffness
+                    )
+
+                override fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T> {
+                    return defaultSpatialSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> fastSpatialSpec(): FiniteAnimationSpec<T> {
+                    return fastSpatialSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> slowSpatialSpec(): FiniteAnimationSpec<T> {
+                    return slowSpatialSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T> {
+                    return defaultEffectsSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> fastEffectsSpec(): FiniteAnimationSpec<T> {
+                    return fastEffectsSpec as FiniteAnimationSpec<T>
+                }
+
+                override fun <T> slowEffectsSpec(): FiniteAnimationSpec<T> {
+                    return slowEffectsSpec as FiniteAnimationSpec<T>
+                }
+            }
+    }
 }
 
 /**
- * A default spatial motion [FiniteAnimationSpec] that is remembered across compositions.
- *
- * [T] is the generic data type that will be animated by the system, as long as the appropriate
- * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
- *
- * @see [MotionScheme.defaultSpatialSpec]
- */
-@Composable
-inline fun <reified T> MotionScheme.rememberDefaultSpatialSpec() =
-    remember(this, T::class) {
-        val spec: FiniteAnimationSpec<T> = defaultSpatialSpec()
-        spec
-    }
-
-/**
- * A fast spatial motion [FiniteAnimationSpec] that is remembered across compositions.
- *
- * [T] is the generic data type that will be animated by the system.
- *
- * @see [MotionScheme.fastSpatialSpec]
- */
-@Composable
-inline fun <reified T> MotionScheme.rememberFastSpatialSpec() =
-    remember(this, T::class) {
-        val spec: FiniteAnimationSpec<T> = fastSpatialSpec()
-        spec
-    }
-
-/**
- * A slow spatial motion [FiniteAnimationSpec] that is remembered across compositions.
- *
- * [T] is the generic data type that will be animated by the system.
- *
- * @see [MotionScheme.slowSpatialSpec]
- */
-@Composable
-inline fun <reified T> MotionScheme.rememberSlowSpatialSpec() =
-    remember(this, T::class) {
-        val spec: FiniteAnimationSpec<T> = slowSpatialSpec()
-        spec
-    }
-
-/**
- * A default effects motion [FiniteAnimationSpec] that is remembered across compositions.
- *
- * [T] is the generic data type that will be animated by the system.
- *
- * @see [MotionScheme.defaultEffectsSpec]
- */
-@Composable
-inline fun <reified T> MotionScheme.rememberDefaultEffectsSpec() =
-    remember(this, T::class) {
-        val spec: FiniteAnimationSpec<T> = defaultEffectsSpec()
-        spec
-    }
-
-/**
- * A fast effects motion [FiniteAnimationSpec] that is remembered across compositions.
- *
- * [T] is the generic data type that will be animated by the system.
- *
- * @see [MotionScheme.fastEffectsSpec]
- */
-@Composable
-inline fun <reified T> MotionScheme.rememberFastEffectsSpec() =
-    remember(this, T::class) {
-        val spec: FiniteAnimationSpec<T> = fastEffectsSpec()
-        spec
-    }
-
-/**
- * A slow effects motion [FiniteAnimationSpec] that is remembered across compositions.
- *
- * [T] is the generic data type that will be animated by the system.
- *
- * @see [MotionScheme.slowEffectsSpec]
- */
-@Composable
-inline fun <reified T> MotionScheme.rememberSlowEffectsSpec() =
-    remember(this, T::class) {
-        val spec: FiniteAnimationSpec<T> = slowEffectsSpec()
-        spec
-    }
-
-/** Returns a standard Material motion scheme. */
-fun standardMotionScheme(): MotionScheme =
-    object : MotionScheme {
-        override fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T> {
-            return spring(
-                dampingRatio = StandardSpatialDampingRatio,
-                stiffness = StandardDefaultStiffness
-            )
-        }
-
-        override fun <T> fastSpatialSpec(): FiniteAnimationSpec<T> {
-            return spring(
-                dampingRatio = StandardSpatialDampingRatio,
-                stiffness = StandardFastStiffness
-            )
-        }
-
-        override fun <T> slowSpatialSpec(): FiniteAnimationSpec<T> {
-            return spring(
-                dampingRatio = StandardSpatialDampingRatio,
-                stiffness = StandardSlowStiffness
-            )
-        }
-
-        override fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsDefaultStiffness)
-        }
-
-        override fun <T> fastEffectsSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsFastStiffness)
-        }
-
-        override fun <T> slowEffectsSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsSlowStiffness)
-        }
-    }
-
-/** Returns an expressive Material motion scheme. */
-fun expressiveMotionScheme(): MotionScheme =
-    object : MotionScheme {
-        override fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T> {
-            return spring(
-                dampingRatio = ExpressiveDefaultDamping,
-                stiffness = ExpressiveDefaultStiffness
-            )
-        }
-
-        override fun <T> fastSpatialSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = ExpressiveFastDamping, stiffness = ExpressiveFastStiffness)
-        }
-
-        override fun <T> slowSpatialSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = ExpressiveSlowDamping, stiffness = ExpressiveSlowStiffness)
-        }
-
-        override fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsDefaultStiffness)
-        }
-
-        override fun <T> fastEffectsSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsFastStiffness)
-        }
-
-        override fun <T> slowEffectsSpec(): FiniteAnimationSpec<T> {
-            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsSlowStiffness)
-        }
-    }
-
-/**
  * CompositionLocal used to pass [MotionScheme] down the tree.
  *
  * Setting the value here is typically done as part of [MaterialTheme]. To retrieve the current
  * value of this CompositionLocal, use [MaterialTheme.motionScheme].
  */
 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-internal val LocalMotionScheme = staticCompositionLocalOf { standardMotionScheme() }
+internal val LocalMotionScheme = staticCompositionLocalOf { MotionScheme.standard() }
 
 // TODO - These values should come from Tokens.
 private const val StandardSpatialDampingRatio = 0.9f
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PageIndicator.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PageIndicator.kt
index 77f72e3..c57cae9 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PageIndicator.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PageIndicator.kt
@@ -58,12 +58,12 @@
 import androidx.wear.compose.foundation.curvedBox
 import androidx.wear.compose.foundation.curvedRow
 import androidx.wear.compose.foundation.padding
+import androidx.wear.compose.foundation.pager.HorizontalPager
 import androidx.wear.compose.foundation.pager.PagerState
+import androidx.wear.compose.foundation.pager.VerticalPager
 import androidx.wear.compose.foundation.radialSize
 import androidx.wear.compose.foundation.size
 import androidx.wear.compose.foundation.weight
-import androidx.wear.compose.material3.pager.HorizontalPager
-import androidx.wear.compose.material3.pager.VerticalPager
 import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
 import androidx.wear.compose.materialcore.BoundsLimiter
 import androidx.wear.compose.materialcore.isLayoutDirectionRtl
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/pager/Pager.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PagerScaffold.kt
similarity index 61%
rename from wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/pager/Pager.kt
rename to wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PagerScaffold.kt
index 3075495..2676d72 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/pager/Pager.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PagerScaffold.kt
@@ -14,23 +14,27 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.pager
+package androidx.wear.compose.material3
 
 import androidx.annotation.FloatRange
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.TargetedFlingBehavior
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.pager.PagerDefaults as ComposePagerDefaults
 import androidx.compose.foundation.pager.PagerScope
 import androidx.compose.foundation.pager.PagerSnapDistance
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -42,15 +46,147 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.ActiveFocusListener
+import androidx.wear.compose.foundation.ScrollInfoProvider
 import androidx.wear.compose.foundation.pager.HorizontalPager
+import androidx.wear.compose.foundation.pager.PagerDefaults
 import androidx.wear.compose.foundation.pager.PagerState
 import androidx.wear.compose.foundation.pager.VerticalPager
-import androidx.wear.compose.material3.DefaultTouchExplorationStateProvider
-import androidx.wear.compose.material3.MaterialTheme
 import kotlin.math.absoluteValue
 
 /**
+ * [HorizontalPagerScaffold] is one of the Wear Material3 scaffold components.
+ *
+ * The scaffold components [AppScaffold] and [HorizontalPagerScaffold] lay out the structure of a
+ * Pager and coordinate transitions of the [HorizontalPageIndicator] and [TimeText] components.
+ *
+ * [HorizontalPagerScaffold] displays the [HorizontalPageIndicator] at the center-end of the screen
+ * by default and coordinates showing/hiding [TimeText] and [HorizontalPageIndicator] according to
+ * whether the Pager is being paged, this is determined by the [PagerState].
+ *
+ * Example of using [AppScaffold] and [HorizontalPagerScaffold]:
+ *
+ * @sample androidx.wear.compose.material3.samples.HorizontalPagerScaffoldSample
+ * @param pagerState The state of the pager controlling the page content.
+ * @param modifier The modifier to be applied to the scaffold.
+ * @param pageIndicator A composable function that defines the page indicator to be displayed. By
+ *   default, it uses a [HorizontalPageIndicator].
+ * @param pageIndicatorAnimationSpec - An optional parameter to set whether the page indicator
+ *   should fade out when paging has finished. This is useful for when the underlying page content
+ *   conflicts with the page indicator. By default this is null, so the page indicator will be
+ *   visible at all times, setting this to [PagerScaffoldDefaults.FadeOutAnimation] ensures the
+ *   indicator only shows during paging, and fades out when the Pager is idle.
+ * @param content A composable function that takes the current page index as a parameter and defines
+ *   the content to be displayed on that page.
+ */
+@Composable
+fun HorizontalPagerScaffold(
+    pagerState: PagerState,
+    modifier: Modifier = Modifier,
+    pageIndicator: (@Composable BoxScope.() -> Unit)? = { HorizontalPageIndicator(pagerState) },
+    pageIndicatorAnimationSpec: AnimationSpec<Float>? = null,
+    content: @Composable PagerScope.(page: Int) -> Unit,
+) =
+    PagerScaffoldImpl(
+        scrollInfoProvider = ScrollInfoProvider(pagerState, orientation = Orientation.Horizontal),
+        pagerContent = { AnimatedHorizontalPager(pagerState, content = content) },
+        modifier = modifier,
+        pagerState = pagerState,
+        pageIndicator = pageIndicator,
+        pageIndicatorAnimationSpec = pageIndicatorAnimationSpec,
+    )
+
+/**
+ * [VerticalPagerScaffold] is one of the Wear Material3 scaffold components.
+ *
+ * The scaffold components [AppScaffold] and [VerticalPagerScaffold] lay out the structure of a
+ * Pager and coordinate transitions of the [VerticalPageIndicator] and [TimeText] components.
+ *
+ * [VerticalPagerScaffold] displays the [VerticalPageIndicator] at the center-end of the screen by
+ * default and coordinates showing/hiding [TimeText] and [VerticalPageIndicator] according to
+ * whether the Pager is being paged, this is determined by the [PagerState].
+ *
+ * Example of using [AppScaffold] and [VerticalPagerScaffold]:
+ *
+ * @sample androidx.wear.compose.material3.samples.VerticalPagerScaffoldSample
+ * @param pagerState The state of the pager controlling the page content.
+ * @param modifier The modifier to be applied to the scaffold.
+ * @param pageIndicator A composable function that defines the page indicator to be displayed. By
+ *   default, it uses a [VerticalPageIndicator].
+ * @param pageIndicatorAnimationSpec - An optional parameter to set whether the page indicator
+ *   should fade out when paging has finished. This is useful for when the underlying page content
+ *   conflicts with the page indicator. By default this is null, so the page indicator will be
+ *   visible at all times, setting this to [PagerScaffoldDefaults.FadeOutAnimation] ensures the
+ *   indicator only shows during paging, and fades out when the Pager is idle.
+ * @param content A composable function that takes the current page index as a parameter and defines
+ *   the content to be displayed on that page.
+ */
+@Composable
+fun VerticalPagerScaffold(
+    pagerState: PagerState,
+    modifier: Modifier = Modifier,
+    pageIndicator: (@Composable BoxScope.() -> Unit)? = { VerticalPageIndicator(pagerState) },
+    pageIndicatorAnimationSpec: AnimationSpec<Float>? = null,
+    content: @Composable PagerScope.(page: Int) -> Unit,
+) =
+    PagerScaffoldImpl(
+        scrollInfoProvider = ScrollInfoProvider(pagerState, orientation = Orientation.Vertical),
+        pagerContent = { AnimatedVerticalPager(pagerState, content = content) },
+        modifier = modifier,
+        pagerState = pagerState,
+        pageIndicator = pageIndicator,
+        pageIndicatorAnimationSpec = pageIndicatorAnimationSpec,
+    )
+
+/** Contains default values used for [HorizontalPagerScaffold] and [VerticalPagerScaffold]. */
+object PagerScaffoldDefaults {
+    /**
+     * The default value for the indicator fade out animation spec. Use this to fade out the page
+     * indicator when paging has stopped.
+     */
+    val FadeOutAnimation: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow)
+}
+
+@Composable
+private fun PagerScaffoldImpl(
+    scrollInfoProvider: ScrollInfoProvider,
+    pagerContent: @Composable () -> Unit,
+    pagerState: PagerState,
+    modifier: Modifier,
+    pageIndicator: (@Composable BoxScope.() -> Unit)?,
+    pageIndicatorAnimationSpec: AnimationSpec<Float>?,
+) {
+    val scaffoldState = LocalScaffoldState.current
+    val key = remember { Any() }
+
+    key(scrollInfoProvider) {
+        DisposableEffect(key) { onDispose { scaffoldState.removeScreen(key) } }
+
+        ActiveFocusListener { focused ->
+            if (focused) {
+                scaffoldState.addScreen(key, null, scrollInfoProvider)
+            } else {
+                scaffoldState.removeScreen(key)
+            }
+        }
+    }
+
+    scaffoldState.UpdateIdlingDetectorIfNeeded()
+
+    Box(modifier = modifier.fillMaxSize()) {
+        pagerContent()
+
+        AnimatedIndicator(
+            isVisible = {
+                scaffoldState.screenStage.value != ScreenStage.Idle || pagerState.isScrollInProgress
+            },
+            animationSpec = pageIndicatorAnimationSpec,
+            content = pageIndicator,
+        )
+    }
+}
+
+/**
  * A full-screen horizontally scrolling Pager optimized for Wear OS devices. This component wraps
  * the Wear Compose Foundation [HorizontalPager] and provides Material3 animations. Note: If
  * accessibility is enabled the Wear system swipe to dismiss gesture will be disabled, in this case
@@ -87,21 +223,19 @@
  * @param content A composable function that defines the content of each page displayed by the
  *   Pager. This is where the UI elements that should appear within each page should be placed.
  */
-@ExperimentalWearFoundationApi
 @Composable
-internal fun HorizontalPager(
+internal fun AnimatedHorizontalPager(
     state: PagerState,
     modifier: Modifier = Modifier,
     contentPadding: PaddingValues = PaddingValues(0.dp),
     contentScrimColor: Color = MaterialTheme.colorScheme.background,
-    beyondViewportPageCount: Int = ComposePagerDefaults.BeyondViewportPageCount,
-    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
+    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
+    flingBehavior: TargetedFlingBehavior = snapWithSpringBehavior(state = state),
     userScrollEnabled: Boolean = true,
     reverseLayout: Boolean = false,
     key: ((index: Int) -> Any)? = null,
     @FloatRange(from = 0.0, to = 1.0)
-    swipeToDismissEdgeZoneFraction: Float =
-        androidx.wear.compose.foundation.pager.PagerDefaults.SwipeToDismissEdgeZoneFraction,
+    swipeToDismissEdgeZoneFraction: Float = PagerDefaults.SwipeToDismissEdgeZoneFraction,
     content: @Composable PagerScope.(page: Int) -> Unit
 ) {
     val touchExplorationStateProvider = remember { DefaultTouchExplorationStateProvider() }
@@ -158,15 +292,14 @@
  * @param content A composable function that defines the content of each page displayed by the
  *   Pager. This is where the UI elements that should appear within each page should be placed.
  */
-@ExperimentalWearFoundationApi
 @Composable
-internal fun VerticalPager(
+internal fun AnimatedVerticalPager(
     state: PagerState,
     modifier: Modifier = Modifier,
     contentPadding: PaddingValues = PaddingValues(0.dp),
     contentScrimColor: Color = MaterialTheme.colorScheme.background,
-    beyondViewportPageCount: Int = ComposePagerDefaults.BeyondViewportPageCount,
-    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
+    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
+    flingBehavior: TargetedFlingBehavior = snapWithSpringBehavior(state = state),
     userScrollEnabled: Boolean = true,
     reverseLayout: Boolean = false,
     key: ((index: Int) -> Any)? = null,
@@ -192,22 +325,20 @@
     }
 }
 
-internal object PagerDefaults {
-    @Composable
-    fun flingBehavior(
-        state: PagerState,
-    ): TargetedFlingBehavior {
-        return ComposePagerDefaults.flingBehavior(
-            state = state,
-            pagerSnapDistance = PagerSnapDistance.atMost(1),
-            snapAnimationSpec = spring(dampingRatio = 1f, stiffness = 200f),
-            snapPositionalThreshold = 0.35f,
-        )
-    }
+@Composable
+internal fun snapWithSpringBehavior(
+    state: PagerState,
+): TargetedFlingBehavior {
+    return PagerDefaults.snapFlingBehavior(
+        state = state,
+        pagerSnapDistance = PagerSnapDistance.atMost(1),
+        snapAnimationSpec = spring(dampingRatio = 1f, stiffness = 200f),
+        snapPositionalThreshold = 0.35f,
+    )
 }
 
 @Composable
-internal fun AnimatedPageContent(
+private fun AnimatedPageContent(
     orientation: Orientation,
     page: Int,
     pagerState: PagerState,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
index 7c1d103..767891d 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
@@ -98,10 +98,9 @@
     interactionSource: InteractionSource,
     shape: CornerBasedShape,
     pressedShape: CornerBasedShape,
-    onPressAnimationSpec: FiniteAnimationSpec<Float> =
-        MaterialTheme.motionScheme.rememberFastEffectsSpec(),
+    onPressAnimationSpec: FiniteAnimationSpec<Float> = MaterialTheme.motionScheme.fastEffectsSpec(),
     onReleaseAnimationSpec: FiniteAnimationSpec<Float> =
-        MaterialTheme.motionScheme.rememberDefaultSpatialSpec(),
+        MaterialTheme.motionScheme.defaultSpatialSpec(),
 ): Shape {
     val pressed = interactionSource.collectIsPressedAsState()
 
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Scaffold.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Scaffold.kt
index 4f722495..e2eba10 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Scaffold.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Scaffold.kt
@@ -16,18 +16,30 @@
 
 package androidx.wear.compose.material3
 
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.animate
+import androidx.compose.animation.core.spring
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.util.fastForEach
 import androidx.wear.compose.foundation.ScrollInfoProvider
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
 
 internal class ScaffoldState(private val appTimeText: (@Composable (() -> Unit))? = null) {
     fun removeScreen(key: Any) {
@@ -96,6 +108,43 @@
     private val screenContent = mutableStateListOf<ScreenContent>()
 }
 
+@Composable
+internal fun AnimatedIndicator(
+    isVisible: () -> Boolean,
+    animationSpec: AnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow),
+    content: @Composable (BoxScope.() -> Unit)? = null
+) {
+    // Skip if no indicator provided
+    content?.let { pageIndicator ->
+        if (animationSpec == null) {
+            // if no animationSpec is provided then indicator will always be visible
+            Box(modifier = Modifier.fillMaxSize(), content = pageIndicator)
+        } else {
+            // if animationSpec is provided this will be used to fade out indicator
+            val alphaValue = remember { mutableFloatStateOf(0f) }
+            LaunchedEffect(isVisible) {
+                launch {
+                    snapshotFlow { if (isVisible()) 1f else 0f }
+                        .distinctUntilChanged()
+                        .collectLatest { targetValue ->
+                            animate(
+                                alphaValue.floatValue,
+                                targetValue,
+                                animationSpec = animationSpec
+                            ) { value, _ ->
+                                alphaValue.floatValue = value
+                            }
+                        }
+                }
+            }
+            Box(
+                modifier = Modifier.fillMaxSize().graphicsLayer { alpha = alphaValue.floatValue },
+                content = pageIndicator
+            )
+        }
+    }
+}
+
 internal val LocalScaffoldState = compositionLocalOf { ScaffoldState() }
 
 private const val IDLE_DELAY = 2500L
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
index 3af611e..50260db 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
@@ -16,10 +16,6 @@
 
 package androidx.wear.compose.material3
 
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.animate
-import androidx.compose.animation.core.spring
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
@@ -27,14 +23,10 @@
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.key
-import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
@@ -49,9 +41,6 @@
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
 import kotlin.math.roundToInt
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.launch
 
 /**
  * [ScreenScaffold] is one of the Wear Material3 scaffold components.
@@ -376,50 +365,17 @@
     Box(modifier = modifier.fillMaxSize()) {
         content()
         scrollInfoProvider?.let {
-            AnimatedScrollIndicator(
-                scrollInfoProvider = scrollInfoProvider,
+            AnimatedIndicator(
+                isVisible = {
+                    scaffoldState.screenStage.value != ScreenStage.Idle &&
+                        scrollInfoProvider.isScrollable
+                },
                 content = scrollIndicator,
-                stage = { scaffoldState.screenStage.value }
             )
         } ?: scrollIndicator?.let { it() }
     }
 }
 
-@Composable
-private fun AnimatedScrollIndicator(
-    scrollInfoProvider: ScrollInfoProvider,
-    stage: () -> ScreenStage,
-    content: @Composable (BoxScope.() -> Unit)? = null
-) {
-    // Skip if no scroll indicator provided
-    content?.let { scrollIndicator ->
-        val alphaValue = remember { mutableFloatStateOf(0f) }
-        val animationSpec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow)
-        LaunchedEffect(scrollInfoProvider, scrollIndicator) {
-            launch {
-                snapshotFlow {
-                        if (stage() != ScreenStage.Idle && scrollInfoProvider.isScrollable) 1f
-                        else 0f
-                    }
-                    .distinctUntilChanged()
-                    .collectLatest { targetValue ->
-                        animate(
-                            alphaValue.floatValue,
-                            targetValue,
-                            animationSpec = animationSpec
-                        ) { value, _ ->
-                            alphaValue.floatValue = value
-                        }
-                    }
-            }
-        }
-        Box(
-            modifier = Modifier.fillMaxSize().graphicsLayer { alpha = alphaValue.floatValue },
-            content = scrollIndicator
-        )
-    }
-}
-
 // Sets the height that will be used down the line, using a state as parameter, to avoid
 // recompositions when the height changes.
 internal fun Modifier.dynamicHeight(heightState: () -> Float) =
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt
index 6de3ad5..a7ee821 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt
@@ -209,7 +209,7 @@
         checkedCornerSize: CornerSize = CheckedCornerSize,
         pressedCornerSize: CornerSize = PressedCornerSize,
         onPressAnimationSpec: FiniteAnimationSpec<Float> =
-            MaterialTheme.motionScheme.rememberFastSpatialSpec(),
+            MaterialTheme.motionScheme.fastSpatialSpec(),
         onReleaseAnimationSpec: FiniteAnimationSpec<Float> =
             MaterialTheme.motionScheme.slowSpatialSpec(),
     ): Shape {
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt
index cb1daac..bcab35d 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt
@@ -424,11 +424,11 @@
             return defaultTimePickerColorsCached
                 ?: TimePickerColors(
                         selectedPickerContentColor =
-                            fromToken(TimePickerTokens.SelectedPickerContentColor),
+                            fromToken(TimePickerTokens.SelectedContentColor),
                         unselectedPickerContentColor =
-                            fromToken(TimePickerTokens.UnselectedPickerContentColor),
+                            fromToken(TimePickerTokens.UnselectedContentColor),
                         separatorColor = fromToken(TimePickerTokens.SeparatorColor),
-                        pickerLabelColor = fromToken(TimePickerTokens.PickerLabelColor),
+                        pickerLabelColor = fromToken(TimePickerTokens.LabelColor),
                         confirmButtonContentColor =
                             fromToken(TimePickerTokens.ConfirmButtonContentColor),
                         confirmButtonContainerColor =
@@ -512,17 +512,17 @@
     val isLargeScreen = LocalConfiguration.current.screenWidthDp > 225
     val labelTextStyle =
         if (isLargeScreen) {
-                TimePickerTokens.PickerLabelLargeTypography
+                TimePickerTokens.LabelLargeTypography
             } else {
-                TimePickerTokens.PickerLabelTypography
+                TimePickerTokens.LabelTypography
             }
             .value
 
     val optionTextStyle =
         if (isLargeScreen || timePickerType == TimePickerType.HoursMinutes24H) {
-                TimePickerTokens.PickerContentLargeTypography
+                TimePickerTokens.ContentLargeTypography
             } else {
-                TimePickerTokens.PickerContentTypography
+                TimePickerTokens.ContentTypography
             }
             .value
             .copy(textAlign = TextAlign.Center)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt
index cd03fe5..d8e3459 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CheckboxButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CheckboxButtonTokens.kt
index 25c6f47..a905591 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CheckboxButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CheckboxButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ChildButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ChildButtonTokens.kt
index 3b050ce..539470c 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ChildButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ChildButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
index bdafd77..8ab38e0 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
index a2e2591..1da7bb3 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -27,13 +27,13 @@
     val OnError = PaletteTokens.Error20
     val OnErrorContainer = PaletteTokens.Error10
     val OnPrimary = PaletteTokens.Primary10
-    val OnPrimaryContainer = PaletteTokens.Primary90
+    val OnPrimaryContainer = PaletteTokens.Primary95
     val OnSecondary = PaletteTokens.Secondary10
-    val OnSecondaryContainer = PaletteTokens.Secondary90
+    val OnSecondaryContainer = PaletteTokens.Secondary95
     val OnSurface = PaletteTokens.Neutral95
     val OnSurfaceVariant = PaletteTokens.NeutralVariant80
     val OnTertiary = PaletteTokens.Tertiary10
-    val OnTertiaryContainer = PaletteTokens.Tertiary90
+    val OnTertiaryContainer = PaletteTokens.Tertiary95
     val Outline = PaletteTokens.NeutralVariant60
     val OutlineVariant = PaletteTokens.NeutralVariant40
     val Primary = PaletteTokens.Primary90
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CompactButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CompactButtonTokens.kt
index 97b5c3f..e3fcd94e 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CompactButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CompactButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/DatePickerTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/DatePickerTokens.kt
index 9c53ae8..41e7411 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/DatePickerTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/DatePickerTokens.kt
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
+// VERSION: v0_87
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
 package androidx.wear.compose.material3.tokens
-
 internal object DatePickerTokens {
-    val SelectedPickerContentColor = ColorSchemeKeyTokens.OnBackground
-    val UnselectedPickerContentColor = ColorSchemeKeyTokens.SecondaryDim
-    val PickerLabelColor = ColorSchemeKeyTokens.Primary
-    val NextButtonContentColor = ColorSchemeKeyTokens.Primary
-    val NextButtonContainerColor = ColorSchemeKeyTokens.SurfaceContainer
-    val ConfirmButtonContentColor = ColorSchemeKeyTokens.OnPrimary
     val ConfirmButtonContainerColor = ColorSchemeKeyTokens.PrimaryDim
-
-    val PickerLabelLargeTypography = TypographyKeyTokens.TitleLarge
-    val PickerLabelTypography = TypographyKeyTokens.TitleMedium
-    val PickerContentLargeTypography = TypographyKeyTokens.NumeralMedium
-    val PickerContentTypography = TypographyKeyTokens.NumeralSmall
+    val ConfirmButtonContentColor = ColorSchemeKeyTokens.OnPrimary
+    val NextButtonContainerColor = ColorSchemeKeyTokens.SurfaceContainer
+    val NextButtonContentColor = ColorSchemeKeyTokens.Primary
+    val ContentLargeTypography = TypographyKeyTokens.NumeralMedium
+    val ContentTypography = TypographyKeyTokens.NumeralSmall
+    val LabelColor = ColorSchemeKeyTokens.Primary
+    val LabelLargeTypography = TypographyKeyTokens.TitleLarge
+    val LabelTypography = TypographyKeyTokens.TitleMedium
+    val SelectedContentColor = ColorSchemeKeyTokens.OnBackground
+    val UnselectedContentColor = ColorSchemeKeyTokens.SecondaryDim
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledButtonTokens.kt
index f2501f5..fc611f1 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_73
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledIconButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledIconButtonTokens.kt
index d68f8cc..5ea9650 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledIconButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledIconButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_67
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTextButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTextButtonTokens.kt
index 92f987b..37b7212 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTextButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTextButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_67
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt
index 1ad42ba..e9ec515 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt
index 681e78f..4ad4143 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt
index a220a6a..5588557e 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconButtonTokens.kt
index c54e56e..49a07de 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt
index eaaddff..501a593 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -24,6 +24,10 @@
 internal object IconToggleButtonTokens {
     val CheckedContainerColor = ColorSchemeKeyTokens.Primary
     val CheckedContentColor = ColorSchemeKeyTokens.OnPrimary
+    val ContainerDefaultSize = 52.0.dp
+    val ContainerExtraLargeSize = 72.0.dp
+    val ContainerLargeSize = 60.0.dp
+    val ContainerSmallSize = 48.0.dp
     val DisabledCheckedContainerColor = ColorSchemeKeyTokens.OnSurface
     val DisabledCheckedContainerOpacity = 0.12f
     val DisabledCheckedContentColor = ColorSchemeKeyTokens.OnSurface
@@ -32,16 +36,11 @@
     val DisabledUncheckedContainerOpacity = 0.12f
     val DisabledUncheckedContentColor = ColorSchemeKeyTokens.OnSurface
     val DisabledUncheckedContentOpacity = 0.38f
+    val IconDefaultSize = 26.0.dp
+    val IconExtraLargeSize = 36.0.dp
+    val IconLargeSize = 32.0.dp
+    val IconSmallSize = 24.0.dp
     val IconDisabledCheckedContainerShape = ShapeKeyTokens.CornerExtraLarge
     val UncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
     val UncheckedContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
-
-    val ContainerSmallSize = 48.0.dp
-    val ContainerDefaultSize = 52.0.dp
-    val ContainerLargeSize = 60.0.dp
-    val ContainerExtraLargeSize = 72.0.dp
-    val IconSmallSize = 24.0.dp
-    val IconDefaultSize = 26.0.dp
-    val IconLargeSize = 32.0.dp
-    val IconExtraLargeSize = 36.0.dp
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt
index a95e41f..c47a1ed 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: 0_72
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageCardTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageCardTokens.kt
index a6fe8bd..260535f9 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageCardTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageCardTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListHeaderTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListHeaderTokens.kt
index fc38bc0..ebdaca2 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListHeaderTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListHeaderTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListSubHeaderTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListSubHeaderTokens.kt
index a31f870..36c8dd6 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListSubHeaderTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ListSubHeaderTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/MotionTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/MotionTokens.kt
index a5a96a11..a2e37ff 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/MotionTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/MotionTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedButtonTokens.kt
index 62eb568..3e60b0c 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedCardTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedCardTokens.kt
index 8c17f20..2ecdd18 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedCardTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedCardTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedIconButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedIconButtonTokens.kt
index 02f0728..cdcb50b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedIconButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedIconButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedTextButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedTextButtonTokens.kt
index de29596..5bc3a3d 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedTextButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/OutlinedTextButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
index 608479b..d2b7737 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -38,7 +38,6 @@
     val Error90 = Color(red = 249, green = 222, blue = 220)
     val Error95 = Color(red = 252, green = 238, blue = 238)
     val Neutral0 = Color(red = 0, green = 0, blue = 0)
-    val Neutral10 = Color(red = 29, green = 26, blue = 38)
     val Neutral100 = Color(red = 255, green = 255, blue = 255)
     val Neutral15 = Color(red = 39, green = 36, blue = 48)
     val Neutral20 = Color(red = 51, green = 46, blue = 60)
@@ -75,17 +74,17 @@
     val Primary90 = Color(red = 233, green = 221, blue = 255)
     val Primary95 = Color(red = 246, green = 237, blue = 255)
     val Secondary0 = Color(red = 0, green = 0, blue = 0)
-    val Secondary10 = Color(red = 30, green = 24, blue = 46)
+    val Secondary10 = Color(red = 12, green = 22, blue = 73)
     val Secondary100 = Color(red = 255, green = 255, blue = 255)
-    val Secondary20 = Color(red = 51, green = 45, blue = 68)
-    val Secondary30 = Color(red = 74, green = 67, blue = 91)
-    val Secondary40 = Color(red = 98, green = 90, blue = 116)
-    val Secondary50 = Color(red = 123, green = 115, blue = 141)
-    val Secondary60 = Color(red = 150, green = 140, blue = 168)
-    val Secondary70 = Color(red = 177, green = 167, blue = 195)
-    val Secondary80 = Color(red = 204, green = 194, blue = 223)
-    val Secondary90 = Color(red = 233, green = 222, blue = 252)
-    val Secondary95 = Color(red = 246, green = 237, blue = 255)
+    val Secondary20 = Color(red = 35, green = 44, blue = 94)
+    val Secondary30 = Color(red = 58, green = 67, blue = 118)
+    val Secondary40 = Color(red = 82, green = 91, blue = 144)
+    val Secondary50 = Color(red = 106, green = 116, blue = 170)
+    val Secondary60 = Color(red = 132, green = 141, blue = 198)
+    val Secondary70 = Color(red = 159, green = 168, blue = 226)
+    val Secondary80 = Color(red = 186, green = 195, blue = 255)
+    val Secondary90 = Color(red = 222, green = 224, blue = 255)
+    val Secondary95 = Color(red = 240, green = 239, blue = 255)
     val Tertiary0 = Color(red = 0, green = 0, blue = 0)
     val Tertiary10 = Color(red = 46, green = 21, blue = 0)
     val Tertiary100 = Color(red = 255, green = 255, blue = 255)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt
index 12ed324..6e5c957 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeKeyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeKeyTokens.kt
index 653efce..7654116 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeKeyTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeKeyTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
index a220415..781e3d8 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SliderTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SliderTokens.kt
index ad0ea71..1157ecc 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SliderTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SliderTokens.kt
@@ -14,27 +14,27 @@
  * limitations under the License.
  */
 
+// VERSION: v0_87
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
 package androidx.wear.compose.material3.tokens
-
 internal object SliderTokens {
-    val ContainerColor = ColorSchemeKeyTokens.SurfaceContainer
     val ButtonIconColor = ColorSchemeKeyTokens.OnSurface
-    val SelectedBarColor = ColorSchemeKeyTokens.Primary
-    val UnselectedBarSeparatorColor = ColorSchemeKeyTokens.Primary
-    val VariantSelectedBarColor = ColorSchemeKeyTokens.PrimaryDim
-    val VariantUnselectedBarSeparatorColor = ColorSchemeKeyTokens.PrimaryDim
-    val UnselectedBarSeparatorOpacity = 0.5f
-    val UnselectedBarColor = ColorSchemeKeyTokens.Background
-    val SelectedBarSeparatorColor = ColorSchemeKeyTokens.PrimaryContainer
-
-    val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
-    val DisabledContainerOpacity = 0.12f
+    val ContainerColor = ColorSchemeKeyTokens.SurfaceContainer
+    val ContainerShape = ShapeKeyTokens.CornerLarge
     val DisabledButtonIconColor = ColorSchemeKeyTokens.OnSurface
     val DisabledButtonIconOpacity = 0.38f
+    val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
+    val DisabledContainerOpacity = 0.12f
     val DisabledSelectedBarColor = ColorSchemeKeyTokens.OutlineVariant
-    val DisabledUnselectedBarColor = ColorSchemeKeyTokens.Background
     val DisabledSelectedBarSeparatorColor = ColorSchemeKeyTokens.SurfaceContainer
+    val DisabledUnselectedBarColor = ColorSchemeKeyTokens.Background
     val DisabledUnselectedBarSeparatorColor = ColorSchemeKeyTokens.OutlineVariant
-
-    val ContainerShape = ShapeKeyTokens.CornerLarge
+    val SelectedBarColor = ColorSchemeKeyTokens.Primary
+    val SelectedBarSeparatorColor = ColorSchemeKeyTokens.PrimaryContainer
+    val UnselectedBarColor = ColorSchemeKeyTokens.Background
+    val UnselectedBarSeparatorColor = ColorSchemeKeyTokens.Primary
+    val UnselectedBarSeparatorOpacity = 0.5f
+    val VariantSelectedBarColor = ColorSchemeKeyTokens.PrimaryDim
+    val VariantUnselectedBarSeparatorColor = ColorSchemeKeyTokens.PrimaryDim
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitCheckboxButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitCheckboxButtonTokens.kt
index 0c24b09..5a7f25a 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitCheckboxButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitCheckboxButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt
index acee450..8fef64d1 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitSwitchButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitSwitchButtonTokens.kt
index 1142bf0..b6ed908 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitSwitchButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitSwitchButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwipeToRevealTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwipeToRevealTokens.kt
index a82d210..0d7f3ed 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwipeToRevealTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwipeToRevealTokens.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.tokens
+// VERSION: v0_87
+// GENERATED CODE - DO NOT MODIFY BY HAND
 
+package androidx.wear.compose.material3.tokens
 internal object SwipeToRevealTokens {
     val PrimaryActionContainerColor = ColorSchemeKeyTokens.Error
     val PrimaryActionContentColor = ColorSchemeKeyTokens.OnError
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwitchButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwitchButtonTokens.kt
index 657e8cc..77f4fb2 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwitchButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SwitchButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextButtonTokens.kt
index 67ae831..1412af9 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt
index f89f0b7..a80bbd4 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_65
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -24,6 +24,13 @@
 internal object TextToggleButtonTokens {
     val CheckedContainerColor = ColorSchemeKeyTokens.Primary
     val CheckedContentColor = ColorSchemeKeyTokens.OnPrimary
+    val ContainerDefaultSize = 52.0.dp
+    val ContainerExtraLargeSize = 72.0.dp
+    val ContainerLargeSize = 60.0.dp
+    val ContainerShape = ShapeKeyTokens.CornerFull
+    val ContentDefaultFont = TypographyKeyTokens.LabelMedium
+    val ContentExtraLargeFont = TypographyKeyTokens.LabelLarge
+    val ContentLargeFont = TypographyKeyTokens.LabelMedium
     val DisabledCheckedContainerColor = ColorSchemeKeyTokens.OnSurface
     val DisabledCheckedContainerOpacity = 0.12f
     val DisabledCheckedContentColor = ColorSchemeKeyTokens.OnSurface
@@ -34,11 +41,4 @@
     val DisabledUncheckedContentOpacity = 0.38f
     val UncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
     val UncheckedContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
-
-    val ContentDefaultFont = TypographyKeyTokens.LabelMedium
-    val ContentLargeFont = TypographyKeyTokens.LabelMedium
-    val ContentExtraLargeFont = TypographyKeyTokens.LabelLarge
-    val ContainerDefaultSize = 52.0.dp
-    val ContainerLargeSize = 60.0.dp
-    val ContainerExtraLargeSize = 72.0.dp
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TimePickerTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TimePickerTokens.kt
index 8700fc6..2597355 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TimePickerTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TimePickerTokens.kt
@@ -14,18 +14,19 @@
  * limitations under the License.
  */
 
+// VERSION: v0_87
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
 package androidx.wear.compose.material3.tokens
-
 internal object TimePickerTokens {
-    val SelectedPickerContentColor = ColorSchemeKeyTokens.OnBackground
-    val UnselectedPickerContentColor = ColorSchemeKeyTokens.SecondaryDim
-    val SeparatorColor = ColorSchemeKeyTokens.OnSurfaceVariant
-    val PickerLabelColor = ColorSchemeKeyTokens.Primary
-    val ConfirmButtonContentColor = ColorSchemeKeyTokens.OnPrimary
     val ConfirmButtonContainerColor = ColorSchemeKeyTokens.PrimaryDim
-
-    val PickerLabelLargeTypography = TypographyKeyTokens.TitleLarge
-    val PickerLabelTypography = TypographyKeyTokens.TitleMedium
-    val PickerContentLargeTypography = TypographyKeyTokens.NumeralMedium
-    val PickerContentTypography = TypographyKeyTokens.NumeralSmall
+    val ConfirmButtonContentColor = ColorSchemeKeyTokens.OnPrimary
+    val ContentLargeTypography = TypographyKeyTokens.NumeralMedium
+    val ContentTypography = TypographyKeyTokens.NumeralSmall
+    val LabelColor = ColorSchemeKeyTokens.Primary
+    val LabelLargeTypography = TypographyKeyTokens.TitleLarge
+    val LabelTypography = TypographyKeyTokens.TitleMedium
+    val SelectedContentColor = ColorSchemeKeyTokens.OnBackground
+    val SeparatorColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedContentColor = ColorSchemeKeyTokens.SecondaryDim
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypeScaleTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypeScaleTokens.kt
index be763e7..1c6878e 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypeScaleTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypeScaleTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_71
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -25,19 +25,22 @@
     val ArcLargeFont = TypefaceTokens.Brand
     val ArcLargeLineHeight = 22.0.sp
     val ArcLargeSize = 20.sp
-    val ArcLargeTracking = 0.4.sp
+    val ArcLargeTrackingBottom = 1.8.sp
+    val ArcLargeTrackingTop = 0.4.sp
     val ArcLargeWeight = 600.0f
     val ArcLargeWidth = 100.0f
     val ArcMediumFont = TypefaceTokens.Brand
     val ArcMediumLineHeight = 18.0.sp
     val ArcMediumSize = 15.sp
-    val ArcMediumTracking = 0.6.sp
+    val ArcMediumTrackingBottom = 1.4.sp
+    val ArcMediumTrackingTop = 0.6.sp
     val ArcMediumWeight = 600.0f
     val ArcMediumWidth = 100.0f
     val ArcSmallFont = TypefaceTokens.Brand
     val ArcSmallLineHeight = 16.0.sp
     val ArcSmallSize = 14.sp
-    val ArcSmallTracking = 0.6.sp
+    val ArcSmallTrackingBottom = 1.6.sp
+    val ArcSmallTrackingTop = 0.6.sp
     val ArcSmallWeight = 560.0f
     val ArcSmallWidth = 100.0f
     val BodyExtraSmallFont = TypefaceTokens.Brand
@@ -69,13 +72,13 @@
     val BodySmallWeightProminent = 700.0f
     val BodySmallWidth = 110.0f
     val DisplayLargeFont = TypefaceTokens.Brand
-    val DisplayLargeLineHeight = 46.0.sp
+    val DisplayLargeLineHeight = 44.0.sp
     val DisplayLargeSize = 40.sp
     val DisplayLargeTracking = 0.2.sp
     val DisplayLargeWeight = 500.0f
     val DisplayLargeWidth = 110.0f
     val DisplayMediumFont = TypefaceTokens.Brand
-    val DisplayMediumLineHeight = 36.0.sp
+    val DisplayMediumLineHeight = 34.0.sp
     val DisplayMediumSize = 30.sp
     val DisplayMediumTracking = 0.2.sp
     val DisplayMediumWeight = 520.0f
@@ -93,7 +96,7 @@
     val LabelLargeWeight = 500.0f
     val LabelLargeWidth = 110.0f
     val LabelMediumFont = TypefaceTokens.Brand
-    val LabelMediumLineHeight = 16.0.sp
+    val LabelMediumLineHeight = 18.0.sp
     val LabelMediumSize = 15.sp
     val LabelMediumTracking = 0.4.sp
     val LabelMediumWeight = 500.0f
@@ -121,7 +124,7 @@
     val NumeralLargeFont = TypefaceTokens.Brand
     val NumeralLargeLineHeight = 50.0.sp
     val NumeralLargeSize = 50.sp
-    val NumeralLargeTracking = 1.0.sp
+    val NumeralLargeTracking = 0.0.sp
     val NumeralLargeWeight = 580.0f
     val NumeralLargeWeightProminent = 780.0f
     val NumeralLargeWidth = 110.0f
@@ -135,7 +138,7 @@
     val NumeralSmallFont = TypefaceTokens.Brand
     val NumeralSmallLineHeight = 30.0.sp
     val NumeralSmallSize = 30.sp
-    val NumeralSmallTracking = 1.0.sp
+    val NumeralSmallTracking = 0.0.sp
     val NumeralSmallWeight = 550.0f
     val NumeralSmallWeightProminent = 750.0f
     val NumeralSmallWidth = 100.0f
@@ -149,12 +152,12 @@
     val TitleMediumLineHeight = 18.0.sp
     val TitleMediumSize = 16.sp
     val TitleMediumTracking = 0.4.sp
-    val TitleMediumWeight = 500.0f
+    val TitleMediumWeight = 550.0f
     val TitleMediumWidth = 110.0f
     val TitleSmallFont = TypefaceTokens.Brand
     val TitleSmallLineHeight = 16.0.sp
     val TitleSmallSize = 14.sp
     val TitleSmallTracking = 0.4.sp
-    val TitleSmallWeight = 500.0f
+    val TitleSmallWeight = 550.0f
     val TitleSmallWidth = 110.0f
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypefaceTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypefaceTokens.kt
index 0779680..75614cf 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypefaceTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypefaceTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_71
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -26,5 +26,6 @@
     val Brand = FontFamily.SansSerif
     val Plain = FontFamily.SansSerif
     val WeightBold = FontWeight.Bold
+    val WeightMedium = FontWeight.Medium
     val WeightRegular = FontWeight.Normal
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyKeyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyKeyTokens.kt
index ea1dec4..04482df 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyKeyTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyKeyTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_71
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyTokens.kt
index 9165f0d..f65d23f 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_71
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -38,7 +38,7 @@
             fontWeight = FontWeight(TypeScaleTokens.ArcLargeWeight.toInt()),
             fontSize = TypeScaleTokens.ArcLargeSize,
             lineHeight = TypeScaleTokens.ArcLargeLineHeight,
-            letterSpacing = TypeScaleTokens.ArcLargeTracking,
+            letterSpacing = TypeScaleTokens.ArcLargeTrackingTop,
         )
     val ArcMedium =
         DefaultTextStyle.copy(
@@ -53,7 +53,7 @@
             fontWeight = FontWeight(TypeScaleTokens.ArcMediumWeight.toInt()),
             fontSize = TypeScaleTokens.ArcMediumSize,
             lineHeight = TypeScaleTokens.ArcMediumLineHeight,
-            letterSpacing = TypeScaleTokens.ArcMediumTracking,
+            letterSpacing = TypeScaleTokens.ArcMediumTrackingTop,
         )
     val ArcSmall =
         DefaultTextStyle.copy(
@@ -67,7 +67,7 @@
             fontWeight = FontWeight(TypeScaleTokens.ArcSmallWeight.toInt()),
             fontSize = TypeScaleTokens.ArcSmallSize,
             lineHeight = TypeScaleTokens.ArcSmallLineHeight,
-            letterSpacing = TypeScaleTokens.ArcSmallTracking,
+            letterSpacing = TypeScaleTokens.ArcSmallTrackingTop,
         )
     val BodyExtraSmall =
         DefaultTextStyle.copy(
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyVariableFontsTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyVariableFontsTokens.kt
index 6169c9f..89874b0 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyVariableFontsTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyVariableFontsTokens.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// VERSION: v0_71
+// VERSION: v0_87
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.wear.compose.material3.tokens
@@ -24,13 +24,13 @@
 internal object TypographyVariableFontsTokens {
     val ArcLargeVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.ArcLargeWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.ArcLargeWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.ArcLargeWeight),
         )
     val ArcMediumVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.ArcMediumWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.ArcMediumWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.ArcMediumWeight),
         )
     val ArcSmallVariationSettings =
         FontVariation.Settings(
@@ -39,8 +39,8 @@
         )
     val BodyExtraSmallVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wdth", TypeScaleTokens.BodyExtraSmallWidth),
             FontVariation.Setting("wght", TypeScaleTokens.BodyExtraSmallWeight),
+            FontVariation.Setting("wdth", TypeScaleTokens.BodyExtraSmallWidth),
         )
     val BodyLargeVariationSettings =
         FontVariation.Settings(
@@ -49,8 +49,8 @@
         )
     val BodyMediumVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wdth", TypeScaleTokens.BodyMediumWidth),
             FontVariation.Setting("wght", TypeScaleTokens.BodyMediumWeight),
+            FontVariation.Setting("wdth", TypeScaleTokens.BodyMediumWidth),
         )
     val BodySmallVariationSettings =
         FontVariation.Settings(
@@ -59,18 +59,18 @@
         )
     val DisplayLargeVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.DisplayLargeWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.DisplayLargeWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.DisplayLargeWeight),
         )
     val DisplayMediumVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wdth", TypeScaleTokens.DisplayMediumWidth),
             FontVariation.Setting("wght", TypeScaleTokens.DisplayMediumWeight),
+            FontVariation.Setting("wdth", TypeScaleTokens.DisplayMediumWidth),
         )
     val DisplaySmallVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.DisplaySmallWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.DisplaySmallWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.DisplaySmallWeight),
         )
     val LabelLargeVariationSettings =
         FontVariation.Settings(
@@ -79,8 +79,8 @@
         )
     val LabelMediumVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.LabelMediumWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.LabelMediumWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.LabelMediumWeight),
         )
     val LabelSmallVariationSettings =
         FontVariation.Settings(
@@ -89,8 +89,8 @@
         )
     val NumeralExtraLargeVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wdth", TypeScaleTokens.NumeralExtraLargeWidth),
             FontVariation.Setting("wght", TypeScaleTokens.NumeralExtraLargeWeight),
+            FontVariation.Setting("wdth", TypeScaleTokens.NumeralExtraLargeWidth),
         )
     val NumeralExtraSmallVariationSettings =
         FontVariation.Settings(
@@ -104,27 +104,27 @@
         )
     val NumeralMediumVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.NumeralMediumWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.NumeralMediumWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.NumeralMediumWeight),
         )
     val NumeralSmallVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.NumeralSmallWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.NumeralSmallWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.NumeralSmallWeight),
         )
     val TitleLargeVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wght", TypeScaleTokens.TitleLargeWeight),
             FontVariation.Setting("wdth", TypeScaleTokens.TitleLargeWidth),
+            FontVariation.Setting("wght", TypeScaleTokens.TitleLargeWeight),
         )
     val TitleMediumVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wdth", TypeScaleTokens.TitleMediumWidth),
             FontVariation.Setting("wght", TypeScaleTokens.TitleMediumWeight),
+            FontVariation.Setting("wdth", TypeScaleTokens.TitleMediumWidth),
         )
     val TitleSmallVariationSettings =
         FontVariation.Settings(
-            FontVariation.Setting("wdth", TypeScaleTokens.TitleSmallWidth),
             FontVariation.Setting("wght", TypeScaleTokens.TitleSmallWeight),
+            FontVariation.Setting("wdth", TypeScaleTokens.TitleSmallWidth),
         )
 }
diff --git a/wear/compose/compose-material3/src/test/kotlin/androidx/wear/compose/material3/AnimationSpecUtilsTest.kt b/wear/compose/compose-material3/src/test/kotlin/androidx/wear/compose/material3/AnimationSpecUtilsTest.kt
index 7888df6..130f0d8 100644
--- a/wear/compose/compose-material3/src/test/kotlin/androidx/wear/compose/material3/AnimationSpecUtilsTest.kt
+++ b/wear/compose/compose-material3/src/test/kotlin/androidx/wear/compose/material3/AnimationSpecUtilsTest.kt
@@ -92,7 +92,7 @@
                     val actual = wrappedSpec.speedAt((duration / speedupFactor * i / 100).toLong())
 
                     // The unit is pixels per second, so this is really small.
-                    assertEquals(expected, actual, 0.005f)
+                    assertEquals(expected, actual, 0.006f)
                 }
             }
         }
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
index a674c88..89744df 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
@@ -49,6 +49,7 @@
 import androidx.wear.compose.material.MaterialTheme
 import androidx.wear.compose.material.SwipeToDismissBox
 import androidx.wear.compose.material.Text
+import androidx.wear.compose.material3.demos.RobotoFlexTypography
 
 @Composable
 fun DemoApp(
@@ -109,12 +110,15 @@
         @Composable { it: @Composable () -> Unit ->
             // Only material3 demos benefit from the Material3 ScreenScaffold
             if (category.materialVersion == 3) {
-                val timeText = @Composable { androidx.wear.compose.material3.TimeText { time() } }
-                androidx.wear.compose.material3.ScreenScaffold(
-                    scrollState = state,
-                    timeText = remember { timeText },
-                ) {
-                    it()
+                androidx.wear.compose.material3.MaterialTheme(typography = RobotoFlexTypography) {
+                    val timeText =
+                        @Composable { androidx.wear.compose.material3.TimeText { time() } }
+                    androidx.wear.compose.material3.ScreenScaffold(
+                        scrollState = state,
+                        timeText = remember { timeText },
+                    ) {
+                        it()
+                    }
                 }
             } else {
                 it()
diff --git a/wear/protolayout/protolayout-material3/api/current.txt b/wear/protolayout/protolayout-material3/api/current.txt
index 582409a..f083fc52 100644
--- a/wear/protolayout/protolayout-material3/api/current.txt
+++ b/wear/protolayout/protolayout-material3/api/current.txt
@@ -47,6 +47,39 @@
     field public static final int TERTIARY_DIM = 27; // 0x1b
   }
 
+  public final class EdgeButtonColors {
+    ctor public EdgeButtonColors(androidx.wear.protolayout.ColorBuilders.ColorProp container, androidx.wear.protolayout.ColorBuilders.ColorProp content);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContainer();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContent();
+    property public final androidx.wear.protolayout.ColorBuilders.ColorProp container;
+    property public final androidx.wear.protolayout.ColorBuilders.ColorProp content;
+  }
+
+  public final class EdgeButtonDefaults {
+    method public androidx.wear.protolayout.material3.EdgeButtonColors filled(androidx.wear.protolayout.material3.MaterialScope);
+    method public androidx.wear.protolayout.material3.EdgeButtonColors filledTonal(androidx.wear.protolayout.material3.MaterialScope);
+    method public androidx.wear.protolayout.material3.EdgeButtonColors filledVariant(androidx.wear.protolayout.material3.MaterialScope);
+    field public static final androidx.wear.protolayout.material3.EdgeButtonDefaults INSTANCE;
+  }
+
+  public final class EdgeButtonKt {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement iconEdgeButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, androidx.wear.protolayout.TypeBuilders.StringProp contentDescription, optional androidx.wear.protolayout.material3.EdgeButtonColors colors, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> iconContent);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement textEdgeButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, androidx.wear.protolayout.TypeBuilders.StringProp contentDescription, optional androidx.wear.protolayout.material3.EdgeButtonColors colors, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> labelContent);
+  }
+
+  public final class EdgeButtonStyle {
+    field public static final androidx.wear.protolayout.material3.EdgeButtonStyle.Companion Companion;
+    field public static final androidx.wear.protolayout.material3.EdgeButtonStyle DEFAULT;
+    field public static final androidx.wear.protolayout.material3.EdgeButtonStyle TOP_ALIGN;
+  }
+
+  public static final class EdgeButtonStyle.Companion {
+  }
+
+  public final class IconKt {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement icon(androidx.wear.protolayout.material3.MaterialScope, String protoLayoutResourceId, optional androidx.wear.protolayout.DimensionBuilders.ImageDimension size, optional androidx.wear.protolayout.ColorBuilders.ColorProp tintColor);
+  }
+
   @androidx.wear.protolayout.material3.MaterialScopeMarker public class MaterialScope {
     method public final androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters getDeviceConfiguration();
     property public final androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters deviceConfiguration;
@@ -56,6 +89,8 @@
     method public static androidx.wear.protolayout.ColorBuilders.ColorProp getColorProp(androidx.wear.protolayout.material3.MaterialScope, int colorToken);
     method public static androidx.wear.protolayout.ModifiersBuilders.Corner getCorner(androidx.wear.protolayout.material3.MaterialScope, int shapeToken);
     method public static boolean isDynamicThemeEnabled(android.content.Context);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement materialScope(android.content.Context context, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters deviceConfiguration, optional boolean allowDynamicTheme, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> layout);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement materialScope(android.content.Context context, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters deviceConfiguration, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> layout);
   }
 
   @kotlin.DslMarker public @interface MaterialScopeMarker {
@@ -72,6 +107,10 @@
     field public static final androidx.wear.protolayout.material3.Shape INSTANCE;
   }
 
+  public final class TextKt {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement text(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.TypeBuilders.StringProp text, optional androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint stringLayoutConstraint, optional int typography, optional androidx.wear.protolayout.ColorBuilders.ColorProp color, optional boolean italic, optional boolean underline, optional boolean scalable, optional int maxLines, optional int multilineAlignment, optional int overflow, optional androidx.wear.protolayout.ModifiersBuilders.Modifiers modifiers);
+  }
+
   public final class Typography {
     field public static final int ARC_MEDIUM = 0; // 0x0
     field public static final int ARC_SMALL = 1; // 0x1
diff --git a/wear/protolayout/protolayout-material3/api/restricted_current.txt b/wear/protolayout/protolayout-material3/api/restricted_current.txt
index 582409a..f083fc52 100644
--- a/wear/protolayout/protolayout-material3/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-material3/api/restricted_current.txt
@@ -47,6 +47,39 @@
     field public static final int TERTIARY_DIM = 27; // 0x1b
   }
 
+  public final class EdgeButtonColors {
+    ctor public EdgeButtonColors(androidx.wear.protolayout.ColorBuilders.ColorProp container, androidx.wear.protolayout.ColorBuilders.ColorProp content);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContainer();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContent();
+    property public final androidx.wear.protolayout.ColorBuilders.ColorProp container;
+    property public final androidx.wear.protolayout.ColorBuilders.ColorProp content;
+  }
+
+  public final class EdgeButtonDefaults {
+    method public androidx.wear.protolayout.material3.EdgeButtonColors filled(androidx.wear.protolayout.material3.MaterialScope);
+    method public androidx.wear.protolayout.material3.EdgeButtonColors filledTonal(androidx.wear.protolayout.material3.MaterialScope);
+    method public androidx.wear.protolayout.material3.EdgeButtonColors filledVariant(androidx.wear.protolayout.material3.MaterialScope);
+    field public static final androidx.wear.protolayout.material3.EdgeButtonDefaults INSTANCE;
+  }
+
+  public final class EdgeButtonKt {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement iconEdgeButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, androidx.wear.protolayout.TypeBuilders.StringProp contentDescription, optional androidx.wear.protolayout.material3.EdgeButtonColors colors, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> iconContent);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement textEdgeButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, androidx.wear.protolayout.TypeBuilders.StringProp contentDescription, optional androidx.wear.protolayout.material3.EdgeButtonColors colors, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> labelContent);
+  }
+
+  public final class EdgeButtonStyle {
+    field public static final androidx.wear.protolayout.material3.EdgeButtonStyle.Companion Companion;
+    field public static final androidx.wear.protolayout.material3.EdgeButtonStyle DEFAULT;
+    field public static final androidx.wear.protolayout.material3.EdgeButtonStyle TOP_ALIGN;
+  }
+
+  public static final class EdgeButtonStyle.Companion {
+  }
+
+  public final class IconKt {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement icon(androidx.wear.protolayout.material3.MaterialScope, String protoLayoutResourceId, optional androidx.wear.protolayout.DimensionBuilders.ImageDimension size, optional androidx.wear.protolayout.ColorBuilders.ColorProp tintColor);
+  }
+
   @androidx.wear.protolayout.material3.MaterialScopeMarker public class MaterialScope {
     method public final androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters getDeviceConfiguration();
     property public final androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters deviceConfiguration;
@@ -56,6 +89,8 @@
     method public static androidx.wear.protolayout.ColorBuilders.ColorProp getColorProp(androidx.wear.protolayout.material3.MaterialScope, int colorToken);
     method public static androidx.wear.protolayout.ModifiersBuilders.Corner getCorner(androidx.wear.protolayout.material3.MaterialScope, int shapeToken);
     method public static boolean isDynamicThemeEnabled(android.content.Context);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement materialScope(android.content.Context context, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters deviceConfiguration, optional boolean allowDynamicTheme, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> layout);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement materialScope(android.content.Context context, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters deviceConfiguration, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> layout);
   }
 
   @kotlin.DslMarker public @interface MaterialScopeMarker {
@@ -72,6 +107,10 @@
     field public static final androidx.wear.protolayout.material3.Shape INSTANCE;
   }
 
+  public final class TextKt {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement text(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.TypeBuilders.StringProp text, optional androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint stringLayoutConstraint, optional int typography, optional androidx.wear.protolayout.ColorBuilders.ColorProp color, optional boolean italic, optional boolean underline, optional boolean scalable, optional int maxLines, optional int multilineAlignment, optional int overflow, optional androidx.wear.protolayout.ModifiersBuilders.Modifiers modifiers);
+  }
+
   public final class Typography {
     field public static final int ARC_MEDIUM = 0; // 0x0
     field public static final int ARC_SMALL = 1; // 0x1
diff --git a/wear/protolayout/protolayout-material3/build.gradle b/wear/protolayout/protolayout-material3/build.gradle
index 94f203d..0931f6f 100644
--- a/wear/protolayout/protolayout-material3/build.gradle
+++ b/wear/protolayout/protolayout-material3/build.gradle
@@ -33,8 +33,10 @@
     annotationProcessor(libs.nullaway)
     api(project(":wear:protolayout:protolayout"))
     api(libs.kotlinStdlib)
+    implementation(project(":wear:protolayout:protolayout-material-core"))
     lintChecks(project(":wear:protolayout:protolayout-lint"))
     lintPublish(project(":wear:protolayout:protolayout-lint"))
+    compileOnly(project(":annotation:annotation-sampled"))
 
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
@@ -65,4 +67,5 @@
     mavenVersion = LibraryVersions.WEAR_PROTOLAYOUT_MATERIAL3
     inceptionYear = "2024"
     description = "Material3 components library for ProtoLayout."
+    samples(project(":wear:protolayout:protolayout-material3-samples"))
 }
diff --git a/wear/protolayout/protolayout-material3/samples/build.gradle b/wear/protolayout/protolayout-material3/samples/build.gradle
new file mode 100644
index 0000000..1c680ec
--- /dev/null
+++ b/wear/protolayout/protolayout-material3/samples/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(project(":wear:protolayout:protolayout-material3"))
+
+    compileOnly(project(":annotation:annotation-sampled"))
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 26
+    }
+
+    namespace "androidx.wear.protolayout.material3.samples"
+}
+
+androidx {
+    name = "androidx.wear.protolayout:protolayout-material3-samples"
+    type = LibraryType.SAMPLES
+    inceptionYear = "2024"
+    description = "Contains the sample code for Material3 components library for ProtoLayout."
+}
diff --git a/wear/protolayout/protolayout-material3/samples/src/main/java/androidx/wear/protolayout/material3/samples/Material3ComponentsSample.kt b/wear/protolayout/protolayout-material3/samples/src/main/java/androidx/wear/protolayout/material3/samples/Material3ComponentsSample.kt
new file mode 100644
index 0000000..1103e2c
--- /dev/null
+++ b/wear/protolayout/protolayout-material3/samples/src/main/java/androidx/wear/protolayout/material3/samples/Material3ComponentsSample.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.material3.samples
+
+import android.content.Context
+import androidx.annotation.Sampled
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+import androidx.wear.protolayout.ModifiersBuilders.Clickable
+import androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint
+import androidx.wear.protolayout.TypeBuilders.StringProp
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
+import androidx.wear.protolayout.material3.ColorTokens
+import androidx.wear.protolayout.material3.Typography
+import androidx.wear.protolayout.material3.getColorProp
+import androidx.wear.protolayout.material3.icon
+import androidx.wear.protolayout.material3.iconEdgeButton
+import androidx.wear.protolayout.material3.materialScope
+import androidx.wear.protolayout.material3.text
+import androidx.wear.protolayout.material3.textEdgeButton
+
+/** Builds Material3 text element with default options. */
+@Sampled
+fun helloWorldTextDefault(context: Context, deviceConfiguration: DeviceParameters): LayoutElement =
+    materialScope(context, deviceConfiguration) {
+        text(text = "Hello Material3".prop(), typography = Typography.DISPLAY_LARGE)
+    }
+
+/** Builds Material3 text element with some of the overridden defaults. */
+@Sampled
+fun helloWorldTextDynamicCustom(
+    context: Context,
+    deviceConfiguration: DeviceParameters
+): LayoutElement =
+    materialScope(context, deviceConfiguration) {
+        text(
+            text =
+                StringProp.Builder("Static")
+                    .setDynamicValue(DynamicString.constant("Dynamic"))
+                    .build(),
+            stringLayoutConstraint = StringLayoutConstraint.Builder("Constraint").build(),
+            typography = Typography.DISPLAY_LARGE,
+            color = getColorProp(ColorTokens.TERTIARY),
+            underline = true,
+            maxLines = 5
+        )
+    }
+
+@Sampled
+fun edgeButtonSampleIcon(
+    context: Context,
+    deviceConfiguration: DeviceParameters,
+    clickable: Clickable
+): LayoutElement =
+    materialScope(context, deviceConfiguration) {
+        iconEdgeButton(onClick = clickable, contentDescription = "Description of a button".prop()) {
+            icon("id")
+        }
+    }
+
+@Sampled
+fun edgeButtonSampleText(
+    context: Context,
+    deviceConfiguration: DeviceParameters,
+    clickable: Clickable
+): LayoutElement =
+    materialScope(context, deviceConfiguration) {
+        textEdgeButton(onClick = clickable, contentDescription = "Description of a button".prop()) {
+            text("Hello".prop())
+        }
+    }
+
+fun String.prop() = StringProp.Builder(this).build()
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/EdgeButton.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/EdgeButton.kt
new file mode 100644
index 0000000..c34e8ca
--- /dev/null
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/EdgeButton.kt
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.material3
+
+import androidx.wear.protolayout.ColorBuilders.ColorProp
+import androidx.wear.protolayout.DimensionBuilders.DpProp
+import androidx.wear.protolayout.DimensionBuilders.dp
+import androidx.wear.protolayout.LayoutElementBuilders
+import androidx.wear.protolayout.LayoutElementBuilders.Box
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+import androidx.wear.protolayout.LayoutElementBuilders.VERTICAL_ALIGN_CENTER
+import androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignment
+import androidx.wear.protolayout.ModifiersBuilders.Background
+import androidx.wear.protolayout.ModifiersBuilders.Clickable
+import androidx.wear.protolayout.ModifiersBuilders.Corner
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers
+import androidx.wear.protolayout.ModifiersBuilders.Padding
+import androidx.wear.protolayout.TypeBuilders.StringProp
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.BOTTOM_MARGIN_DP
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.EDGE_BUTTON_HEIGHT_DP
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.HORIZONTAL_MARGIN_PERCENT
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.ICON_SIZE_DP
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.METADATA_TAG
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.TEXT_SIDE_PADDING_DP
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.TEXT_TOP_PADDING_DP
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.TOP_CORNER_RADIUS
+import androidx.wear.protolayout.material3.EdgeButtonDefaults.filled
+import androidx.wear.protolayout.material3.EdgeButtonStyle.Companion.DEFAULT
+import androidx.wear.protolayout.material3.EdgeButtonStyle.Companion.TOP_ALIGN
+
+/**
+ * ProtoLayout Material3 component edge button that offers a single slot to take an icon or similar
+ * round, small content.
+ *
+ * The edge button is intended to be used at the bottom of a round screen. It has a special shape
+ * with its bottom almost follows the screen's curvature. It has fixed height, and takes 1 line of
+ * text or a single icon. This button represents the most important action on the screen, and it
+ * must occupy the whole horizontal space in its position as well as being anchored to the screen
+ * bottom.
+ *
+ * This component is not intended to be used with an image background.
+ *
+ * @param onClick Associated [Clickable] for click events. When the button is clicked it will fire
+ *   the associated action.
+ * @param contentDescription The content description to be read by Talkback.
+ * @param colors The colors used for this button. If not set, [EdgeButtonDefaults.filled] will be
+ *   used as high emphasis button. Other recommended colors are [EdgeButtonDefaults.filledTonal] and
+ *   [EdgeButtonDefaults.filledVariant]. If using custom colors, it is important to choose a color
+ *   pair from same role to ensure accessibility with sufficient color contrast.
+ * @param iconContent The icon slot for content displayed in this button. It is recommended to use
+ *   default styling that is automatically provided by only calling [icon] with the resource ID.
+ * @sample androidx.wear.protolayout.material3.samples.edgeButtonSampleIcon
+ */
+// TODO(b/346958146): link EdgeButton visuals in DAC
+public fun MaterialScope.iconEdgeButton(
+    onClick: Clickable,
+    contentDescription: StringProp,
+    colors: EdgeButtonColors = filled(),
+    iconContent: (MaterialScope.() -> LayoutElement)
+): LayoutElement =
+    edgeButton(
+        onClick = onClick,
+        contentDescription = contentDescription,
+        colors = colors,
+        style = DEFAULT
+    ) {
+        withStyle(
+                defaultIconStyle = IconStyle(size = ICON_SIZE_DP.toDp(), tintColor = colors.content)
+            )
+            .iconContent()
+    }
+
+/**
+ * ProtoLayout Material3 component edge button that offers a single slot to take a text or similar
+ * long and wide content.
+ *
+ * The edge button is intended to be used at the bottom of a round screen. It has a special shape
+ * with its bottom almost follows the screen's curvature. It has fixed height, and takes 1 line of
+ * text or a single icon. This button represents the most important action on the screen, and it
+ * must occupy the whole horizontal space in its position as well as being anchored to the screen
+ * bottom.
+ *
+ * This component is not intended to be used with an image background.
+ *
+ * @param onClick Associated [Clickable] for click events. When the button is clicked it will fire
+ *   the associated action.
+ * @param contentDescription The content description to be read by Talkback.
+ * @param colors The colors used for this button. If not set, [EdgeButtonDefaults.filled] will be
+ *   used as high emphasis button. Other recommended colors are [EdgeButtonDefaults.filledTonal] and
+ *   [EdgeButtonDefaults.filledVariant]. If using custom colors, it is important to choose a color
+ *   pair from same role to ensure accessibility with sufficient color contrast.
+ * @param labelContent The label slot for content displayed in this button. It is recommended to use
+ *   default styling that is automatically provided by only calling [text] with the content.
+ * @sample androidx.wear.protolayout.material3.samples.edgeButtonSampleText
+ */
+// TODO(b/346958146): link EdgeButton visuals in DAC
+public fun MaterialScope.textEdgeButton(
+    onClick: Clickable,
+    contentDescription: StringProp,
+    colors: EdgeButtonColors = filled(),
+    labelContent: (MaterialScope.() -> LayoutElement)
+): LayoutElement =
+    edgeButton(
+        onClick = onClick,
+        contentDescription = contentDescription,
+        colors = colors,
+        style = TOP_ALIGN
+    ) {
+        withStyle(
+                defaultTextElementStyle =
+                    TextElementStyle(
+                        typography = Typography.LABEL_MEDIUM,
+                        color = colors.content,
+                        scalable = false
+                    )
+            )
+            .labelContent()
+    }
+
+/**
+ * ProtoLayout Material3 component edge button that offers a single slot to take any content.
+ *
+ * The edge button is intended to be used at the bottom of a round screen. It has a special shape
+ * with its bottom almost follows the screen's curvature. It has fixed height, and takes 1 line of
+ * text or a single icon. This button represents the most important action on the screen, and it
+ * must occupy the whole horizontal space in its position as well as being anchored to the screen
+ * bottom.
+ *
+ * This component is not intended to be used with an image background.
+ *
+ * @param onClick Associated [Clickable] for click events. When the button is clicked it will fire
+ *   the associated action.
+ * @param contentDescription The content description to be read by Talkback.
+ * @param colors The colors used for this button. If not set, [EdgeButtonDefaults.filled] will be
+ *   used as high emphasis button. Other recommended colors are [EdgeButtonDefaults.filledTonal] and
+ *   [EdgeButtonDefaults.filledVariant]. If using custom colors, it is important to choose a color
+ *   pair from same role to ensure accessibility with sufficient color contrast.
+ * @param style The style used for the inner content, specifying how the content should be aligned.
+ *   It is recommended to use [EdgeButtonStyle.TOP_ALIGN] for long, wide content. If not set,
+ *   defaults to [EdgeButtonStyle.DEFAULT] which center-aligns the content.
+ * @param content The inner content to be put inside of this edge button.
+ * @sample androidx.wear.protolayout.material3.samples.edgeButtonSampleIcon
+ */
+// TODO(b/346958146): link EdgeButton visuals in DAC
+internal fun MaterialScope.edgeButton(
+    onClick: Clickable,
+    contentDescription: StringProp,
+    colors: EdgeButtonColors,
+    style: EdgeButtonStyle = DEFAULT,
+    content: MaterialScope.() -> LayoutElement
+): LayoutElement {
+    val containerWidth = deviceConfiguration.screenWidthDp.toDp()
+    val edgeButtonWidth: Float =
+        (100f - 2f * HORIZONTAL_MARGIN_PERCENT) * deviceConfiguration.screenWidthDp / 100f
+    val bottomCornerRadiusX = dp(edgeButtonWidth / 2f)
+    val bottomCornerRadiusY = dp(EDGE_BUTTON_HEIGHT_DP - TOP_CORNER_RADIUS.value)
+
+    val modifiers: Modifiers.Builder =
+        Modifiers.Builder()
+            .setClickable(onClick)
+            .setSemantics(contentDescription.buttonRoleSemantics())
+            .setBackground(
+                Background.Builder()
+                    .setColor(colors.container)
+                    .setCorner(
+                        Corner.Builder()
+                            .setRadius(TOP_CORNER_RADIUS)
+                            .setBottomLeftRadius(bottomCornerRadiusX, bottomCornerRadiusY)
+                            .setBottomRightRadius(bottomCornerRadiusX, bottomCornerRadiusY)
+                            .build()
+                    )
+                    .build()
+            )
+
+    val button = Box.Builder().setHeight(EDGE_BUTTON_HEIGHT_DP.toDp()).setWidth(dp(edgeButtonWidth))
+    button
+        .setVerticalAlignment(style.verticalAlignment)
+        .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER)
+        .addContent(content())
+
+    style.padding?.let { modifiers.setPadding(it) }
+
+    return Box.Builder()
+        .setHeight((EDGE_BUTTON_HEIGHT_DP + BOTTOM_MARGIN_DP).toDp())
+        .setWidth(containerWidth)
+        .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_TOP)
+        .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER)
+        .addContent(button.setModifiers(modifiers.build()).build())
+        .setModifiers(Modifiers.Builder().setMetadata(METADATA_TAG.toElementMetadata()).build())
+        .build()
+}
+
+/** Provides style values for edge button component. */
+public class EdgeButtonStyle
+private constructor(
+    @VerticalAlignment internal val verticalAlignment: Int = VERTICAL_ALIGN_CENTER,
+    internal val padding: Padding? = null
+) {
+    public companion object {
+        /**
+         * Style variation for having content of the edge button anchored to the top.
+         *
+         * This should be used for text-like content, or the content that is wide, to accommodate
+         * for more space.
+         */
+        @JvmField
+        public val TOP_ALIGN: EdgeButtonStyle =
+            EdgeButtonStyle(
+                verticalAlignment = LayoutElementBuilders.VERTICAL_ALIGN_TOP,
+                padding =
+                    Padding.Builder()
+                        .setTop(TEXT_TOP_PADDING_DP.toDp())
+                        .setStart(TEXT_SIDE_PADDING_DP.toDp())
+                        .setEnd(TEXT_SIDE_PADDING_DP.toDp())
+                        .build()
+            )
+
+        /**
+         * Default style variation for having content of the edge button center aligned.
+         *
+         * This should be used for icon-like or small, round content that doesn't occupy a lot of
+         * space.
+         */
+        @JvmField public val DEFAULT: EdgeButtonStyle = EdgeButtonStyle()
+    }
+}
+
+public object EdgeButtonDefaults {
+    /**
+     * [EdgeButtonColors] for the high-emphasis button representing the primary, most important or
+     * most common action on a screen.
+     *
+     * These colors are using [ColorTokens.PRIMARY] for background color and
+     * [ColorTokens.ON_PRIMARY] for content color.
+     */
+    public fun MaterialScope.filled(): EdgeButtonColors =
+        EdgeButtonColors(getColorProp(ColorTokens.PRIMARY), getColorProp(ColorTokens.ON_PRIMARY))
+
+    /**
+     * [EdgeButtonColors] for the medium-emphasis button.
+     *
+     * These colors are using [ColorTokens.SURFACE_CONTAINER] for background color and
+     * [ColorTokens.ON_SURFACE] for content color.
+     */
+    public fun MaterialScope.filledTonal(): EdgeButtonColors =
+        EdgeButtonColors(
+            getColorProp(ColorTokens.SURFACE_CONTAINER),
+            getColorProp(ColorTokens.ON_SURFACE)
+        )
+
+    /**
+     * Alternative [EdgeButtonColors] for the high-emphasis button.
+     *
+     * These colors are using [ColorTokens.PRIMARY_CONTAINER] for background color and
+     * [ColorTokens.ON_PRIMARY_CONTAINER] for content color.
+     */
+    public fun MaterialScope.filledVariant(): EdgeButtonColors =
+        EdgeButtonColors(
+            getColorProp(ColorTokens.PRIMARY_CONTAINER),
+            getColorProp(ColorTokens.ON_PRIMARY_CONTAINER)
+        )
+
+    @JvmField internal val TOP_CORNER_RADIUS: DpProp = dp(17f)
+    internal const val HORIZONTAL_MARGIN_PERCENT: Float = 24f
+    internal const val BOTTOM_MARGIN_DP: Int = 3
+    internal const val EDGE_BUTTON_HEIGHT_DP: Int = 46
+    internal const val METADATA_TAG: String = "EB"
+    internal const val ICON_SIZE_DP = 24
+    internal const val TEXT_TOP_PADDING_DP = 12
+    internal const val TEXT_SIDE_PADDING_DP = 8
+}
+
+/** Represents the container and content colors used in [textEdgeButton] or [iconEdgeButton]. */
+public class EdgeButtonColors(
+    /** The container color to be used for a button. */
+    public val container: ColorProp,
+    /** The color or icon tint color to be used for all content within a button. */
+    public val content: ColorProp
+)
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt
index d05e82f..9f088c4 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt
@@ -16,6 +16,16 @@
 
 package androidx.wear.protolayout.material3
 
+import android.os.Build.VERSION.SDK_INT
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import androidx.annotation.Dimension
+import androidx.annotation.Dimension.Companion.SP
+import androidx.wear.protolayout.DimensionBuilders.dp
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata
+import androidx.wear.protolayout.ModifiersBuilders.SEMANTICS_ROLE_BUTTON
+import androidx.wear.protolayout.ModifiersBuilders.Semantics
+import androidx.wear.protolayout.TypeBuilders.StringProp
+import androidx.wear.protolayout.materialcore.fontscaling.FontScaleConverterFactory
 import java.nio.charset.StandardCharsets
 
 /** Returns byte array representation of tag from String. */
@@ -32,3 +42,20 @@
         yield(element)
     }
 }
+
+@Dimension(unit = SP)
+internal fun Float.dpToSp(fontScale: Float): Float =
+    (if (SDK_INT >= UPSIDE_DOWN_CAKE) FontScaleConverterFactory.forScale(fontScale) else null)
+        ?.convertDpToSp(this) ?: dpToSpLinear(fontScale)
+
+@Dimension(unit = SP)
+private fun Float.dpToSpLinear(fontScale: Float): Float {
+    return this / fontScale
+}
+
+internal fun StringProp.buttonRoleSemantics() =
+    Semantics.Builder().setContentDescription(this).setRole(SEMANTICS_ROLE_BUTTON).build()
+
+internal fun Int.toDp() = dp(this.toFloat())
+
+internal fun String.toElementMetadata() = ElementMetadata.Builder().setTagData(toTagBytes()).build()
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Icon.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Icon.kt
new file mode 100644
index 0000000..fe1775c
--- /dev/null
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Icon.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.material3
+
+import androidx.wear.protolayout.ColorBuilders.ColorProp
+import androidx.wear.protolayout.DimensionBuilders.ImageDimension
+import androidx.wear.protolayout.LayoutElementBuilders.ColorFilter
+import androidx.wear.protolayout.LayoutElementBuilders.Image
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+
+/**
+ * Returns the icon components with the defined style.
+ *
+ * Material components provide proper defaults for this icon. In order to take advantage of those,
+ * this should be used with the resource ID only: `icon("id")`.
+ *
+ * @param protoLayoutResourceId The protolayout resource id of the icon. Node that, this is not an
+ *   Android resource id.
+ * @param size The side of an icon that will be used for width and height.
+ * @param tintColor The color used to tint the icon.
+ */
+public fun MaterialScope.icon(
+    protoLayoutResourceId: String,
+    size: ImageDimension = defaultIconStyle.size,
+    tintColor: ColorProp = defaultIconStyle.tintColor,
+): LayoutElement =
+    Image.Builder()
+        .setResourceId(protoLayoutResourceId)
+        .setWidth(size)
+        .setHeight(size)
+        .setColorFilter(ColorFilter.Builder().setTint(tintColor).build())
+        .build()
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt
index 6d6a3cb..bb84681 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt
@@ -18,15 +18,20 @@
 
 import android.content.Context
 import android.provider.Settings
-import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope
 import androidx.annotation.VisibleForTesting
 import androidx.wear.protolayout.ColorBuilders.ColorProp
 import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters
+import androidx.wear.protolayout.DimensionBuilders.ImageDimension
 import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+import androidx.wear.protolayout.LayoutElementBuilders.TEXT_ALIGN_CENTER
+import androidx.wear.protolayout.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE
+import androidx.wear.protolayout.LayoutElementBuilders.TextAlignment
+import androidx.wear.protolayout.LayoutElementBuilders.TextOverflow
 import androidx.wear.protolayout.ModifiersBuilders.Corner
 import androidx.wear.protolayout.material3.ColorTokens.ColorToken
+import androidx.wear.protolayout.material3.ColorTokens.fromToken
 import androidx.wear.protolayout.material3.Shape.ShapeToken
+import androidx.wear.protolayout.material3.Typography.TypographyToken
 
 /**
  * Receiver scope which is used by all ProtoLayout Material3 components and layout to support
@@ -52,14 +57,30 @@
  * @param deviceConfiguration The device parameters for where the components will be rendered
  * @param theme The theme to be used. If not set, default Material theme will be applied
  * @param allowDynamicTheme If dynamic colors theme should be used on components, meaning that
+ * @param defaultTextElementStyle The opinionated text style that text component can use as defaults
  */
 internal constructor(
     internal val context: Context,
     /** The device parameters for where the components will be rendered. */
     public val deviceConfiguration: DeviceParameters,
     internal val theme: MaterialTheme = DEFAULT_MATERIAL_THEME,
-    internal val allowDynamicTheme: Boolean = true
-)
+    internal val allowDynamicTheme: Boolean = true,
+    internal val defaultTextElementStyle: TextElementStyle = TextElementStyle(),
+    internal val defaultIconStyle: IconStyle = IconStyle()
+) {
+    internal fun withStyle(
+        defaultTextElementStyle: TextElementStyle = this.defaultTextElementStyle,
+        defaultIconStyle: IconStyle = this.defaultIconStyle
+    ): MaterialScope =
+        MaterialScope(
+            context = context,
+            deviceConfiguration = deviceConfiguration,
+            theme = theme,
+            allowDynamicTheme = allowDynamicTheme,
+            defaultTextElementStyle = defaultTextElementStyle,
+            defaultIconStyle = defaultIconStyle
+        )
+}
 
 /**
  * Retrieves the [Corner] shape from the default Material theme with shape token name.
@@ -97,7 +118,7 @@
 
 /**
  * Creates a top-level receiver scope [MaterialScope] that calls the given [layout] to support for
- * opinionated defaults.
+ * opinionated defaults and building Material3 components and layout.
  *
  * @param context The Android Context for the Tile service
  * @param deviceConfiguration The device parameters for where the components will be rendered
@@ -107,9 +128,10 @@
  * @param layout Scoped slot for the content of layout to be displayed
  */
 // TODO: b/369350414 - Allow for overriding colors theme.
+// TODO: b/370976767 - Specify in docs that MaterialTileService should be used instead of using this
+// directly.
 @JvmOverloads
-@RestrictTo(Scope.LIBRARY_GROUP)
-public fun primaryScope(
+public fun materialScope(
     context: Context,
     deviceConfiguration: DeviceParameters,
     allowDynamicTheme: Boolean = true,
@@ -128,3 +150,19 @@
 @VisibleForTesting
 internal const val THEME_CUSTOMIZATION_OVERLAY_PACKAGES: String =
     "theme_customization_overlay_packages"
+
+internal class TextElementStyle(
+    @TypographyToken val typography: Int = Typography.BODY_MEDIUM,
+    val color: ColorProp = fromToken(ColorTokens.PRIMARY),
+    val italic: Boolean = false,
+    val underline: Boolean = false,
+    val scalable: Boolean = true,
+    val maxLines: Int = 1,
+    @TextAlignment val multilineAlignment: Int = TEXT_ALIGN_CENTER,
+    @TextOverflow val overflow: Int = TEXT_OVERFLOW_ELLIPSIZE,
+)
+
+internal class IconStyle(
+    val size: ImageDimension = 24.toDp(),
+    val tintColor: ColorProp = fromToken(ColorTokens.PRIMARY),
+)
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialTheme.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialTheme.kt
index 74c6ac0..11887c9 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialTheme.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialTheme.kt
@@ -17,8 +17,10 @@
 package androidx.wear.protolayout.material3
 
 import android.annotation.SuppressLint
-import androidx.annotation.VisibleForTesting
 import androidx.wear.protolayout.ColorBuilders.ColorProp
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters
+import androidx.wear.protolayout.DimensionBuilders.SpProp
+import androidx.wear.protolayout.DimensionBuilders.sp
 import androidx.wear.protolayout.LayoutElementBuilders.FontStyle
 import androidx.wear.protolayout.ModifiersBuilders.Corner
 import androidx.wear.protolayout.material3.ColorTokens.ColorToken
@@ -53,7 +55,7 @@
 internal class MaterialTheme(private val customColorScheme: Map<Int, ColorProp> = emptyMap()) {
     /** Retrieves the [FontStyle.Builder] with the typography name. */
     internal fun getFontStyleBuilder(@TypographyToken typographyToken: Int) =
-        createFontStyleBuilder(textStyle = Typography.fromToken(typographyToken))
+        createFontStyleBuilder(typographyToken)
 
     /** Retrieves the line height with the typography name. */
     internal fun getLineHeight(@TypographyToken typographyToken: Int) =
@@ -70,11 +72,22 @@
 /** MaterialTheme that uses predefined default values without any customization. */
 @JvmField internal val DEFAULT_MATERIAL_THEME: MaterialTheme = MaterialTheme(emptyMap())
 
-@VisibleForTesting
 @SuppressLint("ResourceType")
-internal fun createFontStyleBuilder(textStyle: TextStyle): FontStyle.Builder {
+internal fun createFontStyleBuilder(
+    @TypographyToken typographyToken: Int,
+    deviceConfiguration: DeviceParameters? = null,
+    isScalable: Boolean = true
+): FontStyle.Builder {
+    val textStyle: TextStyle = Typography.fromToken(typographyToken)
+    val sizeSp: SpProp = textStyle.size
     return FontStyle.Builder()
-        .setSize(textStyle.size)
+        .setSize(
+            if (!isScalable && deviceConfiguration != null) {
+                sp(sizeSp.value.dpToSp(deviceConfiguration.fontScale))
+            } else {
+                sizeSp
+            }
+        )
         .setLetterSpacing(textStyle.letterSpacing)
         .setSettings(*textStyle.fontSettings.toTypedArray())
         .setPreferredFontFamilies(textStyle.fontFamily)
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Text.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Text.kt
new file mode 100644
index 0000000..a42427f
--- /dev/null
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Text.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.material3
+
+import androidx.wear.protolayout.ColorBuilders.ColorProp
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
+import androidx.wear.protolayout.LayoutElementBuilders.Text
+import androidx.wear.protolayout.LayoutElementBuilders.TextAlignment
+import androidx.wear.protolayout.LayoutElementBuilders.TextOverflow
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers
+import androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint
+import androidx.wear.protolayout.TypeBuilders.StringProp
+import androidx.wear.protolayout.material3.Typography.TypographyToken
+
+/**
+ * ProtoLayout component that represents text object holding any information.
+ *
+ * There are pre-defined typography styles that can be obtained from Materials token system in
+ * [Typography].
+ *
+ * @param text The text content for this component.
+ * @param stringLayoutConstraint The layout constraints used to correctly measure Text view size and
+ *   align text when `text` has dynamic value.
+ * @param typography The typography from [Typography] to be applied to this text. This will have
+ *   predefined default value specified by each components that uses this text, to achieve the
+ *   recommended look.
+ * @param color The color to be applied to this text. It is recommended to use predefined default
+ *   styles created by each component or `getColorProp(token)`.
+ * @param italic Whether text should be displayed as italic.
+ * @param underline Whether text should be displayed as underlined.
+ * @param scalable Whether text should scale with the user font size.
+ * @param maxLines The maximum number of lines that text can occupy.
+ * @param multilineAlignment The horizontal alignment of the multiple lines of text.
+ * @param overflow The overflow strategy when text doesn't have enough space to be shown.
+ * @param modifiers The additional [Modifiers] for this text.
+ * @sample androidx.wear.protolayout.material3.samples.helloWorldTextDefault
+ * @sample androidx.wear.protolayout.material3.samples.helloWorldTextDynamicCustom
+ */
+public fun MaterialScope.text(
+    text: StringProp,
+    stringLayoutConstraint: StringLayoutConstraint =
+        StringLayoutConstraint.Builder(text.value).build(),
+    @TypographyToken typography: Int = defaultTextElementStyle.typography,
+    color: ColorProp = defaultTextElementStyle.color,
+    italic: Boolean = defaultTextElementStyle.italic,
+    underline: Boolean = defaultTextElementStyle.underline,
+    scalable: Boolean = defaultTextElementStyle.scalable,
+    maxLines: Int = defaultTextElementStyle.maxLines,
+    @TextAlignment multilineAlignment: Int = defaultTextElementStyle.multilineAlignment,
+    @TextOverflow overflow: Int = defaultTextElementStyle.overflow,
+    modifiers: Modifiers = Modifiers.Builder().build()
+): LayoutElement =
+    Text.Builder()
+        .setText(text)
+        .setLayoutConstraintsForDynamicText(stringLayoutConstraint)
+        .setFontStyle(
+            createFontStyleBuilder(typographyToken = typography, deviceConfiguration, scalable)
+                .setColor(color)
+                .setItalic(italic)
+                .setUnderline(underline)
+                .build()
+        )
+        .setMaxLines(maxLines)
+        .setMultilineAlignment(multilineAlignment)
+        .setOverflow(overflow)
+        .setModifiers(modifiers)
+        .build()
diff --git a/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonGroupTest.kt b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonGroupTest.kt
index 7c9300d..b991e06 100644
--- a/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonGroupTest.kt
+++ b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonGroupTest.kt
@@ -36,7 +36,7 @@
         val element2 = Box.Builder().setWidth(expand()).build()
 
         val buttonGroup =
-            primaryScope(
+            materialScope(
                 context = ApplicationProvider.getApplicationContext(),
                 deviceConfiguration = DEVICE_PARAMETERS
             ) {