Adaptive Layout: conceitos-chave, classes de tamanho e qualificadores

Autor: IT Sectr Publicado: 2026-02-26 Tempo de leitura: 10 min

Adaptive Layout é uma abordagem de layout de interface onde o aplicativo usa layouts diferentes para diferentes tipos de dispositivos e orientações de tela. Ao contrário do responsive design, o adaptive layout alterna entre layouts pré-definidos em pontos de breakpoint. Este artigo aborda Size Classes e UITraitCollection no iOS, qualificadores sw600dp/layout-w600dp no Android e fornece exemplos práticos.

Pontos principais

  • Adaptive Layout — alternância discreta de layouts dependendo do tamanho da tela, orientação e tipo de dispositivo
  • Size Classes no iOS classificam o espaço como Compact ou Regular horizontal e verticalmente
  • UITraitCollection — um contêiner de características do ambiente iOS (tamanho, escala, force touch, gama de cores)
  • sw600dp — qualificador Android de largura mínima para tablets (7" e maiores)
  • layout-w600dp — qualificador de largura disponível que ativa quando há espaço suficiente na orientação atual

O que é Adaptive Layout?

Adaptive Layout é uma estratégia de UI onde o desenvolvedor cria layouts separados para cada tipo de dispositivo e alterna entre eles com base nas características da tela. Ao contrário do responsive design, onde os elementos fluem suavemente, o adaptive layout usa alternâncias discretas: telefone — um layout, tablet — outro, desktop — um terceiro.

A Apple introduziu Size Classes no iOS 8 (2014) junto com aplicativos universais (binários que funcionam tanto no iPhone quanto no iPad). O Google apresentou o qualificador smallestWidth (swdp) no Android 3.2 (2011). Ambos os mecanismos resolvem o mesmo problema: dar ao desenvolvedor uma ferramenta para criar diferentes UIs para diferentes telas sem duplicação de código.

As diretrizes modernas da Apple HIG e do Google Material Design concordam que o adaptive layout é essencial para aplicativos que suportam mais de um tamanho de tela. O Google Play marca a falta de adaptação para tablets como uma desvantagem. A App Store não tem um requisito formal, mas os usuários de iPad esperam uma experiência nativa, não um aplicativo de iPhone esticado.

Size Classes e UITraitCollection no iOS

Size Classes são uma característica do iOS que define o espaço disponível como Compact (Compacto) ou Regular (Regular) em cada eixo. UITraitCollection é um sistema iOS para passar características do ambiente pela hierarquia de views: tamanho, escala da tela, capacidade force touch, gama de cores (claro/escuro), configurações de acessibilidade.

Dispositivo / OrientaçãoHorizontalVertical
iPhone retrato (todos os modelos)CompactRegular
iPhone Plus/Pro Max paisagemRegularCompact
iPhone SE / mini paisagemCompactCompact
iPad retratoRegularRegular
iPad paisagemRegularRegular
iPad Split View (1/3 da tela)CompactRegular

O UITraitCollection é formado pelo sistema e passado do UIApplication através do UIWindow para cada UIView. Quando a orientação ou o tamanho da janela (Split View) muda, o sistema gera um novo UITraitCollection e chama traitCollectionDidChange. No SwiftUI, as mudanças são rastreadas através de Environment Values e a view é redesenhada automaticamente.

Exemplo de layout adaptativo em Swift

A adaptação via UITraitCollection no UIKit é implementada inscrevendo-se nas mudanças de trait collection e reconstruindo o layout. O exemplo abaixo mostra um controlador que alterna entre uma pilha vertical para iPhone e um layout dividido para iPad.

swift
// Controlador adaptativo com Size Classes
import UIKit

final class AdaptiveViewController: UIViewController {

    private let compactStack = UIStackView()
    private let regularStack = UIStackView()
    private let sidebar = UIView()
    private let content = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        updateLayout(for: traitCollection)
    }

    override func traitCollectionDidChange(
        _ previousTraitCollection: UITraitCollection?
    ) {
        super.traitCollectionDidChange(previousTraitCollection)
        if traitCollection.horizontalSizeClass
            != previousTraitCollection?.horizontalSizeClass {
            updateLayout(for: traitCollection)
        }
    }

    private func updateLayout(for traits: UITraitCollection) {
        if traits.horizontalSizeClass == .regular {
            showRegularLayout() // iPad: barra lateral + conteúdo
        } else {
            showCompactLayout() // iPhone: barra de abas + push
        }
    }

    private func showRegularLayout() {
        view.subviews.forEach { $0.removeFromSuperview() }
        regularStack.addArrangedSubview(sidebar)
        regularStack.addArrangedSubview(content)
        regularStack.frame = view.bounds
        regularStack.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(regularStack)
    }

    private func showCompactLayout() {
        view.subviews.forEach { $0.removeFromSuperview() }
        compactStack.addArrangedSubview(content)
        compactStack.frame = view.bounds
        compactStack.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(compactStack)
    }

    private func setupViews() {
        sidebar.backgroundColor = .systemGray6
        content.backgroundColor = .systemBackground
        compactStack.axis = .vertical
        regularStack.axis = .horizontal
    }
}

