Getting started with jetpack compose — Modifiers

Velmurugan Murugesan
Howtodoandroid
Published in
11 min readAug 27, 2021

Modifiers are used to modifier certain aspects of how the component is displayed on the screen. The Modifier itself is an interface, so we don’t directly instantiate an instance of that — instead, we use the provided subclasses to decorate our component in some way.

And when it comes to applying these modifiers, we can supply either a single modifier for our component or chain multiple modifiers together to apply multiple decorations to view components.

Modifiers let you,

  • Change the composables behaviour and appearance
  • Add information like accessibility labels
  • Process user input
  • Add high-level interactions like making an element clickable, scrollable, draggable or zoomable.

Combine Modifier

We can chaining multiple modifiers at a time to the composable.

@Composable
fun CombinedModifier() {
Text(text = "Jetpack compose", modifier = Modifier
.size(200.dp,100.dp)
.background(Color.Black).padding(10.dp) )
}

In the above code, we are adding a size modifier, background modifier, and padding modifier. We can add any modifiers like this.

Modifier Ordering

Modifiers will be executed in the order of what we provided. For example,

@Composable
fun DisplayMessage() {
Text(text = "Hello World!", modifier = Modifier.size(200.dp, 100.dp)
.padding(10.dp).background(Color.Gray))
}

In the modifiers, we are setting padding first, after that we are setting the grey background. that is why you can able to see the padding applied to the full view after that the grey background is added.

@Composable
fun DisplayMessage() {
Text(text = "Hello World!", modifier = Modifier.size(200.dp, 100.dp)
.background(Color.Gray).padding(10.dp))
}

Now, I have added background first in the modifier. So that the grey background was added to the view first, then the padding was applied.

Size Modifier

The Size modifier allows us to set an exact value for both the width and the height of the desired component.

size

Modifier.size(size: Dp)

Used to set the same width and height for the composable element.

@Composable
fun SizeModifier() {
Column(Modifier.size(200.dp)) {
Text(text = "Hello", modifier = Modifier.size(100.dp,100.dp))
}
}

Modifier.size(width: Dp, height: Dp)

This modifier is used to set the different Widths and heights for the composable element.

@Composable
fun SizeModifier() {
Column(Modifier.size(200.dp, 100.dp)) {
Text(text = "Hello", modifier = Modifier.size(100.dp,100.dp))
}
}

Width

Modifier.width(width: Dp)

The width modifier is used to set the width for the component.

@Composable
fun SizeModifier() {
Column(Modifier.width(200.dp)) {
Text(text = "Hello", modifier = Modifier.size(100.dp,100.dp))
}
}

Height

Modifier.height(height: Dp)

Declare the preferred height of the content to be exactly.

@Composable
fun SizeModifier() {
Column(Modifier.height(200.dp)) {
Text(text = "Hello", modifier = Modifier.size(100.dp,100.dp))
}
}

FillMaxSize

Modifier.fillMaxSize(fraction: Float = 1f)

This will set the height/width of the Composable to the maximum available height/width.

@Composable
fun SizeModifier() {
Column(Modifier.size(150.dp)) {
Text(text = "Hello", modifier = Modifier.fillMaxSize().background(Color.Gray))
}
}

FillMaxSize accepts one parameter.

fraction — The fraction of the maximum size to use, between 0 and 1, inclusive. by default fraction 1.0f. so the composable will take full width and height. But If the fraction is 0.5f. then the composable will take half of the width and height. check below example,

@Composable
fun SizeModifier() {
Column(Modifier.size(150.dp)) {
Text(text = "Hello", modifier = Modifier.fillMaxSize(0.5f).background(Color.Gray))
}
}

FillMaxHeight

Modifier.fillMaxHeight(fraction: Float = 1f)

This will set the height of the Composable to the maximum available height.

This will accept one parameter fraction.

@Composable
fun SizeModifier() {
Row(Modifier.size(150.dp)) {
Text(text = "Hello", modifier = Modifier.fillMaxHeight().background(Color.Gray))
}
}

FillMaxWidth

Modifier.fillMaxWidth(fraction: Float)

This will set the width of the Composable to the maximum available width.

