Following up on foldables

Thomas Künneth - Jul 25 '23 - - Dev Community

Welcome to the seventh part of Understanding foldable devices. It's been almost six months since the previous instalment, Foldable-aware app layout, went live. Quite a few things have happened. First, the Google Pixel Fold is here. Although it comes with a hefty price tag, it will hopefully drive interest in this still new device category. Second, Google's most important apps are starting to look good on large screens. Which brings us to... third, there's also the Pixel Tablet, so Google is finally back in the tablet game.

There's also a not so nice thing: it now is pretty clear that Microsoft lost interest in its Surface Duo and Surface Duo 2 foldables. But often, when vendors fail, the community jumps in. Here, this is the case, too. Thai Nguyen, a software engineer who in the past has worked at Microsoft, released a couple of Android 13 builds for the Surface Duo and the Duo 2. The builds are based on PixelExperience, an AOSP based ROM with Google apps and all Pixel goodies included. What's important, Thai makes use of the foldable features of the Duos in a way really close to the Pixel Fold. If you have a Surface Duo or Duo 2, have a look at the corresponding XDA thread to get started.

compose_adaptive_scaffold

To make writing apps that look great on foldables and large screens as easy as possible, I started an open source library called compose_adaptive_scaffold. You can find it in the Google Dev Library and on GitHub.

compose_adaptive_scaffold is based on the idea of two panes, called body and secondary body. For small screens you pass alternatives (or variations) called small body and small secondary body (the latter one is optional). Depending on your screen layout, the pairs body and small body, and secondary body and small secondary body may even be the same. Two panes are the basis for Canonical Layouts, an important Material Design concept. Before I show you how easy it is to create the panes, I'd like to mention that compose_adaptive_scaffold is inspired by the Flutter package flutter_adaptive_scaffold.

To use compose_adaptive_scaffold, you just need to add it as an implementation dependency:

dependencies {
  implementation "com.github.tkuenneth:compose_adaptive_scaffold:0.2.1"
}
Enter fullscreen mode Exit fullscreen mode

Next, let's look at the activity.

class AdaptiveScaffoldDemoActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    lifecycleScope.launch {
      lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        setContent {
          
          MaterialTheme(
            content = {
              
            },
            colorScheme = defaultColorScheme()
          )
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

launch and repeatOnLifecycle are needed to get the underlying machinery (Jetpack WindowManager) going. A future version of my library might even do this for you. defaultColorScheme() is a nice little helper that gives your app Dark mode support and dynamic colours on supported systems.

Here's how content is defined:

content = {
  AdaptiveScaffold(
    useDrawer = true,
    startDestination = destination1,
    otherDestinations = listOf(destination2),
    onDestinationChanged = {
      // do something
    },
    topBar = {
      TopAppBar(
        title = {
          Text(
            text = stringResource(
              id = R.string.app_name
            )
          )
        })
    },
  )
},
Enter fullscreen mode Exit fullscreen mode

All magic is handled by a composable called AdaptiveScaffold(). Besides panes, it is based upon destinations. You pass a start destination, as well as a list of other destinations. Depending on the horizontal Window Size Class, compose_adaptive_scaffold uses a bottom navigation, a navigation rail, or a navigation drawer.

The following screenshot was taken on a simulated flip phone in portrait mode.

An unfolded flip phone in portrait mode

Here, the phone was rotated to landscape mode:

An unfolded flip phone in portrait mode

To understand why the colours have changed, let's look at the definition of destination1.

val destination1 = NavigationDestination(
  icon = R.drawable.ic_android_black_24dp,
  label = R.string.app_name,
  body = {
    Box(
      modifier = Modifier
        .fillMaxSize()
        .background(color = Color.Red)
    )
  },
  secondaryBody = {
    Box(
      modifier = Modifier
        .fillMaxSize()
        .background(color = Color.Green)
    )
  },
  smallBody = {
    Box(
      modifier = Modifier
        .fillMaxSize()
        .background(color = Color.Blue)
    )
  },
  smallSecondaryBody = {
    Box(
      modifier = Modifier
        .fillMaxSize()
        .background(color = Color.Yellow)
    )
  },
)
Enter fullscreen mode Exit fullscreen mode

So, a NavigationDestination gets an icon, a label, and two pairs describing the panes:

  1. body and secondary body
  2. small body and small secondary body

Spanning panes

A destination may also receive an overlay.

val destination2 = NavigationDestination(
  icon = R.drawable.ic_android_black_24dp,
  label = R.string.app_name,
  overlay = {
    Box(
      modifier = Modifier
        .fillMaxSize()
        .background(color = Color.LightGray)
    )
  },
)
Enter fullscreen mode Exit fullscreen mode

Here we don't provide any pane. What's that useful for?

The second destination showcasing an overlay

An overlay spans the two panes, it is laid out on top of them. In my example the panes are empty and the overlay uses all available space, but you can also make the overlay smaller, so it floats above the panes.

What's next?

compose_adaptive_scaffold is still in its infancy. I need to explore how to use it with canonical layouts. Also, combining the library with existing navigation frameworks is on the to do list. What else am I missing? And how do you like the general idea? Please share your thoughts in the comments.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player