Bryan Herbst
11/19/2020, 4:55 PMstaticAmbientOf()
/ ambientOf()
to provide an @Composable
without a factory layer like AmbientIndication
uses?
Specifically my use case is providing my own version of AmbientColors
, but all of my theme’s colors come from resources for interop with our existing View-backed content. The colors are defined like so:
object ColorPalette {
@Composable val red: Color get() = colorResource(R.color.red)
}
Since those colors are @Composable
, I can’t just do something like
val primaryColorAmbient = staticAmbientOf { ColorPalette.red }
Zach Klippenstein (he/him) [MOD]
11/19/2020, 4:57 PMColorPalette
in the ambient. Might want to make it an interface then as well.Bryan Herbst
11/19/2020, 5:03 PMMaterialColors
that contains all the theme colors, but the default values for those colors all come from ColorPalette
So at the end of the day an expanded version of the ambient would look like this:
val AmbientColors = staticAmbientOf { lightColors() }
@Composable
fun lightTheme() = ThemeColors(
primary = ColorPalette.Red,
secondary = ColorPalette.Blue,
// etc
)
Basically the same overall structure a MaterialColors
and MaterialTheme
, with the key difference being that the default colors for each theme are defined from resources and thus are ComposableBryan Herbst
11/19/2020, 5:04 PMAmbientColors
would still have to actually resolve the color resourcesBryan Herbst
11/19/2020, 5:05 PMAmbientIndication
handles it is to provide a Composable itself, but I don’t want to have to resolve the color every time I need to use it- I’d like to access it as a property like we can with MaterialColors
Zach Klippenstein (he/him) [MOD]
11/19/2020, 5:12 PMI don’t want to have to resolve the color every time I need to use itWhy not? I believe Android caches resources so you’re not actually looking it up from the apk every time, just hitting a map
Zach Klippenstein (he/him) [MOD]
11/19/2020, 5:13 PMobject ColorPalette {
private var _red by mutableStateOf<Color?>(null)
@Composable val red: Color get() = _red ?: colorResource(R.color.red).also { _red = it }
}
Zach Klippenstein (he/him) [MOD]
11/19/2020, 5:14 PMBryan Herbst
11/19/2020, 5:17 PMZach Klippenstein (he/him) [MOD]
11/19/2020, 5:21 PMAlexjlockwood
11/20/2020, 12:22 AMMdcTheme
? https://github.com/material-components/material-components-android-compose-theme-adapter/blob/develop/lib/src/main/java/com/google/android/material/composethemeadapter/MdcTheme.kt
this is basically the approach i ended up using. so my entire color palette thing is just Color
objects. and then i extract the “themeable” colors w/ the theme attributes we use in pre-compose UI. the caching happens in the theme (basically i remember
the current color palette and recompute it only when ContextAmbient.current.theme
changes)Alexjlockwood
11/20/2020, 12:36 AMColor
is an inline class, which means at compile time it just becomes a long
(which is really nice/efficient).
• As soon as you make it nullable like Color?
, that long
becomes a Long
and introduces some additional overhead (auto-boxing)
• Also FYI, colorResource()
doesn’t internally use AppCompatResources#getColorStateList()
which means it won’t understand theme attributes in color state lists on lollipop.
• I haven’t profiled the performance hit of resolving colors each time you access the palette, but i’d bet that caching it a single time in the top-level theme is probably the more efficient approach (although zach may be right that the difference is negligible 🤷 ).Bryan Herbst
11/20/2020, 2:53 PMremember
a composable, but I also totally get why that isn’t a thing. I think I just need to tweak the way I’m initializing my colors to not be Composable