Is this enough to change the color of a switch in ...
# compose
c
Is this enough to change the color of a switch in m3 ? I'm just doing a one off color change so I don't have a bunch of themes values to set. All I want is a "blue" swtich. I fee like I'm going crazy because the "off" state of the switch looks slightly purple, and so I'm not sure if that means I'm inheriting the default purple from compose m3 or if that gray just looks purple-ish
Copy code
MaterialTheme(colorScheme = lightColorScheme(primary = Color(0xFF89CFF0))) {
    Switch(
        checked = isChecked,
        onCheckedChange = { isChecked = it }
    )
}
d
The default for switch colors is this monster:
Copy code
SwitchColors(
    checkedThumbColor = fromToken(SwitchTokens.SelectedHandleColor),
    checkedTrackColor = fromToken(SwitchTokens.SelectedTrackColor),
    checkedBorderColor = Color.Transparent,
    checkedIconColor = fromToken(SwitchTokens.SelectedIconColor),
    uncheckedThumbColor = fromToken(SwitchTokens.UnselectedHandleColor),
    uncheckedTrackColor = fromToken(SwitchTokens.UnselectedTrackColor),
    uncheckedBorderColor =
        fromToken(SwitchTokens.UnselectedFocusTrackOutlineColor),
    uncheckedIconColor = fromToken(SwitchTokens.UnselectedIconColor),
    disabledCheckedThumbColor =
        fromToken(SwitchTokens.DisabledSelectedHandleColor)
            .copy(alpha = SwitchTokens.DisabledSelectedHandleOpacity)
            .compositeOver(surface),
    disabledCheckedTrackColor =
        fromToken(SwitchTokens.DisabledSelectedTrackColor)
            .copy(alpha = SwitchTokens.DisabledTrackOpacity)
            .compositeOver(surface),
    disabledCheckedBorderColor = Color.Transparent,
    disabledCheckedIconColor =
        fromToken(SwitchTokens.DisabledSelectedIconColor)
            .copy(alpha = SwitchTokens.DisabledSelectedIconOpacity)
            .compositeOver(surface),
    disabledUncheckedThumbColor =
        fromToken(SwitchTokens.DisabledUnselectedHandleColor)
            .copy(alpha = SwitchTokens.DisabledUnselectedHandleOpacity)
            .compositeOver(surface),
    disabledUncheckedTrackColor =
        fromToken(SwitchTokens.DisabledUnselectedTrackColor)
            .copy(alpha = SwitchTokens.DisabledTrackOpacity)
            .compositeOver(surface),
    disabledUncheckedBorderColor =
        fromToken(SwitchTokens.DisabledUnselectedTrackOutlineColor)
            .copy(alpha = SwitchTokens.DisabledTrackOpacity)
            .compositeOver(surface),
    disabledUncheckedIconColor =
        fromToken(SwitchTokens.DisabledUnselectedIconColor)
            .copy(alpha = SwitchTokens.DisabledUnselectedIconOpacity)
            .compositeOver(surface),
)
f
The material3
Switch
has a
colors
property that we use at our company to “theme” the switch. I think the approach you shared doesn’t take into account light/dark/adaptive themes? I think what i would do is wrap the material 3 switch and set your colors with the color property:
Copy code
Switch(
    checked = checked,
    enabled = enabled,
    onCheckedChange = onCheckedChange,
    thumbContent = thumbContent,
    colors = switchColors(),
)
then somewhere:
Copy code
@Composable
private fun switchColors() = SwitchDefaults.colors(
    checkedThumbColor = PedalTheme.colors.interactive_contrast_02,
    checkedTrackColor = PedalTheme.colors.interactive_01,
    checkedBorderColor = PedalTheme.colors.interactive_01,
    checkedIconColor = PedalTheme.colors.interactive_contrast_01,
    uncheckedThumbColor = PedalTheme.colors.interactive_02,
    uncheckedTrackColor = PedalTheme.colors.surface_03,
    uncheckedBorderColor = PedalTheme.colors.interactive_stroke_02,
)
d
Looking at the colors under the hood,
primary
is used for the track colors
f
yeah i think all you need to do is set
checkedTrackColor
i would avoid hardcoding hex/Color values and use semantic colors instead so it’s easier to refactor
d
That is what i do ^^
c
so... by default if I only set primary... is my track purple?
d
If you set the primary color to purple, the track should be purple by default when checked and selected
But in other states, it will not be purple:
Copy code
val SelectedFocusTrackColor = ColorSchemeKeyTokens.Primary
val DisabledSelectedTrackColor = ColorSchemeKeyTokens.OnSurface
val DisabledUnselectedTrackColor = ColorSchemeKeyTokens.SurfaceContainerHighest
val UnselectedTrackColor = ColorSchemeKeyTokens.SurfaceContainerHighest
c
So i set it to blue... but when its off... it looks slightttttly purple? no?
or am i going crazy and that's just gray?
image.png
d
It will depend on the state of the switch and the what colors are in your theme
If you just want to style a switch, it is probably easier to not use an overridden material theme and just pass in the colors as @francisco mentioned above
c
Too many colors to supply though.
Got it working with just
Copy code
MaterialTheme(
                            colorScheme = lightColorScheme(
                                primary = Color(0xFF89CFF0),
                                surfaceContainerHighest = Color.Transparent,
                            )
                        ) {
Output of this
d
That should work fine unless you start using more of the states
You may also run into issues with the dark mode since you are explicitly providing a lightmode theme with override
c
Yeah. I guess I'm just concerned on where the heck that "purple" tint is coming from. I went through each color inidividually and tried setting it to the primary color i have here to see if I would get a programtic alpha applied to it or something. but in this case. i just have a one off switch. I want the "default" material colors... just instead of being based off of purple to instead be based off of the primary color I pass in.
i could have sworn that worked in m2 (give your theme a single color and the rest of the stuff gets generated based off that), but I'm dumb founded at how m3 is doing it.
@Louis Pullen-Freilich [G] sorry for the ping, but if you do get a chance to read this question. I'd be eternally grateful lol
Seems like another route I can take is: 1. go to https://material-foundation.github.io/material-theme-builder/ 2. set primary color as my blue color 3. it'll generate a surf highest color 4. use that instead of Color.Transparent
Then that at least looks good. Better than the Color.Transparent I think.
d
Looking at how the defaults function, some values do apply alpha or composite colors
Copy code
fromToken(SwitchTokens.DisabledUnselectedIconColor)
            .copy(alpha = SwitchTokens.DisabledUnselectedIconOpacity)
            .compositeOver(surface),
But it looks like it is doing that primarily for the disabled states
Supplying your own colors would address this directly as well
c
Oooh. I think I figured it out (without having to use material theme builder website...)
Copy code
MaterialTheme(
    colorScheme = MaterialTheme.colorScheme.copy(
        primary = Color(0xFF89CFF0)
    )
)
{
    Switch(
        checked = !isChecked,
        onCheckedChange = { isChecked = it }
    )
    Switch(
        checked = isChecked,
        onCheckedChange = { isChecked = it }
    )
}
Result does not look purple! 🎉
😀 1
i don't know if it's "right". but it seems to work. like i said above. i know i could set the color for each individual color available for a switch. but in this case. its just a quick demo app I have. and the theme is blue and white. and it just looked super weird to have blue... white... and a hint of purple on my switches.
d
Ideally you would wrap your entire app in a theme of your choice and all of the colors would be applied universally inside your app. Then you could override individual component colors by passing in those specific colors. It is generally not recommended to override the whole theme like this. But it does work.
c
I feel like I remember something in m2 docs where overriding your theme for one-off components that don't match with your original theme was actually recommended to go this route. But again, I could be mistaken. at the end of the day. all i want to do is to change a single color 😄
👍 1
l
Definitely the recommendation is to create your own MyDemoSwitch that wraps switch and explicitly provides colors. Overriding your theme like this for one component is an anti pattern I would say
For one the default values queried from the theme can change, so there's no guarantee this will work over time
a
Overriding the entire theme seems excessive, no? I'd just compute a new SwitchColors based on the color you're looking to use
Ah, nvm, a googler responded
Ditto then 😄
c
gotcha. thanks @Louis Pullen-Freilich [G]! I was hoping I could set a single color, and then have the "gray" variants be generated off of that. but based on what you're saying I'd have to go in and provide all of those values, right?
d
Just to add a bit to this - to make sure you don't get purple tints, you have to override
surfaceTint
in
lightColorScheme()
. A step further actually - I'd make sure you provide values for all
..surface..
colours where you set up your Theme. Then you can use
.copy()
like you've done. Not suggesting this is the best approach as Louis will know better here but just wanted to clear up why you get the purple tint.
c
Even with surfaceTint it still looks purpole. Let me just adding values for all surfaces... edit: adding values for all surfaces includes surfaceContainerHighest, but I still need to come up with a reasonable color myself. I swear Louis or Chris Cinco once chimed in here and gave me an option that included providing a single value and everything else just worked (they explained that material just takes like a 20% alpha or something to generate the track). but my slack search-fu is failing me.
l
It was probably different in material2, the set of colors was smaller and components used less colors. But again, you're fighting with the internals of the components to see what colors it uses for what, and reverse engineering that to derive a theme. If you explicitly want to change the colors for one component, you should provide the component specific colors object. Otherwise, you should provide a theme with every color set to something reasonable, but accept that there is no contract that a specific color set to something will make an entire component look that way
c
gotcha. yeah in this case. my designer just said "use our primary blue color and whatever the default grays are". But there are no default grays. only default purples 😭
l
Right, you would need to change all the color scheme to blue and blue variants. For example by using the theme builder to generate blues
c
yeah. Right now I just went to theme builder on the web. plugged in the blue color. got some greys generated and used those. thank you so much @Louis Pullen-Freilich [G] for confirming! I just wanted to triple-check that there wasn't some overall "single" color I could provide that would give me a sensible default for the switch.