From f8cc621788fae52f67d4679033a5b0c9fb064ceb Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Mon, 20 Jul 2026 13:49:52 -0400 Subject: [PATCH] feat(theme): add wallpaper-derived dynamic accent for buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add rememberDynamicAccent(), which reads the user's Material You (Monet) palette via Material3 dynamicDarkColorScheme() on Android 12+ and falls back to brand colors below API 31. Pulls compose-material3 into :ui:theme as the first step toward a broader Material3 migration. Use it to color the currency creator's bill-customization action button (now "Next") with the system accent, à la Google Photos' save action. Signed-off-by: Brandon McAnsh --- .../CurrencyCreatorFlowScreen.kt | 18 +++++-- ui/theme/build.gradle.kts | 1 + .../kotlin/com/getcode/theme/DynamicColor.kt | 50 +++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 ui/theme/src/main/kotlin/com/getcode/theme/DynamicColor.kt diff --git a/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/CurrencyCreatorFlowScreen.kt b/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/CurrencyCreatorFlowScreen.kt index 33dbd589c0..dce8ec39cf 100644 --- a/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/CurrencyCreatorFlowScreen.kt +++ b/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/CurrencyCreatorFlowScreen.kt @@ -1,8 +1,10 @@ package com.flipcash.app.currencycreator +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -63,6 +65,7 @@ import com.getcode.opencode.model.financial.Fiat import com.getcode.opencode.model.financial.LocalFiat import com.getcode.opencode.model.financial.toFiat import com.getcode.theme.CodeTheme +import com.getcode.theme.rememberDynamicAccent import com.getcode.ui.core.unboundedClickable @Composable @@ -136,15 +139,24 @@ fun CurrencyCreatorFlowScreen( endContent = when (state.currentStep) { is CurrencyCreatorStep.BillCustomization -> { { + val (accent, onAccent) = rememberDynamicAccent( + fallbackAccent = CodeTheme.colors.secondary, + fallbackOnAccent = CodeTheme.colors.onAction, + ) + Text( - text = stringResource(R.string.action_done), + text = stringResource(R.string.action_next), style = CodeTheme.typography.textMedium, - color = Color.White, + color = onAccent, modifier = Modifier + .background(accent, shape = CircleShape) .unboundedClickable { topBarController.onEndAction?.invoke() } - .padding(5.dp), + .padding( + horizontal = CodeTheme.dimens.grid.x2, + vertical = CodeTheme.dimens.grid.x1 + ), ) } } diff --git a/ui/theme/build.gradle.kts b/ui/theme/build.gradle.kts index 56d6e90cfa..9407d9c15e 100644 --- a/ui/theme/build.gradle.kts +++ b/ui/theme/build.gradle.kts @@ -13,6 +13,7 @@ android { dependencies { debugImplementation(libs.compose.ui.tools) implementation(libs.compose.ui.tools.preview) implementation(libs.compose.material) + implementation(libs.compose.material3) implementation(libs.compose.accompanist) implementation(libs.androidx.appcompat) diff --git a/ui/theme/src/main/kotlin/com/getcode/theme/DynamicColor.kt b/ui/theme/src/main/kotlin/com/getcode/theme/DynamicColor.kt new file mode 100644 index 0000000000..d6eb61ad9e --- /dev/null +++ b/ui/theme/src/main/kotlin/com/getcode/theme/DynamicColor.kt @@ -0,0 +1,50 @@ +package com.getcode.theme + +import android.os.Build +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext + +/** + * The user's Material You (Monet) accent color, derived from their wallpaper / system theme. + * + * On Android 12+ (API 31) this reads the wallpaper-derived tonal palette via Material3's + * [dynamicDarkColorScheme] and returns its [accent] (`primary`) and [onAccent] (`onPrimary`). + * The app is dark-only, so we always take the *dark* dynamic scheme regardless of the system + * light/dark setting — its tonal steps read correctly on our dark surfaces. + * + * On API < 31 both fields fall back to [fallbackAccent] / [fallbackOnAccent]. + * + * Usage: + * ``` + * val (accent, onAccent) = rememberDynamicAccent( + * fallbackAccent = CodeTheme.colors.secondary, + * fallbackOnAccent = CodeTheme.colors.onAction, + * ) + * Button(colors = ButtonDefaults.buttonColors(containerColor = accent)) { + * Text("Save", color = onAccent) + * } + * ``` + */ +@Composable +fun rememberDynamicAccent( + fallbackAccent: Color, + fallbackOnAccent: Color = Color.White, +): DynamicAccent { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + return DynamicAccent(accent = fallbackAccent, onAccent = fallbackOnAccent) + } + val context = LocalContext.current + return remember(context) { + val scheme = dynamicDarkColorScheme(context) + DynamicAccent(accent = scheme.primary, onAccent = scheme.onPrimary) + } +} + +/** A wallpaper-derived accent paired with the content color that reads on top of it. */ +data class DynamicAccent( + val accent: Color, + val onAccent: Color, +)