@Composable
fun SizeModifier() {
Row(Modifier.size(150.dp)) {
Text(text = "Hello", modifier = Modifier.fillMaxWidth().background(Color.Gray))
}
}

Padding

Modifier.padding(start: Dp = 0.dp, top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp): Modifier

Apply additional space along each edge of the content in Dp: start, top, end, and bottom.

@Composable
fun PaddingAllSide() {
Text(text = "Jetpack Padding", Modifier.padding(30.dp,10.dp,30.dp,10.dp))
}

Padding was added to all the sides of the Text. 30.Dp to Start and End. 10.Dp for top and bottom.

Modifier.padding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): Modifier

Apply horizontal dp space along the left and right edges of the content, and vertical dp space along the top and bottom edges.

@Composable
fun PaddingVerticalAndHorizontal() {
Text(text = "Jetpack Padding", Modifier.padding(30.dp,10.dp))
}

Apply all dp of additional space along each edge of the content, left, top, right, and bottom.

@Composable
fun PaddingAll() {
Text(text = "Jetpack Padding", Modifier.padding(30.dp))
}

Modifier.aspectRatio(ratio: Float,matchHeightConstraintsFirst: Boolean = false): Modifier

Using the aspect ratio modifier allows us to enforce a ratio for the sizing of our component, meaning that our component’s height and width will expand to fill the specified aspect ratio.

For example, if we wished to use the 1(height):2(Width) aspect ratio then we could pass the corresponding float value for that ratio :

@Composable
fun AspectRadio() {
Text(text = "Jetpack Padding", Modifier.aspectRatio(2f))
}

Scale

Modifier.scale(scale: Float): Modifier

Scale the contents of both the horizontal and vertical axis uniformly by the same scale factor.

@Composable
fun ScaleAll() {
Box(Modifier.size(200.dp)) {
Text(text = "Compose Scale", Modifier
.background(Color.Gray)
.scale(2f))
}
}

This will scale Text in both X and Y-Axis.

Modifier.scale(scaleX: Float, scaleY: Float): Modifier

Scale the contents of the composable by the following scale factors along the horizontal and vertical axis respectively. Negative scale factors can be used to mirror content across the corresponding horizontal or vertical axis.

