Does jetpack compose use drawable-night folder? - android

We have a View based Android app with some drawables in res/drawable folder, and their counterpart for night mode in res/drawable-night folder
When using legacy views, referencing a drawable R.drawable.foo from a XML layout file, the system would pick the drawable from either res/drawable or res/drawable-night folders depending on whether we are in day or night mode.
When using jetpack compose, we reference the drawable in an Image composable like this:
Image(painter = painterResource(R.drawable.foo))
However, this always pick the drawable from res/drawable folder, ignoring day / night mode.
We could do something like this to select the right drawable, but we would need to test the night mode (isSystemInDarkTheme()) within all composables that uses drawables depending on nigh mode:
Image(painter = painterResource(id = if (isSystemInDarkTheme()) R.drawable.foo_dark else R.drawable.foo_light))
Is there a way in compose to ensure that the drawable from day or night mode are picked correctly, and transpartently, as in legacy view system?

Maybe Compose was updated since the other answers were posted, but I can confirm that in a simple app using only Compose and two drawables with the same name in drawable and drawable-night folders, the app is picking up the dark one, if the phone is set to dark mode.
That's also without a composable theme defined, so this simple code does the job:
#Preview
#Preview(uiMode = UI_MODE_NIGHT_YES)
#Composable
fun ImagePreview() {
Image(
painter = painterResource(id = R.drawable.my_icon),
contentDescription = null
)
}
Note that you can also see this in a Preview - having two #Preview annotations produces two previews in Android Studio, and specifying that you want to see your composable in dark mode is also possible!

