Yes, your app should work in both portrait and landscape modes – unless locked orientation is essential for functionality, like in a piano app. However, for most apps, locked orientation is not an option. Oh, and yes, the functionality should be similar in both modes, not hiding anything when the orientation changes.
In this blog post, I’ll cover the topic of locked orientation. It’s mainly concentrated on Compose things, but there might be something useful for Views, too. I’ve also included some good articles in the Read More section of the blog post, explaining, e.g., more about orientation locking themes on Views.
Why Locking Orientation is a Problem?
When discussing locked orientation and its problems, we usually refer to locking the orientation to portrait mode. Unfortunately, this is a typical case among Android apps.
Why is this a problem, then? Some people prefer to use their phones in landscape mode because it gives them more screen width. A wider screen can make it easier for people who use, e.g., larger font sizes of screen magnification. And some people might have their phones mounted on, e.g., a wheelchair in landscape mode, and can’t rotate the phone at all.
In these cases, if the app’s orientation is locked in portrait mode, using the app might be more complicated or even impossible. That is not what any developer behind the apps wants, right? So, let’s talk about how to support different orientations on your app.
It’s Straightforward
From a purely UI point of view, supporting landscape mode is pretty straightforward. You just don’t do anything – like prevent the screen rotation. This way, the landscape mode is available.
Of course, you need to test that the layouts actually look okay in landscape orientation. With Compose, the layout mostly works if you don’t use exact widths for different elements and don’t have sticky items that take up some vertical space on the screen. However, in case you notice some problems, I’ve described one way to handle different layouts for landscape and portrait modes later in this post.
But You Need to Remember Configuration Changes
But there is one thing: You need to remember configuration changes. Switching the screen orientation is a configuration change, which can then trigger all kinds of things.
Android developer documentation has an extensive page on reacting to configuration changes and the logic behind them: Handle Configuration Changes. In the following subsections, I’m going to concentrate on Jetpack Compose-related things. We’ll first discuss UI-related changes and then data-related changes.
Handling UI Changes
Although I mentioned that the UI usually looks pretty okay in landscape mode if there are no sticky elements or fixed widths for elements, the experience could often be better.
Let’s say we have a button with a short text like “Save” that takes the full width in portrait mode. In landscape mode, we would like to keep the button’s width at 75% of the screen width.
To accomplish this, we need to get the current orientation and then act on its value. We can get it from LocalConfiguration composition local. Let’s look at an example:
fun OrientationExample() {
val orientation = LocalConfiguration.current.orientation
when (orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
// Version for Landscape orientation
}
else -> {
// Version for Portrait orientation
}
}
}
Here, we first read the orientation from LocalConfiguration.current and then do something based on its value. So, for the example of our button, we could use this to save the value modifier uses for the width:
Configuration.ORIENTATION_LANDSCAPE -> 0.75f
else -> 1f
}
And then use it in the code:
modifier = Modifier.fillMaxWidth(buttonWidth)
...
)
This way, our UI can react to screen orientation changes to provide a better user experience.
Handling Data with Configuration Changes
The other issue with non-locked screen orientation is data loss. If we store something on a composable, it will be recreated on each recomposition unless we wrap it to remember, which saves things over recompositions. remember, however, doesn’t store its value through activity recreation, which happens with configuration changes.
If you need to store something inside a composable through configuration changes, you can use rememberSaveable. It works well with primitive data types, but if you need to store some more complex data, you’ll need to utilize different state-saving methods listed in the Android documentation: State and Jetpack Compose – Ways to store state.
There’s one more thing I want to talk about: Business logic. If you’re storing your data to a ViewModel, orientation changes are handled on that side automatically. However, note that ViewModels don’t survive system-initiated process deaths. For that, Android documentation suggests using SavedState APIs, and if you want to read more about that, the information is in Save UI state in Compose – SavedStateHandle APIs.
Wrapping Up
In this blog post, we’ve discussed screen orientation changes, how a non-locked screen orientation is an accessibility requirement, and what aspects you should consider when developing applications related to screen orientation.
What’s your experience with locked screen orientations, either as a user or as a developer?
Read More And Links in the Blog Post
Doug Stevenson – Do you have to support landscape in your Android app?
Mobile A11y – Quick Win – Support Landscape
Handle Configuration Changes
State and Jetpack Compose – Ways to store state
Save UI state in Compose – SavedStateHandle APIs