SYSONLINE
SDK36 · MIN 24
KOTLIN2.0
UTC07:37:18
UP00:01
MOD · ARTICLE · JETPACK COMPOSES/N · AX-COMPOSPUBLISHED
Jetpack ComposeJun 9, 202610 MIN

Your Phone App Is Already a Tablet App: Adaptive Layouts in Jetpack Compose

Android 16 ignores orientation and resize locks on screens 600dp and wider. Window size classes and canonical layouts are how a phone app survives it.

By Rocky Elsalaymeh · Founder & Principal Consultant, Strategia-X

The Lock on Your Activity Stopped Holding

Most phone apps ship a quiet act of refusal in their manifest: android:screenOrientation="portrait", resizeableActivity="false", a max aspect ratio. On a tablet the system used to honour it and pillarbox the app in a phone-shaped column with black bars down both sides. Ugly, but contained. That containment is gone. Google's Android 16 behavior changes are blunt about it: "For apps targeting Android 16 (API level 36), orientation, resizability, and aspect ratio restrictions no longer apply on displays with smallest width >= 600dp. Apps fill the entire display window, regardless of aspect ratio or a user's preferred orientation, and pillarboxing isn't used." Read that as an engineering fact, not a design suggestion. The day you bump targetSdk to 36, every layout you only ever tested at 411dp wide gets stretched across a foldable's inner display, a 13-inch tablet in landscape, and a desktop window the user can drag to any size. A Column of full-width buttons becomes a column of 1,200dp-wide buttons. A bottom sheet becomes a letterbox. A camera preview locked to portrait rotates with the device. There is an escape hatch, and it has an expiry date. The manifest property PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY opts an app out for now, but the same page warns that "the opt-out is temporary and won't apply when targeting API level 37." Google has already published the rest of the schedule: Android 17 removes the opt-out, and "for Google Play, new apps and updates will be required to target API level 37, making this behavior mandatory for distribution in August 2027." Games declared through android:appCategory and screens narrower than 600dp are exempt. Everything else gets stretched.

Why Google Is Forcing the Issue

Platform teams do not break millions of manifests for aesthetics. They do it when the audience has moved. The numbers Google publishes explain the urgency:
SignalWhat Google publishesSource
Installed base"More than 300 million Android large screen devices" in useGet started with adaptive apps
Updated installed base"over 580 million large screen devices in the hands of users"Android Developers Blog, May 2026
Revenue"Multi-device users spend 9x more on average than phone only users"Android Developers Blog, May 2026
Ranking"Play ranks apps and games optimized for large screens higher than unoptimized apps"Get started with adaptive apps
Store listingApps below the bar "display a warning on the app details page"Get started with adaptive apps
The two installed-base figures are not a contradiction; they are two Google pages updated at different times, and both say the same thing: the large-screen segment is too big to letterbox. The case studies say the same thing in revenue terms. When Google announced the Android 16 change it cited FlipaClip, which "saw 54% growth in tablet users in the four months after they optimized their app to be adaptive." The contrarian read: adaptive layout is not a tablet feature. It is store placement, a warning label you do not want on your listing, and a revenue cohort that spends more than your phone-only users. Teams that treat it as polish are choosing to be ranked below the competitor who did the work.

Window Size Classes: Branch on Space, Never on Device

The most common adaptive bug is a question asked the wrong way. isTablet() helpers that read the device model, the density bucket, or Configuration.orientation all answer "what hardware is this?" when the layout needs to know "how much space do I have right now?" A foldable is one device with two answers. A tablet in split screen is a large device with a phone-sized window. A desktop window changes answer every time the user drags an edge. Window size classes are Google's answer: a small set of breakpoints computed from the current window, not the device. The breakpoints are opinionated on purpose:
Width classRangeGoogle's characterisation
Compactwidth < 600dp99.96% of phones in portrait
Medium600dp to 840dp93.73% of tablets in portrait, most large unfolded inner displays in portrait
Expanded840dp to 1200dp97.22% of tablets in landscape
Large1200dp to 1600dpLarge tablet displays
Extra-large1600dp and upDesktop displays
In Compose you read the class with currentWindowAdaptiveInfo() from androidx.compose.material3.adaptive, which recomposes whenever the window crosses a boundary. The Large and Extra-large classes are opt-in: the docs say to pass supportLargeAndXLargeWidth = true. The old windowWidthSizeClass enum is deprecated in favour of isWidthAtLeastBreakpoint() against the WIDTH_DP_*_LOWER_BOUND constants, which is also the safer shape: "at least medium" stays correct when a new, larger class is added above it, where an exhaustive when over an enum would silently fall through. Hoist the class once, near the root, and pass decisions down. A leaf composable should receive "show the detail pane beside the list", not a window size class it has to interpret.
kotlin
@Composable
fun AppRoot() {
    val sizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass

    // Ask "how much room do I have?", never "is this a tablet?"
    val layout = when {
        sizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) ->
            AppLayout.TwoPane
        sizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) ->
            AppLayout.ListWithRail
        else -> AppLayout.SinglePane
    }

    InboxScreen(layout = layout)
}