Seeing how the dark & light palette is currently implemented in the theming codelab I'd go with creating my own somewhat similar abstraction:
class LightDrawables: Drawables
class DarkDrawables: Drawables
fun getDrawable(darkTheme: Boolean = isSystemInDarkTheme(), #DrawableRes drawableRes: Int) = if(darkTheme) DarkDrawables.xy else LightDrawables.xy

As of now, "NO".
How to use /drawable-night?
Google provided support for some basic compose utils through Accompanist which does not come with standard Jetpack Compose. For example, PagerLayout, Swipe Refresh and many more.
One of those utils is Drawable Painter and that is something you can use.
After you add dependency, you can use this util using:
#Composable
fun DrawDrawable() {
val drawable = AppCompatResources.getDrawable(LocalContext.current, R.drawable.foo)
Image(
painter = rememberDrawablePainter(drawable = drawable),
contentDescription = "content description",
)
}
Document Source
Note:
Don't expect dark variant of the drawable to show up in #Preview. But it will be loaded at runtime.

Related

How to define shape for Button in Jetpack compose when using Material design 3

I am using Material design 3 with Jetpack compose.
By default the Shape for Button has a corner radius of type full which is 20dp. As per the documentation.
https://m3.material.io/components/buttons/overview
If I look at the shape documentation, I can see following shapes in token section
However when I provide the Shapes object to the Theme for the app. I don't have an option to customise Full Shape.
Here is my code
Shapes(
extraSmall = RoundedCornerShape(4.dp),
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(16.dp),
extraLarge = RoundedCornerShape(8.dp)
)
What I am trying to achieve:
I don't want to specify shape explicitly to the each button or create a composable for button and then reuse it everywhere.
I want to apply it once and not need to specify it explicitly as other components
As you mentioned, M3 FilledButton's container shape is by default ShapeKeyTokens.CornerFull, as you can see in FilledButtonTokens. This token is then converted to Shape in Shapes.fromToken function:
internal fun Shapes.fromToken(value: ShapeKeyTokens): Shape {
return when (value) {
ShapeKeyTokens.CornerExtraLarge -> extraLarge
ShapeKeyTokens.CornerExtraLargeTop -> extraLarge.top()
ShapeKeyTokens.CornerExtraSmall -> extraSmall
ShapeKeyTokens.CornerExtraSmallTop -> extraSmall.top()
ShapeKeyTokens.CornerFull -> CircleShape
ShapeKeyTokens.CornerLarge -> large
ShapeKeyTokens.CornerLargeEnd -> large.end()
ShapeKeyTokens.CornerLargeTop -> large.top()
ShapeKeyTokens.CornerMedium -> medium
ShapeKeyTokens.CornerNone -> RectangleShape
ShapeKeyTokens.CornerSmall -> small
}
}
As you can see, ShapeKeyTokens.CornerFull, unlike most others, isn't converted to some Shape from the theme, but simply to CircleShape. This means that it's not possible to do what you want. You will either have to pass your shape to each button or create custom button composable.

Compose Card always overdrawn

Issue
Compose card surface shows always overdrawn. However, it's not the case if we use old style CardView in xml.
From here it's always recommended to reduce overdrawing of any view to make it more towards green, blue.
Just this following code will produce some output like this, which means it's overdrawn 3 or 4 times.
Card(
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
modifier = modifier.fillMaxWidth().padding(vertical = 10.dp)
) {
Column(
modifier = Modifier
.padding(all = 16.dp)
) {
Text("randomText")
}
}
I tried to look into NowInAndroid sample application but even that produces all red output when debugged to show overdrawn areas.
Question
Is the Debug GPU Overdraw option in developer settings giving right information?
Can we really avoid this overdraw? If so, how?

Android Compose Shadow

Is Android Jetpack Compose shadow modifier supposed to look like this?
I'm so confused. I've written a few components and I'm testing the shadow modifier that I believe was introduced in compose-1.2.0-alpha. I'm now using 1.2.1. My component is written as this.
fun Labels() {
Label(
modifier = Modifier.shadow(
elevation = 1.dp,
shape = RoundedCornerShape(4.dp),
ambientColor = Color.Blue,
spotColor = Color.Blue
),
text = field.name,
style = typographies.tagDefault
)
}
Label is a mirror copy of Text for my own reasons, but that means it boils down to::
fun Label(...) {
BasicText(
text,
modifier,
mergedStyle,
onTextLayout,
overflow,
softWrap,
maxLines,
)
}
So in the grand scheme and to my understanding the shadow effect is ultimately being applied to the BasicText and nothing else. Which looks like the case in the image, but why is it so thick? Why isn't it more like a border the same as box-shadow in CSS? How can I make it look like it should with similarities to box-shadow in CSS? I've tried a few things like moving the modifier to higher component pieces and messing with the elevation. But all that has done is made the shadow a little hazy and maybe slightly thicker. Also if I change the shape to something like RoundedCornerShape(percent = 50) it nearly fills in the whole background with this blue color which is definitely not intended.

Surface at +(...)% colors in jetpack compose

In official Material Design 3 resources (e.g. the Figma design kit), there have been many references to colors called "Surface at +x". These colors are the surface color mixed with x% of the primary color.
Now my question:
How can you implement the "Surface at +x" colors in Jetpack Compose? There is no documentation and no property on the MaterialTheme.colorScheme object.
Figma Design Kit reference:
In case anyone need to get it in a non-compose code, use SurfaceColors enums:
int colorSurface1 = SurfaceColors.SURFACE_2.getColor(context);
Documentation can be found here
Update September 2022
With Material 3, if for some reason you need the elevate color surface but you can't use the Surface, now you can use directly:
MaterialTheme.colorScheme.surfaceColorAtElevation(4.dp)
Surface uses MaterialTheme.colorScheme.surface by default, they also have a new tonalElevation property which you can read about here.
The gist of it is that increasing the tonal elevation changes the color automatically, try it yourself:
Surface(tonalElevation = 5.dp) {
// content
}

Material icon size adjustment in Jetpack Compose?

Jetpack compose provides a nice Icon() object to display icons that accepts a vector asset. Typically, you are able to set the size via a modifier:
Icon(Icons.Filled.PersonPin, Modifier.preferredSize(64.dp))
My problem is I cannot seem to affect the size of the displayed icon when I am using a provided system vector asset (ie, Icons.Filled. or Icons.Default. etc). When I use my own asset, things work as expected. Using the system assets the modifier only increases the UI footprint of the enclosing "box" while the icon stays tiny within:
Applying the modifier using 'then' results in the same behavior:
Icon(Icons.Filled.PersonPin,Modifier.then(Modifier.preferredSize(128.dp)))
Is there something about the native icons? I assumed being vector assets they should be able to resize as well.
With 1.0.x just use the Modifier.size(xx.dp)
Icon(Icons.Filled.Person,
"contentDescription",
modifier = Modifier.size(128.dp))
Internally material icon size is 24.dp
// All Material icons (currently) are 24dp by 24dp, with a viewport size of 24 by 24.
#PublishedApi
internal const val MaterialIconDimension = 24f
And using the size in modifier it's not working, So we can change the icon by copying the icon and change the default height and width.
Icon(Icons.Filled.Person.copy(defaultHeight = 128.dp, defaultWidth = 128.dp))
NOTE: This is not an official recommendation to set the icon size,
Just a hack way to change the icon size.
The accepted answer longer works in 1.0.0-alpha11. This is the associated bug report. As per the bug report comments, the new way of doing this from alpha12 on would be:
Icon(Icons.Filled.Person, modifier = Modifier.size(128.dp))
What works for me is to use Modifier.fillMaxSize(...)), e.g.
Icon(Icons.Filled.Person, contentDescription = "Person", modifier = Modifier.fillMaxSize(0.5F))
Icon(Icons.Filled.Person, contentDescription = "Person", modifier = Modifier.fillMaxSize(0.75F))
Icon(Icons.Filled.Person, contentDescription = "Person", modifier = Modifier.fillMaxSize(1.0F))
To make the answer above more accesible, define some extension functions:
fun VectorAsset.resize(size: Int) = this.resize(size.dp)
fun VectorAsset.resize(size: Dp) = this.resize(size, size)
fun VectorAsset.resize(width: Int, height: Int) = this.resize(width.dp, height.dp)
fun VectorAsset.resize(width: Dp, height: Dp) =
this.copy(defaultWidth = width, defaultHeight = height)

Categories

Resources