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.
Related
I have a screen, which contains a Map and I want to make a statusBar completely transparent.
What I've tried:
implementation "com.google.accompanist:accompanist-systemuicontroller:0.26.1-alpha"
#Composable
fun MapMainScreen() = Column(
modifier = Modifier.fillMaxSize()
) {
val controller = rememberSystemUiController()
controller.setStatusBarColor(color = Color.Transparent)
controller.setNavigationBarColor(color = Color.Transparent)
controller.setSystemBarsColor(color = Color.Transparent)
Map()
}
Also, I've tried to play with window in MainActivity before and in setContent call:
WindowCompat.setDecorFitsSystemWindows(window, false)
window.setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
)
I want to see a result like in Google Maps, but now my statusBar has a White-Gray color instead of Transparent
How can I fix this and make my statusBar Transparent?
Any additional dependency is not needed.
In your Compose theme (or directly in activity) set this:
SideEffect {
with(view.context as Activity) {
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
window.statusBarColor = Color.Transparent.toArgb()
window.navigationBarColor = Color.Transparent.toArgb()
}
}
Optionally, you could also add this line to make the navigation bar transparent:
window.navigationBarColor = Color.Transparent.toArgb()
This is what Accompanist suggests in the doc:
// Remember a SystemUiController
val systemUiController = rememberSystemUiController()
val useDarkIcons = !isSystemInDarkTheme()
DisposableEffect(systemUiController, useDarkIcons) {
// Update all of the system bar colors to be transparent, and use
// dark icons if we're in light theme
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = useDarkIcons
)
// setStatusBarColor() and setNavigationBarColor() also exist
onDispose {}
}
Also the latest version is : 0.26.3-beta
I want to select all the characters in the text field when the text field is shown on the screen. When passing the same index like TextRange(2,2) it works fine but when using a different index like TextRange(0, name.length)), the selection is not working. I have mentioned the function below
#Composable
fun TestFunction(name: String) {
var fieldValue by remember {
mutableStateOf(TextFieldValue(name, selection = TextRange(0, name.length)))
}
val focusRequester by remember {
mutableStateOf(FocusRequester())
}
DisposableEffect(Unit) {
focusRequester.requestFocus()
onDispose { }
}
BasicTextField(
value = fieldValue,
onValueChange = { fieldValue = it },
modifier = Modifier.focusRequester(focusRequester),
)
}
Selection color depends on primary and background colors from your theme. You can see it in the source code of rememberTextSelectionColors which is used internally.
So if you can't see selection, that's probably because of your theme.
Check out how you can override this value without modifying your theme in this answer. To make it work for whole app you can embed this CompositionLocalProvider to your theme, like this:
#Composable
fun AppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: #Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkThemeColors
} else {
LightThemeColors
}
val customTextSelectionColors = TextSelectionColors(
handleColor = Color.Red,
backgroundColor = Color.Red.copy(alpha = 0.4f)
)
MaterialTheme(
colors = colors,
typography = typography,
shapes = shapes,
) {
CompositionLocalProvider(
LocalTextSelectionColors provides customTextSelectionColors,
content = content,
)
}
}
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
}
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.
Android Jetpack Compose Colors class contains a set of color types a material themed app can be implemented with. In case my app theme requires some extra color types, how can I add these extra colors so they would be available via MaterialTheme object?
Just a small change to Valeriy Katkov's answer
In some versions of the android studio, the following code will not work
#Composable
val Colors.myExtraColor: Color
get() = if (isLight) Color.Red else Color.Green
#Composable
fun ExtraColorExample() {
Text(
text = "test",
color = MaterialTheme.colors.myExtraColor // <-- the newly added color
)
}
It will show an error
This annotation is not applicable to target 'top-level property
without backing field or delegate'
To fix this either write it like this
#get:Composable
val Colors.myExtraColor: Color
get() = if (isLight) Color.Red else Color.Green
Or
val Colors.myExtraColor: Color
#Composable
get() = if (isLight) Color.Red else Color.Green
The version I found this error in
Android Studio Arctic Fox | 2020.3.1 Canary 12
Build #AI-203.7148.57.2031.7226969, built on March 23, 2021
Runtime version: 11.0.8+10-b944.6842174 amd64
VM: OpenJDK 64-Bit Server VM by N/A
Windows 10 10.0
There are basically two approaches to this:
The naive way is to just create extension properties tot hard-code the colors. If you don't want to support multiple themes or light/dark mode this works just fine.
If you want to integrate your colors properly to the theme, you can read my article on it, as it's a bit long to inline here: https://gustav-karlsson.medium.com/extending-the-jetpack-compose-material-theme-with-more-colors-e1b849390d50
But in short, the steps are:
Create your own MyColors class that holds a reference to Colors as well as your new colors.
Create a CompositionLocal that holds a MyColor instance.
Create your theme and wrap the MaterialTheme in a CompositionLocalProvider where the CompositionLocal provides MyColors. Make sure to also assign the Colors instance from MyColors to the MaterialTheme.
Create an extension property on MaterialTheme that refers to the CompositionLocal holding MyColors. This is how you refer to your new colors.
This will allow you to introduce new colors to the theme that will update dynamically as the theme changes.
Extending Colors class
You can easily add an extension property to the Colors class so it would be available via any Colors object across your app.
#Composable
val Colors.myExtraColor: Color
get() = if (isLight) Color.Red else Color.Green
#Composable
fun ExtraColorExample() {
Text(
text = "test",
color = MaterialTheme.colors.myExtraColor // <-- the newly added color
)
}
There's an example in the compose documentation as well, see Extending Material colors.
Specifying a content alpha
In case the color you're missing differs from an existing one only by it's alpha and the purpose of the color is to change a content priority, there's no need to add an additional color to the theme. You can specify a content alpha for a hierarchy by providing a value for LocalContentAlpha.
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.medium,
LocalContentColor provides MaterialTheme.colors.onSurface
) {
// this text is displayed using ContentAlpha.medium
// and MaterialTheme.colors.onSurface color
Text("Hello world!")
}
See Content Alpha documentation for more details. There's also Content Alpha section in Jetpack Compose Theming codelab.
For Material3, I had to extend the androidx.compose.material3.ColorScheme object, since MaterialTheme.colors does not exist.
val ColorScheme.successContainer: Color #Composable
get() = if (!isSystemInDarkTheme()) Color(0xFFd6ffe0) else Color(0xFF269300)
Text(
text = "Hello World",
modifier = Modifier.background(color = MaterialTheme.colorScheme.successContainer)
)
"CompositionLocal" answer is right, but maybe not quite easy to understand.
Android Developers on youtube send a video: "Compose by example".
https://www.youtube.com/watch?v=DDd6IOlH3io
on 6:28/22:07 Introduced this usage.
Here is my sample code to learn this part, for someone to copy:
Define the theme:
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
val yellow200 = Color(0xffffeb46)
val yellow400 = Color(0xffffc000)
val yellow500 = Color(0xffffde03)
val yellowDarkPrimary = Color(0xff242316)
val yellowLightPrimary = Color(0xffd4d3f6)
class RabbitColorPalette(
val raFirstColor: Color,
val raSecColor: Color,
val raOnFirstColor: Color,
val raOnSecColor: Color
)
val rabbitPurple = RabbitColorPalette(Purple200,Purple500,Purple700,Teal200)
val rabbitYellow = RabbitColorPalette(yellow200,yellow400,yellow500,yellowDarkPrimary)
val rabbitColors = compositionLocalOf<RabbitColorPalette>{
rabbitPurple
}
#Composable
fun RabbitThemePink(
content: #Composable() () -> Unit
) {
val colors = rabbitPurple
CompositionLocalProvider(rabbitColors provides colors){
MaterialTheme(
typography = Typography,
shapes = Shapes,
content = content
)
}
}
object RabbitTheme{
val colors: RabbitColorPalette
#Composable get() = rabbitColors.current
}
#Composable
fun RabbitThemeYellow(
content: #Composable() () -> Unit
) {
val colors = rabbitYellow
CompositionLocalProvider(rabbitColors provides colors){
MaterialTheme(
typography = Typography,
shapes = Shapes,
content = content
)
}
}
usage
class RabbitThemeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
RabbitThemePink {
SampleLayout()
}
}
}
#Composable
fun SampleLayout() {
Column {
Text(text = "sample", modifier = Modifier.background(color = RabbitTheme.colors.raFirstColor))
Text(text = "des", modifier = Modifier.background(color = RabbitTheme.colors.raSecColor))
}
}
#Preview
#Composable
fun ConstraintLayoutContentPinkPreview() {
RabbitThemePink {
SampleLayout()
}
}
#Preview
#Composable
fun ConstraintLayoutContentYellowPreview() {
RabbitThemeYellow {
SampleLayout()
}
}
}
In my case, the theme component gets its parameters from the saved settings, i.e. The app theme is independent of the device theme. For this reason, the current answers don't work for me.
My implementation is relevant for Material 3 and directly depends on the passed parameters.
Color.kt
package com.my.app.theme
import androidx.compose.ui.graphics.Color
val SuccessLightColor = Color(0xFF436915)
val SuccessLightContainerColor = Color(0xFFC2F18D)
val SuccessDarkColor = Color(0xFFA7D474)
val SuccessDarkContainerColor = Color(0xFF2D5000)
Theme.kt
package com.my.app.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme()
private val LightColorScheme = lightColorScheme()
var successColorSchemeColor by mutableStateOf(SuccessLightColor)
var successContainerColorSchemeColor by mutableStateOf(SuccessLightColor)
#Suppress("unused")
var ColorScheme.success: Color
get() = successColorSchemeColor
set(value) {
successColorSchemeColor = value
}
#Suppress("unused")
var ColorScheme.successContainer: Color
get() = successContainerColorSchemeColor
set(value) {
successContainerColorSchemeColor = value
}
#Composable
fun Theme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: #Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
colorScheme.success = if (darkTheme) {
SuccessDarkColor
} else {
SuccessLightColor
}
colorScheme.successContainer = if (darkTheme) {
SuccessDarkContainerColor
} else {
SuccessLightContainerColor
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
Use case:
Box(
modifier = Modifier
.size(128.dp)
.background(
color = MaterialTheme.colorScheme.successContainer
)
)