Getting started with jetpack compose — Modifiers
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.
If you’re new to Compose, I highly recommend going through the following articles:
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.