DEV Community

Cover image for Following up on foldables
Thomas Künneth
Thomas Künneth

Posted on

Following up on foldables

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.

Top comments (8)

Collapse
 
marlonlom profile image
Marlon López

Great post!
Reading about the library, i like to know:

How to handle that, in case of the folded device, the bottom navigation appear with adjusted width of the viewport? i mean, when the foldable device if separating, the bottom navigation bar (which sould appear when the first part of the divided screen is compact) is displaying in the left part of the separating screen, ah, in this example, im using a single scaffold composable

Thank you very much again for such good content about foldables on Android and I look forward to any reviews.

Collapse
 
tkuenneth profile image
Thomas Künneth

Hey Marlon. Glad you like my posts, thanks a lot. I'm not sure if I understand correctly. Could you maybe share a screenshot? Would love to discuss this further. Thank you

Collapse
 
marlonlom profile image
Marlon López

Hello,
I managed to hand draw this design of what the UI should look like for the scenario I described before.
The bottom navigation part is seen on the left.

Image description

I hope this drawing is of great help and explanation.

Regards,

Thread Thread
 
tkuenneth profile image
Thomas Künneth • Edited

Hi Marlon. Thanks for the drawing. Now I get it. At the moment, this unfortunately is not possible with compose_adaptive_scaffold. It follows a "both panes share one bottom navigation OR navigation rail" approach, depending on the window size class. What you could do, though, is use the two panes that compose_adaptive_scaffold provides, but implement the bottom navigation on your own. Your left pane would likely be a Column() with two children, your content and below that the bottom navigation. But you should even be able to use a Scaffold() instead.

Collapse
 
4gus71n profile image
Agustín Tomas Larghi

Nice! I haven't seen that many people talk about this subject. I'll play with some of this stuff on my Samsung Z Flip4 :P

Collapse
 
marlonlom profile image
Marlon López

great!! is there any info about building an android enumator for the samsung galaxy z flip 4? i know about the skin for download, but, it isnt the same

Collapse
 
tkuenneth profile image
Thomas Künneth

Unfortunately, I haven't heard of one, sorry

Collapse
 
tkuenneth profile image
Thomas Künneth

Sounds cool. Thanks for trying out the library. If you have questions, feel free to reach out