O método traitCollectionDidChange é chamado em qualquer mudança de traits, mas verificamos apenas horizontalSizeClass — o indicador mais significativo para troca de layout. No iOS 17+ é recomendado usar UIViewController.horizontalSizeClass como uma propriedade computada para atualizações reativas sem inscrição em mudanças.

Qualificadores Android: sw600dp e w600dp

O Android fornece dois qualificadores principais de largura: swdp (smallestWidth) e wdp (available width). A diferença é crítica: sw é a largura mínima da tela independentemente da orientação, w é a largura disponível considerando a orientação atual. sw garante que o layout carregue para um dispositivo que fisicamente tem a largura mínima especificada. w responde à rotação — em paisagem, w600dp pode ativar mesmo que a largura em retrato seja 360 dp.

QualificadorCondiçãoExemplo de dispositivo
layout-sw600dpLargura mínima ≥ 600 dpiPad (768 dp), Pixel C (900 dp)
layout-w600dpLargura atual ≥ 600 dpiPad em Split View (600 dp), telefone em paisagem
layout-landOrientação landscapeQualquer dispositivo girado horizontalmente
layout-sw720dpLargura mínima ≥ 720 dpiPad Pro (833 dp), Galaxy Tab S9 (800 dp)

Hierarquia de recursos no Android: res/layout/activity_main.xml (telefone), res/layout-sw600dp/activity_main.xml (tablet 7"), res/layout-w600dp-land/activity_main.xml (telefone em paisagem). O Android seleciona o qualificador mais específico que corresponde à configuração atual do dispositivo. Quando nenhum arquivo correspondente existe, o recurso base de res/layout/ é usado.

Exemplo de layout adaptativo em Kotlin

O desenvolvimento moderno Android usa Jetpack WindowManager para determinar o tamanho da janela em vez de ler recursos diretamente. Isso é especialmente importante para dispositivos dobráveis onde o tamanho da tela muda dinamicamente (Galaxy Fold se desdobra de 6.2" para 7.6").

kotlin
// Adaptação via Jetpack WindowManager
import androidx.window.core.layout.WindowHeightSizeClass
import androidx.window.core.layout.WindowSizeClass
import androidx.window.core.layout.WindowWidthSizeClass

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun AdaptiveScreen() {
    val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass

    FlowRow(
        verticalAlignment = Arrangement.Top,
        horizontalArrangement = when (windowSizeClass.windowWidthSizeClass) {
            WindowWidthSizeClass.EXPANDED -> Arrangement.SpaceEvenly
            else -> Arrangement.Start
        }
    ) {
        when (windowSizeClass.windowWidthSizeClass) {
            WindowWidthSizeClass.COMPACT -> {
                CompactContent()
            }
            WindowWidthSizeClass.MEDIUM -> {
                MediumContent()
            }
            WindowWidthSizeClass.EXPANDED -> {
                ExpandedContent()
            }
        }
    }
}

@Composable
private fun CompactContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        Text("Telefone", style = MaterialTheme.typography.headlineSmall)
        ListContent()
    }
}

@Composable
private fun MediumContent() {
    Row(modifier = Modifier.padding(24.dp)) {
        NavigationRail { ... }
        Column { ListContent() }
    }
}

@Composable
private fun ExpandedContent() {
    Row(modifier = Modifier.padding(32.dp)) {
        PermanentNavigationDrawer { ... }
        Column {
            ListContent()
        }
        DetailPane()
    }
}

O componente currentWindowAdaptiveInfo() da biblioteca androidx.window adapta a interface para qualquer dispositivo: telefone, tablet, dobrável, ChromeOS. Classes de largura: COMPACT (0–600 dp), MEDIUM (600–840 dp), EXPANDED (>840 dp). Classes de altura: COMPACT (0–480 dp), MEDIUM (480–900 dp), EXPANDED (>900 dp).

Adaptive Layout no SwiftUI

SwiftUI fornece ferramentas integradas para layout adaptativo sem usar diretamente Size Classes. AnyLayout, ViewThatFits e GeometryReader permitem construir interfaces que se ajustam automaticamente ao espaço disponível.

swift
// Adaptação SwiftUI via AnyLayout e ViewThatFits
import SwiftUI

struct AdaptiveGrid: View {
    @Environment(\.horizontalSizeClass) private var hSizeClass

    var body: some View {
        let layout = hSizeClass == .regular
            ? AnyLayout(HStackLayout())
            : AnyLayout(VStackLayout())

        layout {
            Label("Favoritos", systemImage: "star")
            Label("Recentes", systemImage: "clock")
            Label("Configurações", systemImage: "gear")
        }
        .padding()
    }
}

// ViewThatFits — seleção automática de layout
struct SmartLayout: View {
    var body: some View {
        ViewThatFits {
            HStack { // Prioridade 1: horizontal
                CardView()
                CardView()
            }
            VStack { // Se não couber — vertical
                CardView()
                CardView()
            }
        }
    }
}