enum class AppLayout { SinglePane, ListWithRail, TwoPane }

Canonical Layouts: Stop Inventing a Tablet UI

The second most common failure is ambition. A team decides to "do tablets properly", designs a bespoke large-screen experience, and ships it eighteen months later, if ever. Google has already done the design research and packaged the result as three canonical layouts: list-detail, supporting pane, and feed. Almost every app screen is one of them.
Canonical layoutUse it forCompose API
List-detailInbox, settings, catalogue, chat listListDetailPaneScaffold, NavigableListDetailPaneScaffold
Supporting paneDocument plus comments, video plus relatedSupportingPaneScaffold
FeedEquivalent items in a gridLazy grids with adaptive columns
The navigable variant is the one to reach for first. Per the list-detail guide, NavigableListDetailPaneScaffold "wraps ListDetailPaneScaffold and adds built-in navigation and predictive back animations." On a compact window it behaves like two screens with a back gesture between them; on an expanded window it shows both panes; the navigator decides, so your code never branches on width at all. Navigation chrome follows the same rule. NavigationSuiteScaffold switches navigation UI by window size class, a bottom bar on a compact window and a rail on a wider one, "including dynamically changing the UI during runtime window size changes." A bottom navigation bar stretched across a landscape tablet is the single most recognisable sign of an app that was never adapted; this removes it in about ten lines.
kotlin
@Composable
fun InboxListDetail(messages: List<Message>) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()
    val scope = rememberCoroutineScope()

    NavigableListDetailPaneScaffold(
        navigator = navigator,
        listPane = {
            AnimatedPane {
                MessageList(
                    messages = messages,
                    onOpen = { id ->
                        scope.launch {
                            navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, id)
                        }
                    },
                )
            }
        },
        detailPane = {
            AnimatedPane {
                navigator.currentDestination?.contentKey
                    ?.let { id -> MessageDetail(messageId = id) }
                    ?: EmptyDetailPlaceholder()
            }
        },
    )
}

State Has to Survive the Fold

Adaptive layout fails in production less often at the pixel level than at the state level. Folding, unfolding, rotating and resizing are all configuration changes, and by default a configuration change recreates the Activity. The layout survives that because it is recomputed. Anything held in a plain remember does not. The failure is easy to recognise once you have seen it: a user selects a message on the outer display, unfolds the phone, and the detail pane opens empty because the selection lived in a remember that was thrown away. A video restarts from zero. A half-written reply vanishes. Google's guidance is one sentence long and worth enforcing in review: "Use ViewModel for data and business logic, and use rememberSaveable for UI-level state." Selection, scroll position, expanded or collapsed panes, draft text: rememberSaveable, or state in a ViewModel. The list-detail navigator above already saves its own destination, which is one more reason to use it rather than hand-rolling a selectedId variable. The cheapest test you can add is a recreation test. Compose's StateRestorationTester emulates a save-and-restore cycle without recreating the Activity, so "the selection survives a configuration change" becomes an assertion in CI instead of a bug report from a foldable owner.
kotlin
@Composable
fun ReplyComposer(threadId: Long) {
    // Survives rotation, fold/unfold and window resize.
    var draft by rememberSaveable(threadId) { mutableStateOf("") }
    var attachmentsExpanded by rememberSaveable { mutableStateOf(false) }

    OutlinedTextField(value = draft, onValueChange = { draft = it })
    AttachmentTray(expanded = attachmentsExpanded, onToggle = { attachmentsExpanded = !attachmentsExpanded })
}

