Skip to content

Commit 09f9d7b

Browse files
authored
feat(theme): add wallpaper-derived dynamic accent for buttons (#1098)
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 <git@bmcreations.dev>
1 parent eac9851 commit 09f9d7b

3 files changed

Lines changed: 66 additions & 3 deletions

File tree

‎apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/CurrencyCreatorFlowScreen.kt‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.flipcash.app.currencycreator
22

3+
import androidx.compose.foundation.background
34
import androidx.compose.foundation.layout.Column
45
import androidx.compose.foundation.layout.fillMaxSize
56
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.shape.CircleShape
68
import androidx.compose.material3.Text
79
import androidx.compose.runtime.Composable
810
import androidx.compose.runtime.CompositionLocalProvider
@@ -63,6 +65,7 @@ import com.getcode.opencode.model.financial.Fiat
6365
import com.getcode.opencode.model.financial.LocalFiat
6466
import com.getcode.opencode.model.financial.toFiat
6567
import com.getcode.theme.CodeTheme
68+
import com.getcode.theme.rememberDynamicAccent
6669
import com.getcode.ui.core.unboundedClickable
6770

6871
@Composable
@@ -136,15 +139,24 @@ fun CurrencyCreatorFlowScreen(
136139
endContent = when (state.currentStep) {
137140
is CurrencyCreatorStep.BillCustomization -> {
138141
{
142+
val (accent, onAccent) = rememberDynamicAccent(
143+
fallbackAccent = CodeTheme.colors.secondary,
144+
fallbackOnAccent = CodeTheme.colors.onAction,
145+
)
146+
139147
Text(
140-
text = stringResource(R.string.action_done),
148+
text = stringResource(R.string.action_next),
141149
style = CodeTheme.typography.textMedium,
142-
color = Color.White,
150+
color = onAccent,
143151
modifier = Modifier
152+
.background(accent, shape = CircleShape)
144153
.unboundedClickable {
145154
topBarController.onEndAction?.invoke()
146155
}
147-
.padding(5.dp),
156+
.padding(
157+
horizontal = CodeTheme.dimens.grid.x2,
158+
vertical = CodeTheme.dimens.grid.x1
159+
),
148160
)
149161
}
150162
}

‎ui/theme/build.gradle.kts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ android {
1313
dependencies { debugImplementation(libs.compose.ui.tools)
1414
implementation(libs.compose.ui.tools.preview)
1515
implementation(libs.compose.material)
16+
implementation(libs.compose.material3)
1617
implementation(libs.compose.accompanist)
1718

1819
implementation(libs.androidx.appcompat)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.getcode.theme
2+
3+
import android.os.Build
4+
import androidx.compose.material3.dynamicDarkColorScheme
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.remember
7+
import androidx.compose.ui.graphics.Color
8+
import androidx.compose.ui.platform.LocalContext
9+
10+
/**
11+
* The user's Material You (Monet) accent color, derived from their wallpaper / system theme.
12+
*
13+
* On Android 12+ (API 31) this reads the wallpaper-derived tonal palette via Material3's
14+
* [dynamicDarkColorScheme] and returns its [accent] (`primary`) and [onAccent] (`onPrimary`).
15+
* The app is dark-only, so we always take the *dark* dynamic scheme regardless of the system
16+
* light/dark setting — its tonal steps read correctly on our dark surfaces.
17+
*
18+
* On API < 31 both fields fall back to [fallbackAccent] / [fallbackOnAccent].
19+
*
20+
* Usage:
21+
* ```
22+
* val (accent, onAccent) = rememberDynamicAccent(
23+
* fallbackAccent = CodeTheme.colors.secondary,
24+
* fallbackOnAccent = CodeTheme.colors.onAction,
25+
* )
26+
* Button(colors = ButtonDefaults.buttonColors(containerColor = accent)) {
27+
* Text("Save", color = onAccent)
28+
* }
29+
* ```
30+
*/
31+
@Composable
32+
fun rememberDynamicAccent(
33+
fallbackAccent: Color,
34+
fallbackOnAccent: Color = Color.White,
35+
): DynamicAccent {
36+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
37+
return DynamicAccent(accent = fallbackAccent, onAccent = fallbackOnAccent)
38+
}
39+
val context = LocalContext.current
40+
return remember(context) {
41+
val scheme = dynamicDarkColorScheme(context)
42+
DynamicAccent(accent = scheme.primary, onAccent = scheme.onPrimary)
43+
}
44+
}
45+
46+
/** A wallpaper-derived accent paired with the content color that reads on top of it. */
47+
data class DynamicAccent(
48+
val accent: Color,
49+
val onAccent: Color,
50+
)

0 commit comments

Comments
 (0)