@Composable
fun ScaleAxis() {
Box(Modifier.size(200.dp)) {
Text(text = "Compose Scale", Modifier
.background(Color.Gray)
.scale(1f,2f)
}
}

Draw Modifiers

background

Modifier.background(color: Color, shape: Shape = RectangleShape): Modifier

With this modifier, you can set a background color/shape for the Composable.

@Composable
fun BackgroundModifier() {
Text(text = "Hello Jetpack Compose", Modifier.background(Color.Red))
}

Parameters

color: Color — color to paint background with

s hape: Shape — Set the desired shape of the background. Default RectangleShape.

Modifier.background(brush: Brush,shape: Shape = RectangleShape,alpha: Float = 1.0f): Modifier

With this modifier, we can set the background color for View and Share. Also, we can adjust the opacity of the background.

Parameters
brush: Brush — brush to paint background with

shape: Shape — set the desired shape of the background

alpha: Float — Default 1.0f — Opacity to be applied to the brush, with 0 being completely transparent and 1 being completely opaque. The value must be between 0 and 1.

@Composable
fun BackgroundWithShapeModifier() {
val gradientBrush = Brush.horizontalGradient(
colors = listOf(Color.Gray, Color.DarkGray),
startX = 0.0f,
endX = 500.0f
)
Text(text = "Hello Jetpack Compose", Modifier
.padding(20.dp)
.background(gradientBrush, CutCornerShape(8.dp),1.0f)
.padding(20.dp))
}

Border

Modifier.border(width: Dp, color: Color, shape: Shape = RectangleShape): Modifier

Border() is used to set the border for the composable. This will take three parameters,

Parametes

width: Dp — width of the border

color: Color — Color of the border

shape: Shape — RectangleShape (Default) — the shape of the border

@Composable
fun BorderModifier() {
Text(text = "Hello Jetpack Compose", Modifier
.padding(20.dp)
.border(4.dp, Color.DarkGray)
.padding(20.dp))
}

Modifier.border(width: Dp, brush: Brush, shape: Shape): Modifier

This border() is used to draw the border using a brush. We can crate brush by using a gradient.

Parametes

width: Dp — width of the border

brush: Brush — brush to paint the border with

shape: Shape — RectangleShape (Default) — the shape of the border

@Composable
fun BorderWithBrush() {
val gradientBrush = Brush.horizontalGradient(
colors = listOf(Color.Red, Color.Blue, Color.Green),
startX = 0.0f,
endX = 500.0f,
tileMode = TileMode.Repeated
)
Text(
"Hello Jetpack Compose",
modifier = Modifier
.padding(10.dp)
.border(width = 2.dp, brush = gradientBrush, shape = CircleShape)
.padding(10.dp)
)
}

Clip

Modifier.clip(shape: Shape): Modifier

Clip the content to the desired shape (RectangleShape, CircleShape).

@Composable
fun ClipModifier() {
Text(
"Hello Jetpack Compose",
modifier = Modifier
.padding(10.dp)
.clip(CircleShape)
.background(Color.Gray)
.padding(10.dp)
)
}

ClipToBounds

Modifier.clipToBounds(): Modifier

Clip the content to the bounds of a layer defined at this modifier.

@Composable
fun ClipModifier() {
Text(
"Hello Jetpack Compose",
modifier = Modifier
.padding(10.dp)
.clipToBounds()
.background(Color.Gray)
.padding(10.dp)
)
}

Alpha

Modifier.alpha(alpha: Float): Modifier

Draw content with modified visibility. The range will be 0f to 1f.

@Composable
fun AlphaModifier() {
Text(
"Hello Jetpack Compose",
modifier = Modifier
.padding(10.dp)
.alpha(0.7f)
.background(Color.Gray)
.padding(10.dp)
)
}

Rotate

Modifier.rotate(degrees: Float): Modifier

Sets the degrees the view is rotated around the center of the composable.

@Composable
fun RotateModifier() {
Row(Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
Text(
"Hello Jetpack Compose",
modifier = Modifier
.padding(10.dp)
.rotate(45f)
.background(Color.Gray)
.padding(10.dp)
)
}
}

Shadow

Modifier.shadow(elevation: Dp,shape: Shape = RectangleShape,
clip: Boolean = elevation > 0.dp): Modifier

The shadow() is used to adding shadow around the composable.

Parameters
elevation: Dp — The elevation for the shadow in pixels

shape: Shape — RectangleShape(Default) — Defines a shape of the physical object

clip: Boolean — elevation > 0.dp (Default) — When active, the content drawing clips to the shape.

@Composable
fun ShadowModifier() {
Text(
"Hello Jetpack Compose",
modifier = Modifier
.padding(10.dp)
.shadow(4.dp, CircleShape)
.padding(10.dp)
)
}

Gesture Modifiers

Clickable

Modifier.clickable(enabled: Boolean = true, onClickLabel: String? = null, role: Role? = null,
onClick: () -> Unit): Modifier

Configure component to receive clicks via input or accessibility “click” event.

Parameters
enabled: Boolean — true (Default)- Controls the enabled state. When false, onClick, and this modifier will appear disabled for accessibility services

onClickLabel: String? = null (Default)- semantic / accessibility label for the onClick action

role: Role? = null (Default)- the type of user interface element. Accessibility services might use this to describe the element or do customizations

onClick: () -> Unit — will be called when the user clicks on the element

@Composable
fun ClickableModifier() {
val context = LocalContext.current
Text(
text = "Hello Jetpack Compose", Modifier
.clickable {
Toast
.makeText(context, "Clicked", Toast.LENGTH_SHORT)
.show()
}
.padding(8.dp)
)
}

Selectable

Modifier.selectable(selected: Boolean, enabled: Boolean = true, role: Role? = null,
onClick: () -> Unit): Modifier

Configure components to be selectable, usually as a part of a mutually exclusive group, where only one item can be selected at any point in time.

Parameters

selected: Boolean — whether or not this item is selected in a mutual exclusion set

enabled: Boolean — true (Default) — whether or not this selectable will handle input events and appear enabled from a semantics perspective

role: Role? = null (Default) — the type of user interface element. Accessibility services might use this to describe the element or do customizations

onClick: () -> Unit — callback to invoke when this item is clicked

@Composable
fun SelectableModifier() {
var selected by remember {
mutableStateOf(false)
}
val selectedColor = if (selected) Color.Black else Color.Gray
Text(text = "Jetpack compose", color = selectedColor, modifier = Modifier.selectable(
selected = selected,onClick = {
selected = !selected
}
)
.padding(8.dp))
}

Swipable

Modifier.swipeable(state: SwipeableState,anchors: Map,orientation: Orientation,enabled: Boolean = true,reverseDirection: Boolean = false,interactionSource: MutableInteractionSource? = null,
thresholds: (from, to) -> ThresholdConfig = { , -> FixedThreshold(56.dp) },
resistance: ResistanceConfig?= resistanceConfig(anchors.keys),velocityThreshold: Dp = VelocityThreshold): @ExperimentalMaterialApi Modifier

Enable swipe gestures between a set of predefined states.

Parameters

s tate: SwipeableState — The state of the swipeable.

anchors: Map — Pairs of anchors and states, used to map anchors to states and vice versa.

orientation: Orientation — The orientation in which the swipeable can be swiped.

enabled: Boolean = true — Whether this swipeable is enabled and should react to the user’s input.

reverseDirection: Boolean = false — Whether to reverse the direction of the swipe, so a top to bottom swipe will behave like bottom to top, and a left to right swipe will behave like right to left.

i nteractionSource: MutableInteractionSource? = null — Optional MutableInteractionSource that will passed on to the internal Modifier.draggable.

thresholds: (from, to) -> ThresholdConfig = { , -> FixedThreshold(56.dp) } — Specifies where the thresholds between the states are. The thresholds will be used to determine which state to animate to when swiping stops. This is represented as a lambda that takes two states and returns the threshold between them in the form of a ThresholdConfig. Note that the order of the states corresponds to the swipe direction.

resistance: ResistanceConfig? = resistanceConfig(anchors.keys) — Controls how much resistance will be applied when swiping past the bounds.

velocityThreshold: Dp = VelocityThreshold — The threshold (in dp per second) that the end velocity has to exceed in order to animate to the next state, even if the positional thresholds have not been reached.

@Composable
fun SwipeableModifier() {
val width = 320.dp
val squareSize = 50.dp
val barSize = 20.dp

val swipeableState = rememberSwipeableState("A")
val sizePx = with(LocalDensity.current) { (width - squareSize).toPx() }
val anchors = mapOf(0f to "A", sizePx / 2 to "B", sizePx to "C")

Column {
Box(
modifier = Modifier
.fillMaxWidth()
.height(squareSize)
.swipeable(
state = swipeableState,
anchors = anchors,
thresholds = { _, _ -> FractionalThreshold(0.5f) },
orientation = Orientation.Horizontal
)
) {

Text(text = "", Modifier.width(width).height(barSize)
.background(Color.Black, shape = CircleShape).align(Alignment.CenterStart))

Box(
Modifier
.offset { IntOffset(swipeableState.offset.value.roundToInt() , 0) }
.size(squareSize)
.background(Color.Blue,shape = CircleShape), contentAlignment = Alignment.Center
) {
Text(swipeableState.currentValue, color = Color.White, fontSize = 24.sp)
}
}
Spacer(modifier = Modifier.padding(20.dp))

}
}

In the above example, First I have created Box view and make it swipeable. On top of it, I have added another Box view for the Swipe handler. Now the View on top of the swipe view is swipeable.

onKeyEvent

Modifier.onKeyEvent(onKeyEvent: (KeyEvent) -> Boolean): Modifier

Adding this modifier to the modifier parameter of a component will allow it to intercept hardware key events when it (or one of its children) is focused.

@Composable
fun OnKeyEventModifier() {
var name by remember {
mutableStateOf("")
}
TextField(value = name, onValueChange = {name = it},
Modifier.onKeyEvent {
if (it.key.keyCode == Key.Enter.keyCode) {
name = ""
}
return@onKeyEvent true
})
}

Thanks for reading. You can download this example from GitHub.

Originally published at https://howtodoandroid.com on August 27, 2021.

--

--

Velmurugan Murugesan
Howtodoandroid

Lead Android Engineer @htcindia | @github contributor | Blog writer @howtodoandroid | Quick Learner