Skip to content

Commit c720861

Browse files
committed
Migrate JetSnack to use Styles
1 parent 17698dd commit c720861

20 files changed

Lines changed: 331 additions & 191 deletions

File tree

Jetsnack/app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ android {
8686
kotlin {
8787
compilerOptions {
8888
jvmTarget = JvmTarget.fromTarget("17")
89+
freeCompilerArgs.add("-opt-in=androidx.compose.foundation.style.ExperimentalFoundationStyleApi")
8990
}
9091
}
9192
compileOptions {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package androidx.compose.material3
2+
3+
import androidx.compose.runtime.ProvidableCompositionLocal
4+
import java.lang.reflect.Method
5+
6+
// This file is only because we don't have this yet implemented in Material 3 theming.
7+
8+
// This will not be how it's done when its released :)
9+
10+
val LocalShapes: ProvidableCompositionLocal<Shapes>
11+
get() = getInternalLocal(
12+
className = "androidx.compose.material3.ShapesKt",
13+
methodName = "getLocalShapes",
14+
readableName = "LocalShapes"
15+
)
16+
17+
val LocalColorScheme: ProvidableCompositionLocal<ColorScheme>
18+
get() = getInternalLocal(
19+
className = "androidx.compose.material3.ColorSchemeKt",
20+
methodName = "getLocalColorScheme",
21+
readableName = "LocalColorScheme"
22+
)
23+
24+
val LocalTypography: ProvidableCompositionLocal<Typography>
25+
get() = getInternalLocal(
26+
className = "androidx.compose.material3.TypographyKt",
27+
methodName = "getLocalTypography",
28+
readableName = "LocalTypography"
29+
)
30+
31+
private fun <T> getInternalLocal(className: String, methodName: String, readableName: String): ProvidableCompositionLocal<T> {
32+
try {
33+
val clazz = Class.forName(className)
34+
val method: Method = clazz.getDeclaredMethod(methodName)
35+
method.isAccessible = true
36+
@Suppress("UNCHECKED_CAST")
37+
return method.invoke(null) as ProvidableCompositionLocal<T>
38+
} catch (e: Exception) {
39+
throw RuntimeException("Failed to access internal $readableName via reflection", e)
40+
}
41+
}

Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Button.kt

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
@file:OptIn(ExperimentalFoundationStyleApi::class)
18+
1719
package com.example.jetsnack.ui.components
1820

1921
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -29,6 +31,10 @@ import androidx.compose.foundation.layout.RowScope
2931
import androidx.compose.foundation.layout.defaultMinSize
3032
import androidx.compose.foundation.layout.padding
3133
import androidx.compose.foundation.shape.RoundedCornerShape
34+
import androidx.compose.foundation.style.ExperimentalFoundationStyleApi
35+
import androidx.compose.foundation.style.Style
36+
import androidx.compose.foundation.style.rememberUpdatedStyleState
37+
import androidx.compose.foundation.style.then
3238
import androidx.compose.material3.ButtonDefaults
3339
import androidx.compose.material3.MaterialTheme
3440
import androidx.compose.material3.ProvideTextStyle
@@ -47,33 +53,23 @@ import androidx.compose.ui.semantics.Role
4753
import androidx.compose.ui.tooling.preview.Preview
4854
import com.example.jetsnack.ui.theme.JetsnackTheme
4955

56+
//todo think about the text style here
5057
@Composable
5158
fun JetsnackButton(
5259
onClick: () -> Unit,
5360
modifier: Modifier = Modifier,
61+
style: Style = Style,
5462
enabled: Boolean = true,
5563
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
56-
shape: Shape = ButtonShape,
57-
border: BorderStroke? = null,
58-
backgroundGradient: List<Color> = JetsnackTheme.colors.interactivePrimary,
59-
disabledBackgroundGradient: List<Color> = JetsnackTheme.colors.interactiveSecondary,
60-
contentColor: Color = JetsnackTheme.colors.textInteractive,
61-
disabledContentColor: Color = JetsnackTheme.colors.textHelp,
62-
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
6364
content: @Composable RowScope.() -> Unit,
6465
) {
66+
val styleState = rememberUpdatedStyleState(interactionSource, {
67+
it.isEnabled = enabled
68+
})
6569
JetsnackSurface(
66-
shape = shape,
67-
color = Color.Transparent,
68-
contentColor = if (enabled) contentColor else disabledContentColor,
69-
border = border,
70+
style = JetsnackTheme.appStyles.buttonStyle then style,
71+
styleState = styleState,
7072
modifier = modifier
71-
.clip(shape)
72-
.background(
73-
Brush.horizontalGradient(
74-
colors = if (enabled) backgroundGradient else disabledBackgroundGradient,
75-
),
76-
)
7773
.clickable(
7874
onClick = onClick,
7975
enabled = enabled,
@@ -91,8 +87,7 @@ fun JetsnackButton(
9187
minWidth = ButtonDefaults.MinWidth,
9288
minHeight = ButtonDefaults.MinHeight,
9389
)
94-
.indication(interactionSource, ripple())
95-
.padding(contentPadding),
90+
.indication(interactionSource, ripple()),
9691
horizontalArrangement = Arrangement.Center,
9792
verticalAlignment = Alignment.CenterVertically,
9893
content = content,
@@ -101,8 +96,6 @@ fun JetsnackButton(
10196
}
10297
}
10398

104-
private val ButtonShape = RoundedCornerShape(percent = 50)
105-
10699
@Preview("default", "round")
107100
@Preview("dark theme", "round", uiMode = UI_MODE_NIGHT_YES)
108101
@Preview("large font", "round", fontScale = 2f)
@@ -122,7 +115,9 @@ private fun ButtonPreview() {
122115
private fun RectangleButtonPreview() {
123116
JetsnackTheme {
124117
JetsnackButton(
125-
onClick = {}, shape = RectangleShape,
118+
onClick = {}, style = {
119+
shape(RectangleShape)
120+
},
126121
) {
127122
Text(text = "Demo")
128123
}

Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Card.kt

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17+
@file:OptIn(ExperimentalFoundationStyleApi::class)
18+
1719
package com.example.jetsnack.ui.components
1820

1921
import android.content.res.Configuration
2022
import androidx.compose.foundation.BorderStroke
2123
import androidx.compose.foundation.layout.padding
24+
import androidx.compose.foundation.style.ExperimentalFoundationStyleApi
25+
import androidx.compose.foundation.style.Style
26+
import androidx.compose.foundation.style.then
2227
import androidx.compose.material3.MaterialTheme
2328
import androidx.compose.material3.Text
2429
import androidx.compose.runtime.Composable
@@ -33,20 +38,12 @@ import com.example.jetsnack.ui.theme.JetsnackTheme
3338
@Composable
3439
fun JetsnackCard(
3540
modifier: Modifier = Modifier,
36-
shape: Shape = MaterialTheme.shapes.medium,
37-
color: Color = JetsnackTheme.colors.uiBackground,
38-
contentColor: Color = JetsnackTheme.colors.textPrimary,
39-
border: BorderStroke? = null,
40-
elevation: Dp = 4.dp,
41+
style: Style = Style,
4142
content: @Composable () -> Unit,
4243
) {
4344
JetsnackSurface(
4445
modifier = modifier,
45-
shape = shape,
46-
color = color,
47-
contentColor = contentColor,
48-
elevation = elevation,
49-
border = border,
46+
style = JetsnackTheme.appStyles.cardStyle then style,
5047
content = content,
5148
)
5249
}

Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Divider.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,33 @@
1414
* limitations under the License.
1515
*/
1616

17+
@file:OptIn(ExperimentalFoundationStyleApi::class)
18+
1719
package com.example.jetsnack.ui.components
1820

1921
import android.content.res.Configuration
2022
import androidx.compose.foundation.layout.Box
2123
import androidx.compose.foundation.layout.size
24+
import androidx.compose.foundation.style.ExperimentalFoundationStyleApi
25+
import androidx.compose.foundation.style.Style
26+
import androidx.compose.foundation.style.styleable
2227
import androidx.compose.material3.HorizontalDivider
2328
import androidx.compose.runtime.Composable
2429
import androidx.compose.ui.Alignment
2530
import androidx.compose.ui.Modifier
26-
import androidx.compose.ui.graphics.Color
2731
import androidx.compose.ui.tooling.preview.Preview
28-
import androidx.compose.ui.unit.Dp
2932
import androidx.compose.ui.unit.dp
3033
import com.example.jetsnack.ui.theme.JetsnackTheme
34+
import com.example.jetsnack.ui.theme.LocalAppStyles
3135

3236
@Composable
3337
fun JetsnackDivider(
3438
modifier: Modifier = Modifier,
35-
color: Color = JetsnackTheme.colors.uiBorder.copy(alpha = DividerAlpha),
36-
thickness: Dp = 1.dp,
39+
style: Style = Style
3740
) {
38-
HorizontalDivider(
39-
modifier = modifier,
40-
color = color,
41-
thickness = thickness,
42-
)
41+
Box(modifier = modifier.styleable(null, LocalAppStyles.current.dividerStyle, style))
4342
}
4443

45-
private const val DividerAlpha = 0.12f
4644

4745
@Preview("default", showBackground = true)
4846
@Preview("dark theme", uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)

Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Filters.kt

Lines changed: 53 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,15 @@
1414
* limitations under the License.
1515
*/
1616

17-
@file:OptIn(ExperimentalSharedTransitionApi::class)
17+
@file:OptIn(ExperimentalSharedTransitionApi::class, ExperimentalFoundationStyleApi::class)
1818

1919
package com.example.jetsnack.ui.components
2020

2121
import android.content.res.Configuration
2222
import androidx.compose.animation.AnimatedVisibility
2323
import androidx.compose.animation.ExperimentalSharedTransitionApi
2424
import androidx.compose.animation.SharedTransitionScope
25-
import androidx.compose.animation.animateColorAsState
26-
import androidx.compose.foundation.background
2725
import androidx.compose.foundation.interaction.MutableInteractionSource
28-
import androidx.compose.foundation.interaction.collectIsPressedAsState
2926
import androidx.compose.foundation.layout.Arrangement
3027
import androidx.compose.foundation.layout.Box
3128
import androidx.compose.foundation.layout.PaddingValues
@@ -35,17 +32,24 @@ import androidx.compose.foundation.lazy.LazyRow
3532
import androidx.compose.foundation.lazy.items
3633
import androidx.compose.foundation.selection.toggleable
3734
import androidx.compose.foundation.shape.CircleShape
35+
import androidx.compose.foundation.style.ExperimentalFoundationStyleApi
36+
import androidx.compose.foundation.style.Style
37+
import androidx.compose.foundation.style.pressed
38+
import androidx.compose.foundation.style.rememberUpdatedStyleState
39+
import androidx.compose.foundation.style.styleable
40+
import androidx.compose.foundation.style.then
3841
import androidx.compose.material3.Icon
3942
import androidx.compose.material3.IconButton
43+
import androidx.compose.material3.LocalShapes
4044
import androidx.compose.material3.MaterialTheme
4145
import androidx.compose.material3.Text
4246
import androidx.compose.runtime.Composable
43-
import androidx.compose.runtime.getValue
4447
import androidx.compose.runtime.remember
4548
import androidx.compose.ui.Alignment
4649
import androidx.compose.ui.Modifier
50+
import androidx.compose.ui.graphics.Brush
4751
import androidx.compose.ui.graphics.Color
48-
import androidx.compose.ui.graphics.Shape
52+
import androidx.compose.ui.graphics.TileMode
4953
import androidx.compose.ui.res.painterResource
5054
import androidx.compose.ui.res.stringResource
5155
import androidx.compose.ui.tooling.preview.Preview
@@ -54,6 +58,7 @@ import com.example.jetsnack.R
5458
import com.example.jetsnack.model.Filter
5559
import com.example.jetsnack.ui.FilterSharedElementKey
5660
import com.example.jetsnack.ui.theme.JetsnackTheme
61+
import com.example.jetsnack.ui.theme.LocalJetsnackColors
5762

5863
@Composable
5964
fun FilterBar(
@@ -93,59 +98,62 @@ fun FilterBar(
9398
}
9499
}
95100
items(filters) { filter ->
96-
FilterChip(filter = filter, shape = MaterialTheme.shapes.small)
101+
FilterChip(
102+
filter = filter,
103+
style = Style {
104+
shape(LocalShapes.currentValue.small)
105+
},
106+
)
97107
}
98108
}
99109
}
100110
}
101111

102112
@Composable
103-
fun FilterChip(filter: Filter, modifier: Modifier = Modifier, shape: Shape = MaterialTheme.shapes.small) {
113+
fun FilterChip(
114+
filter: Filter,
115+
modifier: Modifier = Modifier,
116+
style: Style = Style,
117+
) {
118+
104119
val (selected, setSelected) = filter.enabled
105-
val backgroundColor by animateColorAsState(
106-
if (selected) JetsnackTheme.colors.brandSecondary else JetsnackTheme.colors.uiBackground,
107-
label = "background color",
108-
)
109-
val border = Modifier.fadeInDiagonalGradientBorder(
110-
showBorder = !selected,
111-
colors = JetsnackTheme.colors.interactiveSecondary,
112-
shape = shape,
113-
)
114-
val textColor by animateColorAsState(
115-
if (selected) Color.Black else JetsnackTheme.colors.textSecondary,
116-
label = "text color",
120+
val interactionSource = remember { MutableInteractionSource() }
121+
val styleState = rememberUpdatedStyleState(
122+
interactionSource,
123+
{
124+
it.isSelected = selected
125+
},
117126
)
118127

119128
JetsnackSurface(
120-
modifier = modifier,
121-
color = backgroundColor,
122-
contentColor = textColor,
123-
shape = shape,
124-
elevation = 2.dp,
129+
modifier = modifier
130+
.toggleable(
131+
value = selected,
132+
onValueChange = setSelected,
133+
interactionSource = interactionSource,
134+
indication = null,
135+
),
136+
style = JetsnackTheme.appStyles.filterChipStyle then style,
137+
styleState = styleState,
125138
) {
126-
val interactionSource = remember { MutableInteractionSource() }
127-
128-
val pressed by interactionSource.collectIsPressedAsState()
129-
val backgroundPressed =
130-
if (pressed) {
131-
Modifier.offsetGradientBackground(
132-
JetsnackTheme.colors.interactiveSecondary,
133-
200f,
134-
0f,
135-
)
136-
} else {
137-
Modifier.background(Color.Transparent)
139+
val innerBackgroundStyle = Style {
140+
background(Color.Transparent)
141+
pressed {
142+
animate {
143+
background(
144+
Brush.horizontalGradient(
145+
colors = LocalJetsnackColors.currentValue.interactiveSecondary,
146+
startX = 0f,
147+
endX = 200f,
148+
tileMode = TileMode.Mirror,
149+
),
150+
)
151+
}
138152
}
153+
}
139154
Box(
140155
modifier = Modifier
141-
.toggleable(
142-
value = selected,
143-
onValueChange = setSelected,
144-
interactionSource = interactionSource,
145-
indication = null,
146-
)
147-
.then(backgroundPressed)
148-
.then(border),
156+
.styleable(styleState, innerBackgroundStyle),
149157
) {
150158
Text(
151159
text = filter.name,

0 commit comments

Comments
 (0)