I wrote some Jetpack Compose Demo, but I found library bug about adapt dark mode, therefore I want to show light mode only in my App, however when I set <item name="android:forceDarkAllowed" tools:targetApi="q">false</item> and AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) , those do not work, any idea for show light mode only for Jetpack Compose?
The color we used for compose is not defined in xml, should be something like below:
#Composable
fun MyComposeTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: #Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colorScheme = colors,
content = content
)
}
As you can see, you can pass any color as you wish in MaterialTheme function call, just remove the dark mode check will do the trick.
Simply add an item in both res/theme.xml and res/theme.xml(night) file is <item name="android:windowBackground">#color/white</item> in both darkand light mode it will be white.
Thank you.
on theme.kt
change this
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
to this
val colors = LightColorPalette
Related
I discovered today that MaterialTheme applies an alpha to Text's colour. As you can see from the example attached, when I change the background colour, the text's colour appears to be different because it has a transparency value. I can force set a colour (Text(color = MaterialTheme.colors.onBackground, ....)) and this works correctly but I don't want to have to do this for every single Text.
Why does MaterialTheme do this? How do I override this behaviour?
Compose and Material Compose Material version: 1.2.1
#Preview
#Composable
private fun Preview_Playground() {
MaterialTheme {
Box(Modifier.background(Color.Green)) {
Text("Test", fontWeight = FontWeight.ExtraBold, modifier = Modifier.alpha(1f))
}
}
}
With M2 (androidx.compose.material) the color of the Text is defined by the color parameter or applying a TextStyle.
The default value is Color.Unspecified.
If color = Color.Unspecified and style has no color set, this will be LocalContentColor mixed with LocalContentAlpha.current.
In the Text.kt you can find:
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
}
With M3 (androidx.compose.material3) it doesn't happen since LocalContentColor.current is not mixed:
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current
}
}
If you have to use M2, you can define a custom composable for your Text, or you can change the LocalContentAlpha in the theme for the whole application (not only the Text):
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes
){
CompositionLocalProvider(LocalContentAlpha provides 0.5f) {
content()
}
}
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
As I don't have any experience with Android prior to JetPack compose, I'm unable to figure this out.
I'm using the barteksc/AndroidPdfViewer but since this is an old library, I need to wrap this in a AndroidView() composable. This works and displays the PDF correctly.
#Composable
fun PDFView(
byteArray: ByteArray,
) {
AndroidView(
modifier = Modifier
.fillMaxSize(),
factory = { context ->
PDFView(
ContextThemeWrapper(context, R.style.PdfView), null
)
},
update = { pdfView ->
Log.d(TAG, "PDF view UPDATE called")
pdfView
.fromBytes(byteArray)
.autoSpacing(false)
.spacing(25)
.pageFitPolicy(FitPolicy.BOTH)
.load()
}
)
}
As per documentation of the lib, setting some spacing and adding a background colour would result in a visual separation of the PDF pages.
Setting the AndroidView's modifier background does not work.
So I tried setting background on R.style.PDFView like so (tried several options):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="PdfView" parent="Theme.MyPDFView">
<item name="colorSurface">#color/blue</item>
<item name="background">#color/blue</item>
<item name="backgroundColor">#color/blue</item>
</style>
</resources>
But this still does not change the background.
To be complete, the Theme.MyPDFTheme looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.MyPDFView" parent="Theme.MaterialComponents.DayNight">
<item name="android:statusBarColor">#color/background_material_light</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">#color/background_material_light</item>
<item name="android:windowLightNavigationBar">true</item>
</style>
</resources>
I also tried setting background color in Theme.kt
private val LightColorPalette = lightColors(
surface = Color.Green,
background = Color.Yellow,
But the background of the PDFView does not change, yes, device is in light mode.
How can I set the background on this view?
You can set PDFView background color with setBackgroundColor:
factory = { context ->
PDFView(
context,
null
).also {
it.setBackgroundColor(Color.Transparent.toArgb())
}
},
Then the background color set with the Modifier will become visible. Full example:
AndroidView(
factory = { context ->
PDFView(
context,
null
).also {
it.setBackgroundColor(Color.Transparent.toArgb())
}
},
update = { pdfView ->
pdfView
.fromStream(pdfView.context.resources.openRawResource(R.raw.sample))
.autoSpacing(false)
.spacing(25)
.pageFitPolicy(FitPolicy.BOTH)
.load()
},
modifier = Modifier
.fillMaxSize()
.background(Color.Red)
)
In Jetpack Compose, TopAppBar shows default background color irrespective of what we added to themes.xml.
So how can we change TopAppBar background color from themes.xml so it's applied globally to the App?
TopAppBar(
title = { Text("Activity") },
navigationIcon = {
IconButton(onClick = { onBackPressed() }) {
Icon(Icons.Filled.ArrowBack, contentDescription = null)
}
}
)
themes.xml
<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight">
<!-- Primary brand color. -->
<item name="colorPrimary">#android:color/black</item>
<item name="colorPrimaryVariant">#android:color/holo_orange_dark</item>
<item name="colorOnPrimary">#android:color/white</item>
</style>
Note : we can change it through backgroundColor attribute of TopAppBar but here I want to achieve it globally.
The accepted answer explains what to do adequately. There is one thing you might need to keep in mind, though.
TopAppBar uses MaterialTheme.colors.primarySurface as the background color, and it's defined as the following.
[androidx/compose/material/Colors.kt]
val Colors.primarySurface: Color get() = if (isLight) primary else surface
In other words, if the device is in the light mode, it uses primary, and in the dark mode, it uses surface.
Suppose I have a theme with everything as default values.
private val DarkColorPalette = darkColors()
private val LightColorPalette = lightColors()
#Composable
fun ComposeTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: #Composable () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
)
lightColors and darkColors have the following values by default.
fun lightColors(
primary: Color = Color(0xFF6200EE),
...
)
fun darkColors(
surface: Color = Color(0xFF121212),
...
)
When the device is in the light mode, primary (0xFF6200EE) will be TopAppBar's background color.
But when the device is in the dark mode, surface (0xFF121212) is not TopAppBar's background color. It's slightly lighter; 0xFF282828 to be exact.
The reason is TopAppBar has a built-in elevation, which is 4.dp by default.
[androidx/compose/material/AppBar.kt]
#Composable
fun TopAppBar(
...
elevation: Dp = AppBarDefaults.TopAppBarElevation // 4.dp
) {
This most likely won't cause a problem, but it might matter if you want to apply the exactly same background color to somewhere else, such as setting the same color for the status area's background.
val systemUiController = rememberSystemUiController()
systemUiController.setSystemBarsColor(color = Color(0xFF282828))
Note that for this particular case, it would be easier if you just go full screen and add padding at the top.
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
MyAppTheme {
Surface(
modifier = Modifier.systemBarsPadding().fillMaxSize(), // <--
) {
...
}
}
}
For the TopAppBar and the other composables the basis of theming is the MaterialTheme composable and not the AppCompat/MaterialComponents XML themes.
The TopAppBar uses the backgroundColor attribute.
The default value is defined by MaterialTheme.colors.primarySurface.
You can change these colors globally defining your theme adding your Colors and passing them to a MaterialTheme:
private val LightColors = lightColors(
primary = Yellow500,
//...
)
Otherwise you can simply use :
TopAppBar(
title = { Text("Activity") },
backgroundColor = /*...*/,
/* ... */
)
If you want to use the AppCompat XML themes in Jetpack Compose you can use the AppCompat Compose Theme Adapter provided by the accompanist library.
When you create a new project in studio, you get a file named Theme.kt, in which there are color palettes named lightColors and darkColors. You should modify the parameters of those values to achieve the result globally in your app.
Does somebody know how to change default style to button?
Style in xml:
<item name="materialButtonStyle">#style/ButtonStyle</item>
And I want to convert it to Jetpack Compose.
In default compose sample(Android Studio Canary) You can see ui.theme folder and it's a analog of values folder but without Strings and Dimens. So how I can add Strings and Dimens to this compose folder?
As described in the nglauber answer you can customize the shape, typography and color in your theme, or in the Button parameters.
Also you can override these values and build a default button style.
Something like:
#Composable
fun DefaultButtonStyle(content: #Composable () -> Unit) {
MaterialTheme(
//override the shape
shapes = MaterialTheme.shapes.copy(small = CutCornerShape(12.dp)),
//Override the typography.button using the merge method
typography = MaterialTheme.typography.copy(
button = MaterialTheme.typography.button.merge(TextStyle(fontSize = 20.sp))),
//override the colors define in the material theme
colors = MaterialTheme.colors.copy(
primary = Color.Yellow,
onPrimary = Color.Blue)
) {
content()
}
}
Then just use it with:
DefaultButtonStyle() {
Button(onClick = { /*....*/ }) {
Text(text = "BUTTON")
}
}
If you look into the Button source, you'll notice that it uses a couple of default values that you can customize (via params or via custom style).
shape: Uses MaterialTheme.shapes.small (you can customized this field in your style);
val shapes = Shapes(
small = CutCornerShape(4.dp), // << here
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)
colors: which is an instance of ButtonColors that provides backgroundColor, contentColor, disabledBackgroundColor and disabledContentColor. Look into the Button.buttonColors function to see how to customize the colors for your button.
In terms of text, the Button component gets the text style from MaterialTheme.typography.button, so you can override this field in your style to customize your button's text.
val typography = Typography(
...
button = defaultTypography.button.copy(
fontFamily = yourFontFamily,
color = Color.Yellow
)
)
For text and dimensions you can continue using XML files (res/values) and refer to them using stringResource(id) and dimensionResource(id) functions respectively.