Material icon size adjustment in Jetpack Compose? - android

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)

Related

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.

Change the size of box in Jetpack Compose doesnt work [duplicate]

This code fills the full screen if i specify the size to be 100.dp.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
This code behave properly by filling the required size.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
}
Can somebody please explain why is it happening?
This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.
As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.
Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.
Here is how one of the maintainers explains the reasons for this decision:
Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.
It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.

Jetpack Compose - A Card is inside a Surface Composable - Card size is ignored with Surface Composable being fillMaxSize [duplicate]

This code fills the full screen if i specify the size to be 100.dp.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
This code behave properly by filling the required size.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
}
Can somebody please explain why is it happening?
This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.
As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.
Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.
Here is how one of the maintainers explains the reasons for this decision:
Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.
It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.

Jetpack Compose Surface strange behavior [duplicate]

This code fills the full screen if i specify the size to be 100.dp.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
This code behave properly by filling the required size.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
}
Can somebody please explain why is it happening?
This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.
As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.
Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.
Here is how one of the maintainers explains the reasons for this decision:
Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.
It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.

Does jetpack compose use drawable-night folder?

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.

Categories

Resources