ViewThatFits é uma ferramenta poderosa do SwiftUI que seleciona automaticamente a primeira view filha que cabe no espaço disponível sem cortes. Isso elimina a necessidade de verificar explicitamente Size Classes para alternâncias adaptativas simples. AnyLayout permite alternar o tipo de layout (HStack/VStack) sem if-else no corpo da view.

Adaptive Layout no Jetpack Compose

Jetpack Compose usa WindowSizeClass (da biblioteca Material 3 adaptive) e BoxWithConstraints para layout adaptativo. Ao contrário dos qualificadores XML, o Compose determina o tamanho da janela em tempo de execução e recompõe a UI reativamente em mudanças de configuração.

kotlin
// Jetpack Compose adaptativo via BoxWithConstraints
@Composable
fun AdaptiveList(items: List<String>) {
    BoxWithConstraints {
        val width = maxWidth

        if (width >= 600.dp) {
            // Tablet: duas colunas (list-detail)
            Row(modifier = Modifier.fillMaxSize()) {
                LazyColumn(modifier = Modifier.weight(1f)) {
                    items(items) { item ->
                        ListItem(text = item)
                    }
                }
                var selected by remember { mutableStateOf(items.first()) }
                DetailPanel(item = selected)
                    .weight(2f)
            }
        } else {
            // Telefone: uma coluna com navegação
            LazyColumn(modifier = Modifier.fillMaxSize()) {
                items(items) { item ->
                    ListItem(text = item, onClick = { navigateToDetail(item) })
                }
            }
        }
    }
}

@Composable
fun AdaptiveListMaterial3() {
    val windowClass = currentWindowAdaptiveInfo().windowSizeClass

    AdaptiveLayout(
        layout = windowClass.windowWidthSizeClass
    ) {
        when (windowClass.windowWidthSizeClass) {
            WindowWidthSizeClass.COMPACT -> {
                ListDetailScaffold(
                    isDetailOnly = false,
                    list = { ListPane() },
                    detail = { DetailPane() }
                )
            }
            else -> {
                ListDetailScaffold(
                    isDetailOnly = false,
                    list = { ListPane() },
                    detail = { DetailPane() }
                )
            }
        }
    }
}

Material 3 Adaptive Layouts fornece componentes prontos: ListDetailScaffold, SupportingPaneScaffold, NavigationSuiteScaffold. Esses componentes se adaptam automaticamente ao WindowWidthSizeClass — alternando entre empilhado (COMPACT) e lado a lado (MEDIUM/EXPANDED). O desenvolvedor só precisa escolher um padrão e passar os painéis de conteúdo.

Perguntas frequentes

Qual é a diferença entre sw600dp e w600dp?

sw600dp (smallestWidth) — largura mínima da tela independentemente da orientação. Ativa sempre em tablets de 7"+. w600dp (available width) — largura disponível considerando a orientação atual. Ativa em telefones em paisagem onde a largura pode exceder 600 dp.

O que é UITraitCollection no iOS?

UITraitCollection é um objeto iOS que contém características do ambiente: size classes (horizontal/vertical), escala de exibição, capacidade force touch, idiom de interface do usuário (iPhone/iPad), gama de cores, configurações de acessibilidade. É passado do UIApplication pela hierarquia de views e muda quando a orientação ou o tamanho da janela muda.

Como adaptar um aplicativo para dispositivos dobráveis?

Use Jetpack WindowManager (Android) e UIScreen nativeBounds + traitCollection (iOS). Dispositivos dobráveis mudam Size Class ao desdobrar. Recomenda-se uma arquitetura baseada em layouts canônicos (list-detail, supporting pane) com inscrição reativa a mudanças de tamanho de janela sem reiniciar a Activity.

Preciso criar storyboards separados para iPad?

Não. Use um único storyboard com variações de constraints por Size Class ou layout programático via UIKit. SwiftUI não requer storyboards. Storyboards separados para iPad criam duplicação e complicam a manutenção — é melhor adaptar um único layout através de Size Classes.

O que são layouts canônicos no Material 3?

Canonical layouts são padrões de layout adaptativo prontos do Google: list-detail (lista + detalhe), supporting pane (conteúdo + painel de ações), feed (cartões). Cada padrão suporta três estados (compact/medium/expanded) e está implementado na biblioteca Material 3 Adaptive através de ListDetailScaffold e SupportingPaneScaffold.

Resumo

  • Adaptive Layout — alternância discreta de layouts em breakpoints para diferentes dispositivos
  • Size Classes iOS (Compact/Regular) definem o layout para iPhone, iPad e Split View
  • UITraitCollection — mecanismo para passar características do ambiente pela hierarquia de views
  • sw600dp — qualificador Android de largura mínima para tablets 7"+
  • w600dp — qualificador de largura disponível que ativa ao girar para paisagem
  • Jetpack WindowManager fornece WindowSizeClass (WindowWidthSizeClass: COMPACT/MEDIUM/EXPANDED)
  • Material 3 Adaptive — componentes prontos (ListDetailScaffold) com adaptação automática

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também