Jetpack Compose

Jetpack Compose: Divider

This article covers using and customizing the “Dividers” components from the "Material 2" and "Material 3" libraries in the Jetpack Compose. In addition to that, we will explore the difference between implementation of the Divider, HorizontalDivider and VerticalDivider.
Alex Zhukovich 4 min read
Jetpack Compose: Divider
Table of Contents

Introduction

Divider is a widespread component used to separate content.

In this article, we will explore:

  • What is the difference between dividers in the "Material 2" and "Material 3" libraries?
  • How to use horizontal and vertical dividers in Jetpack Compose?
  • How to customize dividers from the "Material 3" library?
  • How to create a custom divider?

Jetpack Compose Divider

Let’s start by exploring the differences between dividers from the "Material 2" and "Material 3" libraries.

The Divider component from the "Material 2" library uses the Box container to display a divider on the screen.

Here is the source code of the Divider component from the "Material 2" library.

@Composable
fun Divider(
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.onSurface.copy(alpha = DividerAlpha),
    thickness: Dp = 1.dp,
    startIndent: Dp = 0.dp
) {
    ...
    Box(
        modifier.then(indentMod)
            .fillMaxWidth()
            .height(targetThickness)
            .background(color = color)
    )
}

The HorizontalDivider and VerticalDivider components from the "Material 3" library draw a line on a Canvas.

@Composable
fun VerticalDivider(
    modifier: Modifier = Modifier,
    thickness: Dp = DividerDefaults.Thickness,
    color: Color = DividerDefaults.color,
) = Canvas(modifier.fillMaxHeight().width(thickness)) {
    drawLine(
        color = color,
        strokeWidth = thickness.toPx(),
        start = Offset(thickness.toPx() / 2, 0f),
        end = Offset(thickness.toPx() / 2, size.height),
    )
}
All the following examples will be based on the "Material 3" implementation.

Both horizontal and vertical dividers have the modifier, thickness, and color parameters. By default, they have the following values:

  • thickness: 1.dp
  • color: MaterialTheme.colorScheme.outlineVariant

Horizontal Divider

The HorizontalDivider represents a thin horizontal line that splits different elements. By default, it uses all available width.

@Composable
fun Demo_HorizontalDivider() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        Text("Item 1", fontSize = 18.sp)
        HorizontalDivider()
        Text("Item 2", fontSize = 18.sp)
    }
}

Vertical Divider

The VerticalDivider represents a thin vertical line that splits different elements. By default, it uses all available height, which is why we need to specify the height of the Row container. I recommend using .height(IntrinsicSize.Min) because it will properly render all children inside the Row using the minimum possible height.

The VerticalDivider doesn’t add any space before and after the divider.

@Composable
fun Demo_VerticalDivider() {
    Row(
        modifier = Modifier.padding(16.dp)
            .height(IntrinsicSize.Min)
    ) {
        Text("Item 1", fontSize = 18.sp)
        VerticalDivider(
            modifier = Modifier.padding(horizontal = 16.dp),
            thickness = 1.dp,
            color = Color.DarkGray
        )
        Text("Item 2", fontSize = 18.sp)
    }
}

Customizing the Divider

Both HorizontalDivider and VerticalDivider have relatively limited customization options, as they have only the following parameters:

  • modifier: Modifier
  • thickness: Dp
  • color: Color

If we want to change the size of a divider or add padding, we need to use the modifier: Modifier parameter for it.

Adjusting Divider Colors

To set a color, we need to pass it via a parameter. We can use the Jetpack Compose Color type or pass a color from the colors.xml file using colorResource(id = R.color.myColor).

@Composable
fun Demo_HorizontalDivider() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        Text("Item 1", fontSize = 18.sp)
        HorizontalDivider(color = Color.DarkGray)
        Text("Item 2", fontSize = 18.sp)
    }
}

Changing Divider Thickness

To specify the thickness of the horizontal and vertical dividers, we need to pass the value for the thickness: Dp parameter.

@Composable
fun Demo_HorizontalDivider_thickness() {
    Column(
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.spacedBy(10.dp)
    ) {
        for (thicknessValue in 1..10) {
            HorizontalDivider(
                thickness = thicknessValue.dp
            )
        }
    }
}

Creating a Custom Divider

Of course, the HorizontalDivider and VerticalDivider don’t cover all use cases. To create a custom divider, we can adopt a similar approach by drawing on a canvas to suit the specific needs of our application.

Let’s imagine we want to build a file manager application and we need to create a divider component that will separate segments of the path to the file.

So, let’s create a custom divider that helps us implement a UI similar to what we see above.

@Composable
fun SegmentPathDivider(
    modifier: Modifier = Modifier,
    width: Dp = 20.dp,
    thickness: Dp = DividerDefaults.Thickness,
    color: Color = DividerDefaults.color,
) = Canvas(modifier.fillMaxHeight().width(width)) {
    ...
}

Drawing a diagonal line requires more width than a vertical line, so we need to consider the width parameter. In our case, we will draw a line between 25% and 75% of the width. (By default, the VerticalDivider doesn’t add any additional space before and after the divider.)

@Composable
fun SegmentPathDivider(
    modifier: Modifier = Modifier,
    width: Dp = 20.dp,
    thickness: Dp = DividerDefaults.Thickness,
    color: Color = DividerDefaults.color,
) = Canvas(
    modifier
        .fillMaxHeight()
        .width(width)) {
    drawLine(
        color = color,
        strokeWidth = thickness.toPx(),
        start = Offset(thickness.toPx() / 2 + width.toPx() * 0.25f, size.height),
        end = Offset(thickness.toPx() / 2 + width.toPx() * 0.75f, 0f)
    )
}

The final step is to apply the SegmentPathDivider in the user interface of our application.

@Composable
fun Demo_SegmentPathDivider() {
    val pathSegments = listOf("Projects", "Important project", "src")
    Row(
        modifier = Modifier
            .padding(16.dp)
            .height(IntrinsicSize.Min)
    ) {
        pathSegments.forEachIndexed { index, segment ->
            Text(text = segment, fontSize = 20.sp)
            if (index < pathSegments.size - 1) {
                SegmentPathDivider(color = Color.Black)
            }
        }
    }
}

Conclusion

As you can see, the HorizontalDivider and VerticalDivider from the "Material 3" library are easy to use, but they are relatively limited in customizations.

Under the hood, this is just a Canvas with a drawn line. So, we can easily create a custom divider and reuse it in many places of our application.


Mobile development with Alex

A blog about Android development & testing, Best Practices, Tips and Tricks

Share
More from Mobile development with Alex
Jetpack Compose: Switch
Jetpack Compose

Jetpack Compose: Switch

This article covers creating and customizing the "Switch" component in Jetpack Compose for enabling/disabling features. It explores differences between "Material" and "Material 3" libraries, and how to interact with and verify the Switch component's state in UI tests.
Alex Zhukovich 8 min read

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Mobile development with Alex.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.