@Test
fun draftSurvivesRecreation() {
    val tester = StateRestorationTester(composeTestRule)
    tester.setContent { ReplyComposer(threadId = 7) }

    composeTestRule.onNode(hasSetTextAction()).performTextInput("On my way")
    tester.emulateSavedInstanceStateRestore()

    composeTestRule.onNode(hasSetTextAction()).assertTextEquals("On my way")
}

A Migration Order That Actually Ships

Google grades large-screen support in three quality tiers. Tier 3, "Adaptive ready", means the app "runs full screen (or full window in multi-window mode) on all devices, but app layout might not be ideal." Tier 2, "Adaptive optimized", adds "layout optimizations for all screen sizes and device configurations along with enhanced support for external input devices." Tier 1, "Adaptive differentiated", is an experience designed for the device it is running on. Tier 3 is the floor API 36 imposes, and it is reachable in a sprint. Treat the tiers as a sequence, not a menu: 1. Delete the locks. Remove screenOrientation, resizeableActivity="false" and aspect-ratio caps, then run on a resizable emulator and a foldable profile. Fix what breaks: usually hard-coded widths, fillMaxWidth() on content that should cap its width, and camera or media code that assumed portrait. 2. Adopt NavigationSuiteScaffold. One change, every screen benefits, and the stretched bottom bar disappears. 3. Move the primary flow to list-detail. Pick the screen users spend the most time in. It is almost always a list. 4. Make state configuration-proof. Audit every remember on those screens; add a StateRestorationTester case for each piece of user-entered state. 5. Then input. Keyboard navigation, focus order and pointer hover are what separate Tier 2 from Tier 3, and they are easier once the layout is stable. The calendar is not negotiable. Google Play's target API requirements move new apps and updates to API 36 on August 31, 2026, with the temporary opt-out still available; API 37 removes it, and Play requires API 37 in August 2027. That is the whole runway. Spend it on steps 1 to 3 now, while the opt-out is a safety net rather than the only thing standing between your manifest and a stretched layout.
MOD · TAKEAWAYS6 POINTSSUMMARY

Key Takeaways

  1. 1For apps targeting API 36, Android ignores orientation, resizability and aspect-ratio restrictions on displays 600dp and wider; the temporary opt-out disappears at API 37.
  2. 2Google Play ranks large-screen-optimized apps higher and shows a warning on listings that miss its large-screen quality bar.
  3. 3Branch on window size classes from currentWindowAdaptiveInfo(), never on device model, density or orientation.
  4. 4Use isWidthAtLeastBreakpoint() with the WIDTH_DP_*_LOWER_BOUND constants; the windowWidthSizeClass enum is deprecated.
  5. 5Reach for the canonical layouts first: NavigableListDetailPaneScaffold and NavigationSuiteScaffold cover most screens without bespoke tablet design.
  6. 6Fold, unfold and resize are configuration changes: keep UI state in rememberSaveable or a ViewModel, and assert it with StateRestorationTester.
MOD · FAQ3 ENTRIESANSWERED

Frequently Asked

Does Android 16 ignore screenOrientation for every app?

No. It applies to apps targeting API 36 on displays whose smallest width is at least 600dp. Games declared with android:appCategory and smaller screens are exempt, and a temporary opt-out exists until API 37.

Should I check whether the device is a tablet?

No. Read the window size class from currentWindowAdaptiveInfo(). A foldable, a split-screen tablet and a resizable desktop window all change the available space without changing the device.

What is the fastest route to Google’s Tier 3 “Adaptive ready”?

Remove orientation and resizability locks, fix what breaks on a resizable emulator, and keep UI state in rememberSaveable or a ViewModel so a fold or resize does not lose it.

MOD · BUILD · NEXTS/N · AX-CTA-0001READY

Ready to architect your next Android app?

ANDROID-ARCHITECT generates production-ready Kotlin code, architecture blueprints, and CI/CD configurations from plain-language descriptions. Start building for free.

ANDROID-ARCHITECT · CONSOLE
S/N · AX-1A-00001
STRATEGIA-X · ENGINEERED · IN · CALIFORNIA