I want to custom colors system using Compose, but it isn't working. It effected by colors in themes.xml.
Activity
class DemoComposeMainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val colorPrimary = colorResource(R.color.md_green_500)
val colorSecondary = colorResource(R.color.md_orange_500)
val colors = lightColors(
primary = colorPrimary,
primaryVariant = colorPrimary,
onPrimary = Color.White,
secondary = colorSecondary,
secondaryVariant = colorSecondary,
onSecondary = Color.White)
MaterialTheme(colors = colors) {
// TODO
}
}
}
}
Please help me. Thanks.
The statusbar color is based on the android:statusBarColor defined in your app theme.
If you want to change the statusBar color you can use the accompanist library.
Something like.
val systemUiController = rememberSystemUiController()
val useDarkIcons = MaterialTheme.colors.isLight
SideEffect {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = useDarkIcons
)
// setStatusBarsColor() and setNavigationBarsColor() also exist
}
Related
Before I define a color with a name in resources file such as Code A, and access it using such as Divider(color = colorResource(R.color.colorGrey)).
Now I read the article. I get the following warning:
Prefer colors from the theme rather than hard-coded colors. Even though it's possible to access colors using the colorResource function, it's recommended that the colors of your app are defined in a MaterialTheme which can be accessed from your composables like MaterialTheme.colors.primary.
So I study Code B, I find I can use such as MaterialTheme.colors.secondary to access a color.
I find there are only some preset colors with name in colors such as primary, primaryVariant, ...etc, it seems that I can only overwrite preset colors such as primary = Purple900.
I hope I can customize a color with a name in MaterialTheme, so I can use such as MaterialTheme.colors.myIcon to access color, how can I do?
Code A
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="colorGrey">#FF6200EE</color>
</resources>
Code B
#Composable
fun SoundMeterTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: #Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200
)
fun darkColors(
primary: Color = Color(0xFFBB86FC),
...
): Colors = Colors(
primary,
...
)
If you simply just want a named color, just a global static will work for you. There is no need to use MaterialTheme.
Just have a Color.kt file with top level public constant declarations.
val MyGrey = Color(#FF6200EE)
or you could sort of namespace them with
object MyColors {
val Grey = Color(#FF6200EE)
}
Then you can just use it in your composables
Divider(color = MyColors.Grey)
The downside though is that now you are not supporting dark theme or other possible config changes. For that you can extend MaterialTheme and we have docs on how to do that here
For your example of myIcon it would be
import androidx.compose.material.Colors
val Colors.myIcon: Color
get() = if (isLight) Color(#...) else Color(#...)
Use compositionLocal to feed data into your composable.
simple example:
private val LocalMyColors = compositionLocalOf {
DarkColorPalette
}
#Stable
object MyTheme {
val colors: darkColors
#Composable
get() = LocalMyColors.current
enum class Theme {
Light, Dark
}
}
#Composable
fun SoundMeterTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: #Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
CompositionLocalProvider(LocalMyColors provides colors) {
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
}
fun darkColors(
primary: Color = Color(0xFFBB86FC),
...
): Colors = Colors(
primary,
...
)
use:
LocalMyColors.current.primary
MyTheme.colors.primary
I'm using the Accompanist systemUiController library and I'm setting
darkIcons=false
while in Light Theme but the effect doesn't apply on devices with android 11. It seems to work on devices with android 10 for example.
This is the code with which I'm trying to set the colors.
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color=statusBarColor,darkIcons=false)
where statusBarColor is a darker color which represents the reason I want white foreground/icons
While in Dark Theme it works to set darkIcons
to both true and false and the effect applies accordingly
This is the status bar on LightTheme with darkIcons=false and darkIcons=true
This is the status bar on DarkTheme with darkIcons=false
This is the status bar on DarkTheme with darkIcons=true
For reference this is my whole Theme.kt
private val LightBase = ASBTheme(
material = lightColors(
background = FigmaPrimaryWhite,
onBackground = FigmaTextBlack,
surface = FigmaPrimaryWhite,
onSurface = FigmaTextBlack,
primary = FigmaSecondaryAvastBlue,
error = FigmaStatusPink,
),
textColorLabel = FigmaSecondaryAvastPurple,
colorAccent = FigmaPrimaryGreen,
... //bunch of custom colors
)
private val DarkBase = ASBTheme(
material = darkColors(
background = FigmaPrimaryBlackBg,
onBackground = FigmaTextWhite,
surface = FigmaSecondaryAvastBlueDark,
onSurface = FigmaTextWhite,
primary = FigmaSecondaryBlackDark,
error = FigmaStatusPink
),
textColorLabel = FigmaSecondaryAvastPurpleBright,
colorAccent = FigmaStatusGreen,
... //bunch of custom colors
)
private val LocalAsbTheme = staticCompositionLocalOf { LightBase }
var navBarColor: Color? = null
var statusBarColor: Color? = null
#Composable
fun ASBTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: #Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkBase
} else {
LightBase
}
if (darkTheme) {
navBarColor = DarkBase.background
statusBarColor = DarkBase.primary
} else {
navBarColor = LightBase.background
statusBarColor = LightBase.primary
}
SetBarsTheme(statusBarColor!!, navBarColor!!)
CompositionLocalProvider(
LocalAsbTheme provides colors,
) {
MaterialTheme(
colors = colors.material,
content = content,
)
}
}
val MaterialTheme.asbTheme: ASBTheme
#Composable
#ReadOnlyComposable
get() = LocalAsbTheme.current
SetBarsTheme() is where I'm trying to set the status bar colors depending on the lifecycle event so that the colors would maintain after onPause() / onStop(). I also tried to set the colors outside of this logic and the bug still persists.
#Composable
fun SetBarsTheme(
statusBarColor: Color,
navigationBarColor: Color,
darkIcons:Boolean=false,
) {
val lifecycleOwner = LocalLifecycleOwner.current
val systemUiController = rememberSystemUiController()
DisposableEffect(lifecycleOwner) {
// Create an observer that triggers our remembered callbacks
// for sending analytics events
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
systemUiController.setStatusBarColor(color=statusBarColor,darkIcons)
systemUiController.setNavigationBarColor(color=navigationBarColor)
}
}
// Add the observer to the lifecycle
lifecycleOwner.lifecycle.addObserver(observer)
// When the effect leaves the Composition, remove the observer
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
There's a fixed bug, make sure you're using the latest Accompanist version.
If you still able to reproduce it, you should report it, including used device/dependencies versions.
I'm testing with Material Theming with Jetpack Compose and I'm not sure why I can't make the theme's onSurface color work.
Here is the Theme.kt with a onSurface color set to Color.Red:
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200,
onSurface = Color.Red, // <------- HERE
onPrimary = Color.Blue, // <----- HERE
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200,
onSurface = Color.Red, // <------- AND HERE
onPrimary = Color.Blue, // <----- HERE
)
#Composable
fun ExploringMaterialTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: #Composable() () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
and here is the Activity:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp()
}
}
}
#Preview
#Composable
fun MyApp() {
ExploringMaterialTheme {
// I also tried
// Surface(color = MaterialTheme.colors.surface) {
Surface {
Text(text = "Hello!!", modifier = Modifier.padding(16.dp))
}
}
}
I was expecting "Hello!!" to be shown in Red, but instead, it's shown in normal black. Any ideas what I'm missing? 🤔
It works has expected when I set the a color in Surface component. Surface gets the right on Color (the onPrimary in this case):
Surface(color = MaterialTheme.colors.primary) {
Text(text = "Hello!!", modifier = Modifier.padding(16.dp))
}
The Surface composable uses:
CompositionLocalProvider(
LocalContentColor provides contentColor){
//
content()
}
where the contentColor is defined by:
fun Colors.contentColorFor(backgroundColor: Color): Color {
return when (backgroundColor) {
primary -> onPrimary
primaryVariant -> onPrimary
secondary -> onSecondary
secondaryVariant -> onSecondary
background -> onBackground
surface -> onSurface
error -> onError
else -> Color.Unspecified
}
}
Currently you have to specify the surface color in your theme:
private val LightColorPalette = lightColors(
primary = Blue500,
surface = Color.Yellow)
In this case the Text uses the onSurface color.
If you don't specify the surface color the Surface components use the background as colorBackground and the onBackground for the Text.
There seems to be some issue where the color matches the background color instead of the surface color, so it returns the onBackground. If you set your surface color so that it is different from the background color then it will pick the correct onSurface color, for insance
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200,
onSurface = Color.Red,
surface = Color.Green,
)
This may be a bug in compose.
This happens here:
fun Colors.contentColorFor(backgroundColor: Color): Color {
return when (backgroundColor) {
primary -> onPrimary
primaryVariant -> onPrimary
secondary -> onSecondary
secondaryVariant -> onSecondary
background -> onBackground
surface -> onSurface
error -> onError
else -> Color.Unspecified
}
}
I'm trying to use staticCompositionLocalOf in Jetpack Compose according to this article on Medium.
This is my ProvidableCompositionLocal
val lightColors = lightColors(
primary = LightColor.BackgroundWhite,
primaryVariant = LightColor.ToolbarWhite,
secondary = LightColor.FabBlue,
secondaryVariant = LightColor.BackgroundWhite,
surface = LightColor.SurfaceWhite,
onPrimary = LightColor.TextBlack,
onSecondary = LightColor.TextBlack,
onSurface = LightColor.TextBlack
)
val darkColors = darkColors(
primary = DarkColor.BackgroundBlue,
primaryVariant = DarkColor.ToolbarBlue,
secondary = DarkColor.FabGrey,
secondaryVariant = DarkColor.BackgroundBlue,
surface = DarkColor.SurfaceBlue,
onPrimary = Color.White,
onSecondary = Color.White,
onSurface = Color.White
)
private val DarkColorPalette =
Colors(
material = darkColors,
toolbar = DarkColor.ToolbarBlue,
background = DarkColor.BackgroundBlue,
surface = DarkColor.SurfaceBlue,
fab = DarkColor.FabGrey,
pink = DarkColor.Pink
)
private val LightColorPalette =
Colors(
material = lightColors,
toolbar = LightColor.ToolbarWhite,
background = LightColor.BackgroundWhite,
surface = LightColor.SurfaceWhite,
fab = LightColor.FabBlue,
pink = LightColor.Pink
)
val TheColor: Colors
#Composable
#ReadOnlyComposable
get() = LocalColors.current
}
val LocalColors = staticCompositionLocalOf { DarkColorPalette }
This is the wrapper around the normal Colors class provided by Compose class. The material variable is the normal Colors class.
data class Colors(
val material: Colors,
val toolbar: Color,
val background: Color,
val surface: Color,
val fab: Color,
val pink: Color
) {
val primary: Color get() = material.primary
val primaryVariant: Color get() = material.primaryVariant
val secondary: Color get() = material.secondary
val secondaryVariant: Color get() = material.secondaryVariant
// val background: Color get() = material.background
// val surface: Color get() = material.surface
val error: Color get() = material.error
val onPrimary: Color get() = material.onPrimary
val onSecondary: Color get() = material.onSecondary
val onBackground: Color get() = material.onBackground
val onSurface: Color get() = material.onSurface
val onError: Color get() = material.onError
val isLight: Boolean get() = material.isLight
}
I have also provided it in my Theme function as shown below. I'm getting the darkTheme from Android DataStore
#Composable
fun BMICalculatorTheme(
darkTheme: Boolean,
content: #Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
CompositionLocalProvider(LocalColors provides colors) {
MaterialTheme(
colors = colors.material,
typography = CabinTypography,
shapes = Shapes,
content = content
)
}
}
However, I'm getting the below error but can't find any online resources that can help me fix it. In case any further information is needed, I would be more than happy to clarify. Any help would be highly appreciated.
IllegalStateException: CompositionLocal LocalConfiguration not present
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.noLocalProvidedFor(AndroidCompositionLocals.android.kt:123)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.access$noLocalProvidedFor(AndroidCompositionLocals.android.kt:1)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$LocalConfiguration$1.invoke(AndroidCompositionLocals.android.kt:44)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$LocalConfiguration$1.invoke(AndroidCompositionLocals.android.kt:43)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at androidx.compose.runtime.LazyValueHolder.getCurrent(ValueHolders.kt:29)
at androidx.compose.runtime.LazyValueHolder.getValue(ValueHolders.kt:31)
at androidx.compose.runtime.ComposerImpl.resolveCompositionLocal(Composer.kt:1895)
at androidx.compose.runtime.ComposerImpl.consume(Composer.kt:1865)
at androidx.compose.foundation.DarkTheme_androidKt.isSystemInDarkTheme(DarkTheme.android.kt:52)
at com.octagon_technologies.bmicalculator.data.ThemeDataStore$isDarkMode$$inlined$map$1$2.emit(Collect.kt:135)
at kotlinx.coroutines.flow.FlowKt__ErrorsKt$catchImpl$$inlined$collect$1.emit(Collect.kt:134)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(Unknown Source:4)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:77)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:59)
at androidx.datastore.core.SingleProcessDataStore$data$1$invokeSuspend$$inlined$map$1$2.emit(Collect.kt:139)
at kotlinx.coroutines.flow.FlowKt__LimitKt$dropWhile$$inlined$unsafeFlow$1$lambda$1.emit(Collect.kt:137)
at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:348)
at kotlinx.coroutines.flow.StateFlowImpl$collect$1.invokeSuspend(Unknown Source:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:57)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
The problem appears to be here:
... ThemeDataStore$isDarkMode$$inlined$map$1$2.emit(Collect.kt:135)
From the stack trace it looks like the isSystemInDarkTheme is being called inside a lambda passed to collect() which, since it is an #Composable function, it should only be called from another #Composable function and cannot be called inside a flow collect().
The compiler should have reported this an error. Please consider reporting this as a compose compiler plugin bug.
I want to use custom colors defined in the colors.xml class directly without using the Material theme colors or the default theme provided by the Jetpack. Is there any straightforward way to do it?
You can use colorResource() which loads a color resource.
Text(
text = "Hello World",
color = colorResource(R.color.purple_200)
)
To use color in jetpack compose using recommended create a package ui.theme in com.<domain_name>.<app_name> which will likely be present by default if you are creating empty compose project. Now create Color.kt and Theme.kt kotlin files if they are not present in your project.
In Color.kt add the colors you need
package com.<domain_name>.<app_name>.ui.theme
import androidx.compose.ui.graphics.Color
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
val Flower = Color(0xFF4CAF50)
val Deer = Color(0xFFFF5722)
val Mango = Color(0xFFFF9800)
val AppbarColor = Color(0xFF2196F3)
Here is ready to use a Material Color template made by me
There are 3 common ways of using colors
Method 1 : Directly use color
import com.<domain_name>.<app_name>.ui.theme.*
Text(text = "Hello ", color = Flower)
Method 2 : Override default MaterialTheme colors
Now in, Theme.kt
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200,
onBackground = Flower //Custom color
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200,
onBackground = Deer //Custom color
/* Other default colors to override
background = Color.White,
surface = Color.White,
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
*/
)
#Composable
fun NotepadTheme(darkTheme: Boolean = isSystemInDarkTheme(),
content:#Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ColorApp()
}
}
}
#Composable
fun ColorApp() {
ColorTheme {
Surface(modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!", color = MaterialTheme.colors.onBackground) //Using color
}
#Preview(
showBackground = true, name = "Light mode",
uiMode = Configuration.UI_MODE_NIGHT_NO or
Configuration.UI_MODE_TYPE_NORMAL
)
#Preview(
showBackground = true, name = "Night mode",
uiMode = Configuration.UI_MODE_NIGHT_YES or
Configuration.UI_MODE_TYPE_NORMAL
)
#Composable
fun DefaultPreview() {
ColorApp()
}
Method 3 : Custom theme (Recommended method)
Text(text = "Hello ", color = AppNameTheme.colors.customColor)