Tiny things on big screens
Tags: android, jetpackcompose, foldables, ux
Published: 2022-01-07 12:45:54

Tablets and foldables offer plenty of screen real estate, especially when compared to mid-sized smartphones. A lot of proven user interface design patterns help you arrange the content of your app on large devices, for example *Master Detail*. But what if your app doesn't need all that space? This article explores how to deal with *too much* room.

To get an idea what I am talking about, take a look at the following screenshot.

![A small tool using one screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw0z5mikbtnod1ov0w6o.png)
 
[Time Calculator](https://github.com/tkuenneth/timecalculator) can add and subtract hours, minutes, and seconds. Nothing more. So, naturally, there is not much that can take advantage of a big screen. In *one screen mode*, the user interface looks fine. Here's how the app looks on two screens.

![Time Calculator on two screens](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5kbinfvyl85kxdzrih2.png)
 
The hinge of the device obstructs parts of some user interface elements. But that's not the main issue here. What I am referring to is: the app tries to respect the screen size, but this results in ridiculously large buttons.

This begs the question: *Should we do this?*

With such a huge virtual dial pad, entering the times to add or subtract is an unpleasant experience, because the fingers need to travel (relatively) large distances. Smaller keys would reduce the movement, presumably resulting in a more natural, fluent input. 

Let's try to draw some inspiration from well known Android apps. The following screenshot was taken from a simulated 10 inch device running Android 12L. It shows the *Clock* app in *Timer* mode.

![The Clock app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4ym83jumzs6r7yuyvhv.png)
 
The virtual keys are somewhat big, too. But the pad as a whole appears almost centred. Let's see if this works for *Time Calculator*.

![Time Calculator centred on a large screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rxe7cao3u6gsrtj3x2we.png)

Not bad.

You can easily centre your existing user interface with `BoxWithConstraints()`:

```kotlin
BoxWithConstraints(
  modifier = Modifier
    .fillMaxSize()
    .background(color = MaterialTheme.colorScheme.tertiaryContainer),
  contentAlignment = Alignment.Center
) {
  Column(
    modifier = Modifier
      .width(min(maxWidth, 600.dp))
      .background(color = MaterialTheme.colorScheme.surface)
  ) {
  …
```

This way you can set a maximum width, in my example 600 density independent pixels. 

So, what's our verdict? Should we go for this? While the coloured boxes to the left and to the right are not particularly pleasing, the user interface looks less *inflated*. And directing the attention to the centre of the screen is not a bad thing either, right?

![The modified version of Time Calculator on a device with a hinge](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fhf50zuzndgr8c2lczsp.png)
 
Well, on a device with a hinge that area may be obstructed. As you can see in the screenshot, the hinge *divides* the main area of the app. Not pleasing at all.

Let's refine our recipe.

- On ordinary devices, do nothing
- On large screens without a hinge that obstructs content, consider centring your user interface horizontally
- On devices with an obstructing or blocking hinge, show your user interface on one side of the hinge

With *Jetpack WindowManager* this is quite easy. First, add an implementation dependency to your module-level *build.gradle* file:

```groovy
implementation "androidx.window:window:1.0.0-rc01"
```

When the Activity is created, we need to check window bounds and some hinge-related information. This is done like this:

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  lifecycleScope.launchWhenResumed {
    setContent {
      val li by WindowInfoTracker.getOrCreate(this@TimeCalculatorActivity)
        .windowLayoutInfo(this@TimeCalculatorActivity).collectAsState(
          initial = null
        )
      val wm = WindowMetricsCalculator.getOrCreate()
        .computeCurrentWindowMetrics(this@TimeCalculatorActivity)
  …
  Content(li, wm)
  …
```

Here's what `Content()` does:

```kotlin
@Composable
fun Content(layoutInfo: WindowLayoutInfo?,
            windowMetrics: WindowMetrics) {
  …
  val hingeDef = createHingeDef(layoutInfo, windowMetrics)
  val largeScreen = windowWidthDp(windowMetrics) >= 600.dp
  BoxWithConstraints(
    modifier = Modifier
      .fillMaxSize()
      .background(color =  
         MaterialTheme.colorScheme.tertiaryContainer),
    contentAlignment = if (hingeDef.hasGap and largeScreen)
      Alignment.TopStart
    else
      Alignment.Center
  ) {
    val width = min(
      maxWidth, if (hingeDef.hasGap)
        hingeDef.sizeLeft
      else
        600.dp
    )
    Column(
      modifier = Modifier
        .width(width)
        .background(color = MaterialTheme.colorScheme.surface)
    ) {
      Column(
        …
```

As you can see, my bullet point list can be nicely implemented with a few `if`s. `createHingeDef()` and `windowWidthDp()` are small helper functions. You can find them [here](https://github.com/tkuenneth/timecalculator/blob/master/app/src/main/java/com/thomaskuenneth/zeitrechner/HingeHelper.kt). The following screenshot shows the result:

![Hinge-aware version of Time Calculator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ef7us1nnl25aijmhle7d.png)
 
I think this looks much better. But it is not ideal yet. Because showing *nothing* is not very inviting, right?

### Adding content 

Here's how the *Phone* app on the Surface Duo Emulator looks like on two screens:

![The Phone app on the Surface Duo Emulator in two screen mode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihn7cb3fm7btuv6wwv0r.png)

While it currently does a poor job in respecting the hinge, it shows us what we can do with the additional space: display helpful content. This may be:

- actions (something the user can do)
- tips or tricks
- a brief set of instructions

If your app has a settings page, you might want to show it there. But please respect established screen flows and interaction patterns. Also, refrain from diverting the user, for example by showing unrelated information and content. Under no circumstances should you show or present something your user would not expect.

How do we implement this? Fortunately, my `HingeDef` class can help here, too, because it also contains the size of the gap and the size of the area to its right. Let's look at the implementation in *Time Calculator*.

```kotlin
BoxWithConstraints(
  modifier = Modifier
    .fillMaxSize()
    .background(color = MaterialTheme.colorScheme.tertiaryContainer),
  contentAlignment = if (hingeDef.hasGap and largeScreen)
    Alignment.TopStart
  else
    Alignment.Center
) {
  val width = min(
    maxWidth, if (hingeDef.hasGap)
      hingeDef.sizeLeft
    else
      600.dp
  )
  Row(
    modifier = Modifier
      .fillMaxSize()
      .background(color = MaterialTheme.colorScheme.surface)
  ) {
    Column(
      modifier = Modifier
        .width(width)
    ) {
      Column(…) {
        …
      }
      Spacer(modifier = Modifier.height(16.dp))
      Column(…) {
        …
      }
    }
    Spacer(modifier = Modifier.width(hingeDef.widthGap))
    Column(
      modifier = Modifier
        .width(hingeDef.sizeRight)
        .fillMaxHeight()
        .padding(16.dp),
      horizontalAlignment = Alignment.CenterHorizontally
    ) {
      Text(text = stringResource(id = R.string.info1))
      Spacer(modifier = Modifier.height(16.dp))
      Text(text = stringResource(id = R.string.info2))
      Spacer(modifier = Modifier.height(16.dp))
      Text(text = stringResource(id = R.string.info3))
      Spacer(modifier = Modifier.height(16.dp))
      Text(text = stringResource(id = R.string.info4))
    }
  }
}
```

So, all we do is adding three composables to a `Row()`.

![Time Caluclator shows useful information on large devices and foldables](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qamzdxifvh5kgo6ntxn3.png)
 

### Conclusion

Small utilities may not have enough content to *really* fill large screens. You can enhance the experience by adding worthwhile additional information. However, you should never show content unrelated to the core idea of your app. And you must refrain from *inventing* new interaction patterns just to populate the display. Have you written small tools and faced such issues? Please share your thoughts in the comments.