Skip to main content
Donovan LaDuke - Developer

Improve Compose UI with Spacing Constants


Featured in Android Weekly Issue 639 As Seen In - jetc.dev Newsletter Issue #231

A standard approach to laying out UI is to adhere to an 8pt grid system. This system ensures consistency in the look and feel of an application's UI and a smooth layout on a variety of screen sizes. But perhaps even more importantly, it gives a consistent language to be shared between developers and designers. This can result in better collaboration and less discrepancies between design and implementation. Once the team adopts this mindset, the next step is to set up the project to use this approach. Below is outlined an implementation using Jetpack Compose.

Approach #

The simplest way to implement this approach is to use an enum or sealed class in Kotlin and create new overloads for the assorted spacing modifiers and/or composables (e.g. padding and Spacer). Then utilize the new methods instead of the existing methods. Here is a simplified example as an overview of the approach.

Example #

First, set up the sealed class and wrap the underlying dp values so they can be easily accessed.

sealed class Dimen(val dp: Dp)

data object Space0: Dimen(0.dp)
data object Space1: Dimen(8.dp)
data object Space2: Dimen(16.dp)

Then, create new overloads for the relevant spacing modifiers and/or composables.

fun Modifier.padding(all: Dimen): Modifier 
  = this.padding(all = all.dp)

fun PaddingValues(
  all: Dimen = Space0
): PaddingValues {
  return PaddingValues(all = all.dp)
}

@Composable
fun DimenSpacer(
  height: Dimen = Space0,
  width: Dimen = Space0,
  modifier: Modifier = Modifier,
) {
  Spacer(
    modifier = modifier.size(
      height = height.dp,
      width = width.dp,
    )
  )
}

Finally, replace existing uses of hard-coded spacing values with the new components.

@Composable
fun TestComposable(
  modifier: Modifier = Modifier,
) {
  Row(modifier = modifier) {
    Text(text = "Hello") 
    Spacer(height = Space1)
    Text(
      text = "World",
      modifier = Modifier
        .padding(all = Space2),
    )
  }
}

That's all it takes to start adopting a formal set of spacing constants.

The Why #

The immediate question is why bother? It may seem like a lot of overhead and Modifier.padding(all = 8.dp) looks pretty similar to Modifier.padding(all = Spacing1)? The short answer is that the version with a hard-coded constant 8.dp could easily become 7.dp and it wouldn't stand out. By constraining the valid values there is a pit of success that ensures consistency from both developers and designers. For designers, they get immediate feedback from developers if a value doesn't conform to the 8-pt grid and any deviation becomes an intentional choice. On the developers side, it becomes obvious in code review if an invalid size is used, ensuring consistent and correct values.

Conclusion #

Adopting spacing constants can be a benefit to both the project and the team by creating a consistent language of design. Consider trying this in a project to see how it works and as always tweak as necessary. Until next time, thanks!

Did you find this content helpful?

Please share this post and be sure to subscribe to the RSS feed to be notified of all future articles!

Want to go above and beyond? Help me out by sending me $1 on Ko-fi. It goes a long way in helping run this site and keeping it advertisement free. Thank you in advance!

Buy me a Coffee on Ko-fi