Dia 1 — Layout com Flexbox

Informatica · Conteudo · publicado em 05/10/2026
Dia 1 de 16

Layout com Flexbox

Aula 1

flexDirection: linha e coluna

flexDirection: linha e coluna

flexDirection decide o eixo principal do View. É a propriedade que faz os

filhos ficarem lado a lado ou empilhados, e o valor padrão é column — por isso

a primeira coisa que aparece numa tela é uma pilha de Text um embaixo do outro,

sem que ninguém tenha pedido coluna. Trocar o valor para row gira o eixo

principal para o horizontal, e é essa troca que transforma um View qualquer

numa barra: os filhos passam a ocupar a largura da esquerda para a direita.

ValorEixo principalOrdem dos filhos
columnverticalde cima para baixo
rowhorizontalda esquerda para a direita
column-reverseverticalde baixo para cima
row-reversehorizontalda direita para a esquerda

O exemplo desta página monta os dois casos com os mesmos dois filhos e imprime o

eixo que cada árvore carrega: a que usa o estilo coluna responde column, e a

que usa o estilo linha responde row. A contagem de filhos é 2 nos dois

casos, e os rótulos saem na mesma ordem — primeiro e segundo. É a prova de

que flexDirection não mexe no conteúdo: ele muda o caminho que os filhos

percorrem, não quem eles são.

O que trava não é o nome da propriedade: é achar que alignItems também mexe

no eixo principal. Ele mexe no eixo cruzado, sempre. Se o flexDirection é

column, o eixo cruzado é a horizontal e alignItems decide se o filho gruda

na esquerda, na direita ou no centro. Se o flexDirection é row, o eixo

cruzado é a vertical e o mesmo alignItems decide se o filho sobe, desce ou

centraliza. A propriedade não muda de nome; o que muda é a direção em que ela

age.

const estilos = StyleSheet.create({
  linha: { flexDirection: 'row' },
  coluna: { flexDirection: 'column' },
});

// ler o eixo de uma arvore ja montada
function eixoDe(arvore) {
  return StyleSheet.flatten(arvore.props.style).flexDirection || 'column';
}

StyleSheet.flatten é o que torna essa leitura possível. O estilo de um

elemento pode ser um objeto, um array de objetos ou nada; flatten resolve

qualquer um dos três em um objeto só, que é a forma de consultar o valor efetivo

de uma propriedade. E o || 'column' da função é o valor padrão escrito como

código: a função devolve column para uma árvore que não declara eixo nenhum, e

o exemplo mostra isso passando uma árvore sem style.

O eixo principal decide o resto

Regra que resolve quase tudo: **justifyContent age no eixo principal e

alignItems no cruzado**. Guardar a frase resolve o caso em que o aluno troca

flexDirection, esquece de mexer no alinhamento e não entende por que o texto

ficou colado na borda. Nada de errado aconteceu: os dois alinhamentos continuam

válidos, estão medindo em eixos diferentes.

O eixo transversal também tem nome quando o eixo principal é row: a altura da

barra. E em column a altura deixa de ser o eixo principal e vira a soma dos

filhos, o que significa que um View de coluna sem altura definida não tem

altura própria — os filhos que a determinam.

Vale medir o eixo pelo valor efetivo e não pelo que está no StyleSheet, porque

os dois divergem com frequência. Uma barra pode declarar flexDirection em um

estilo compartilhado e receber flexDirection de outro lugar por um segundo

argumento do StyleSheet.create. O exemplo imprime a linha inteira como { flexDirection: 'row' }, e é esse objeto — não o nome do estilo — que diz qual eixo está valendo naquela árvore.

Quando o eixo é column-reverse, o cruzão continua sendo a horizontal, e o

começo da linha passa a ser embaixo. É por isso que a inversão do eixo não

inverte também o alinhamento: justifyContent: 'flex-start' num eixo invertido

gruda os filhos na borda que passou a ser a última.

Alinhar dentro de um filho é outra coisa

alignSelf sobrescreve o alignItems do pai em um único filho, e é o recurso

certo quando um View de linha tem três filhos e só um deles deve ir para o

fim. justifyContent: 'flex-end' empurraria os três; alignSelf: 'flex-end'

empurra um. A diferença entre os dois é a mesma diferença entre alinhar o grupo

e alinhar o membro.

// o pai alinha os filhos; o filho se posiciona por conta
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Text>aviso</Text>
  <Text style={{ alignSelf: 'flex-end' }}>agora</Text>
</View>

O mesmo alignSelf funciona nos dois eixos, e ele sempre age no cruzado. Numa

coluna ele empurra o filho para a esquerda ou para a direita; numa linha, para

cima ou para baixo. Escrever alignSelf: 'flex-end' esperando que o texto

desça em uma coluna não funciona, porque o eixo principal da coluna é o

vertical, e no eixo principal quem distribui é o pai, por justifyContent.

alignSelf aceita auto, que é o padrão: o filho herda o alignItems do pai.

Escrever auto explicitamente é a forma de cancelar um alignSelf que veio de

um estilo compartilhado, e é mais claro do que trocar o estilo inteiro.

quando reverto o eixo

row-reverse e column-reverse existem para o padrão de escrita de fora para

dentro: idioma que se lê da direita para a esquerda, e barra de navegação

empurrada para o fim da linha. Também aparecem quando se usa `position:

absolute com right em vez de left`, o que o React Native aceita mas evita

por ser menos previsível.

O exemplo trata o reverso como mais um valor da mesma propriedade, e é o que ele

é: StyleSheet.flatten de { flexDirection: 'row-reverse' } devolve um objeto

com uma chave só, com o mesmo formato do row. Não existe propriedade

diferente para inverter, não existe flag e não existe efeito colateral: o eixo

principal muda de direção e o cruzado continua ortogonal a ele.

Na dúvida, o conserto quase nunca é reverter o eixo: é reordenar a lista que

alimenta o map. Inverter o eixo muda a direção em que os filhos aparecem;

reordenar os dados muda a ordem em que eles aparecem — e só a segunda abordagem

resolve o caso em que a ordem importa para outra coisa, como a ordem de leitura

do conteúdo ou a ordem de envio de um formulário.

Exemplo

// `flexDirection` escolhe o eixo principal do `View`. O exemplo monta os
// dois casos, linha e coluna, e le o estilo de cada arvore para mostrar o
// que muda de verdade quando o valor muda.
const { View, Text, StyleSheet } = require('react-native');

const estilos = StyleSheet.create({
  coluna: { flexDirection: 'column' },
  linha: { flexDirection: 'row' },
  item: { padding: 8 },
});

function Item(props) {
  return <Text style={estilos.item}>{props.rotulo}</Text>;
}

function EmColuna() {
  return (
    <View style={estilos.coluna}>
      <Item rotulo="primeiro" />
      <Item rotulo="segundo" />
    </View>
  );
}

function EmLinha() {
  return (
    <View style={estilos.linha}>
      <Item rotulo="primeiro" />
      <Item rotulo="segundo" />
    </View>
  );
}

// `flatten` resolve o array ou o objeto de estilo em um objeto so, que e a
// forma de ler o valor efetivo de uma propriedade.
function eixoDe(arvore) {
  return StyleSheet.flatten(arvore.props.style).flexDirection || 'column';
}

console.log('eixo da coluna:', eixoDe(EmColuna()));
console.log('eixo da linha:', eixoDe(EmLinha()));

// os filhos continuam os mesmos; o que muda e o caminho que eles percorrem
const linha = EmLinha();
console.log('filhos na linha:', linha.props.children.length);
console.log('rotulos:', linha.props.children.map((f) => f.props.children));

// o eixo transversal: com `row`, o cruzado e a altura
const estiloLinha = StyleSheet.flatten(linha.props.style);
console.log('linha inteira:', estiloLinha);
console.log('padrao quando nada e declarado:', eixoDe({ props: {} }));
console.log('o reverso usa o mesmo par de eixos:', StyleSheet.flatten({ flexDirection: 'row-reverse' }));

Saída real

eixo da coluna: column
eixo da linha: row
filhos na linha: 2
rotulos: [ 'primeiro', 'segundo' ]
linha inteira: { flexDirection: 'row' }
padrao quando nada e declarado: column
o reverso usa o mesmo par de eixos: { flexDirection: 'row-reverse' }
Aula 2

justifyContent, alignItems e flexWrap

justifyContent, alignItems e flexWrap

justifyContent distribui os filhos ao longo do eixo principal e alignItems

ao longo do eixo cruzado. Com flexDirection: 'row', o primeiro decide o que

acontece na horizontal e o segundo na vertical; com flexDirection: 'column' os

papéis invertem. É por isso que justifyContent: 'center' centraliza a pilha

inteira de uma tela, mas centraliza lado a lado numa linha — o mesmo valor, dois

resultados, porque o eixo que ele mede mudou.

justifyContentE os filhos ficam
flex-startcolados no começo do eixo, que é o padrão
centercentralizados no eixo, com o mesmo espaço antes e depois
flex-endcolados no fim do eixo
space-betweencolados nas pontas, com o espaço todo entre eles
space-aroundespaço igual entre eles, metade desse espaço nas pontas
space-evenlyespaço igual em todas as frestas, inclusive nas pontas

alignItems aceita os mesmos seis valores mais baseline, que alinha os filhos

pela linha de base do texto — o que importa quando a linha tem um Text

grande ao lado de um pequeno e os dois precisam parecer da mesma altura de linha,

e não estar centralizados.

O exemplo desta página monta um cabeçalho de linha com space-between e

alignItems: 'center', e imprime o estilo efetivo da raiz já achatado em três

chaves: flexDirection: 'row', justifyContent: 'space-between',

alignItems: 'center'. Depois passa pelos seis valores de justifyContent um a

um, e cada um sobrevive no objeto como foi escrito. É o que separa "o valor não

fez nada" de "o valor fez, e o eixo estava errado": no objeto o valor está lá de

qualquer forma, e o que muda o resultado é o eixo em que ele age.

distribuir é o mesmo que centralizar

space-around com dois filhos e space-evenly com três dá a mesma sensação de

ar entre eles; a diferença está nas pontas. space-around põe metade do espaço

na borda, então o grupo fica visualmente deslocado para o meio. space-evenly

divide em partes iguais e o grupo fica opticamente centrado, que é o motivo de

ele ser o valor certo para a linha de um rodapé com três textos. Quando o

espaço é pouco, space-between e space-evenly produzem a mesma tela — a

diferença só aparece quando sobra espaço.

Os três valores com space medem o mesmo intervalo: o que sobra entre o último

filho e a borda. O que muda é quem fica com esse sobra. Em space-between o

sobra vai todo para dentro, e as pontas ficam limpas. Em space-around cada

filho ganha a mesma margem dos dois lados, e a borda ganha metade. Em

space-evenly a borda ganha a mesma margem que o espaço entre os filhos, e o

resultado é o único dos três que fica balanceado sem ajuste.

Como a diferença é de proporção, ela só aparece com espaço sobrando. Três itens

curtos que quase preenchem a largura renderizam igual nos três valores — e o

escolhido errado só importa quando a largura mudar. Como a largura muda quando o

aparelho gira, o valor que "dava para escolher qualquer um" deixa de servir.

O detalhe que costuma passar: center não é o mesmo que space-evenly com um

filho — com um só, os dois põem o espaço igualmente dos dois lados. Com dois

filhos, center os junta no meio e deixa o vazio todo fora; space-evenly os

separa e distribui o vazio entre eles e as bordas. Depende do que a linha faz:

agrupar é center, espalhar é space-*.

flexWrap é o que decide se quebra

flexWrap: 'nowrap' é o padrão: os filhos nunca quebram linha, e se a soma

delas passa da largura do pai, o View transborda ou os filhos encolhem, porque

o padrão do flex é poder encolher. flexWrap: 'wrap' permite a quebra: o filho

que não cabe passa para a linha de baixo inteiro. É a propriedade de Tags,

filtros e atalhos de teclado, onde a quantidade de itens varia e ninguém quer

cortar texto ao meio.

O exemplo testa a conta do wrap sem depender de largura de tela real. Ele soma

as larguras dos itens — 70, 90 e 60, o que dá 220 — compara com a largura

da linha de 150 e imprime que não cabe. Em nowrap e sem flex, sobram 70

pixels para dividir entre os três filhos, e é essa divisão que faz cada um

encolher e o Text quebrar por dentro. Em wrap, o exemplo conta quantos itens

cabem antes de estourar e imprime 1: o primeiro item sozinho já passa dos

150, e os outros dois vão para a linha de baixo.

// a linha de filtro quebra quando os itens somam mais que a largura
<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
  {filtros.map((f) => (
    <Text key={f.id} style={{ padding: 6 }}>{f.rotulo}</Text>
  ))}
</View>

Um Text não quebra no meio de uma palavra por padrão: ele quebra em espaços.

Com wrap, a quebra acontece entre os itens inteiros; sem wrap, o que quebra é

o texto de cada item. A diferença é visível na hora: uma linha de filtro que

corta "Todos" no meio está com nowrap e itens encolhendo.

Quando a linha quebra, a altura do pai cresce — a altura é a soma das alturas

das linhas. É por isso que uma linha de filtro com wrap e altura fixa corta a

segunda linha em vez de mostrar. Quem decide é a altura, e o conserto é deixar a

altura acompanhar o conteúdo ou colocar a linha dentro de um ScrollView.

O valor que produz o exemplo é o mesmo que o flexWrap decide: o View de

filtro com wrap e altura livre mostra todas as linhas, e o mesmo View com

height fixa mostra a primeira e corta a segunda. A propriedade decide se a

quebra existe; a altura decide se ela aparece inteira.

o sintoma que denuncia alinhamento trocado

Se um View de linha não obedece a justifyContent: 'center', quase sempre a

falta é width. View de linha ocupa a largura disponível por padrão, então

centralizar filhos dentro dele não muda nada: já estão no centro do espaço que ele

ocupa. Para o justifyContent fazer efeito visível, o View precisa medir

menos que a tela — width, maxWidth, ou nenhum filho ocupando a linha inteira

com flex: 1. Alinhar é só operação com o espaço que sobrou.

O contra-sintoma do mesmo problema aparece do outro lado: quando a linha ocupa

toda a largura e ninguém força nada, o primeiro filho gruda na borda. Isso não é

justifyContent falhando — é o padrão flex-start funcionando, e o defeito

está em não ter declarado nada. A confusão entre os dois casos é o que faz o

aluno culpar a propriedade errada.

Há um terceiro sintoma, mais difícil: o alinhamento funciona no eixo errado. A

caixa centraliza na vertical quando o Wanted era a horizontal, e mexer em

justifyContent não muda nada. A verificação é uma frase — *qual é o

flexDirection deste View?* — e a resposta diz qual dos dois alinhamentos age

onde. O exemplo resolve a pergunta antes de qualquer valor: ele imprime o estilo

da raiz com flexDirection: 'row' ao lado dos dois alinhamentos, e a leitura é

que aquele cabeçalho é justificado na horizontal e alinhado na vertical.

alignItems: 'center' num View sem height é o mesmo caso por outro nome.

Sem altura declarada, a altura do pai é a soma dos filhos, o eixo cruzado não

tem sobra e centralizar não tem para onde mover. Centralizar exige sobra em um

dos eixos, e sobra é sempre consequência de alguma medida declarada em algum

lugar.

Exemplo

// `justifyContent` age no eixo principal, `alignItems` no cruzado, e
// `flexWrap` decide se os filhos quebram linha. O exemplo monta um
// cabecalho em linha e imprime o efeito de cada valor de alinhamento.
const { View, Text, StyleSheet } = require('react-native');

const estilos = StyleSheet.create({
  cabecalho: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
  titulo: { fontSize: 18 },
  contador: { fontSize: 12, paddingHorizontal: 8 },
});

function Cabecalho(props) {
  return (
    <View style={estilos.cabecalho}>
      <Text style={estilos.titulo}>{props.titulo}</Text>
      <Text style={estilos.contador}>{props.total + ' itens'}</Text>
    </View>
  );
}

const arvore = Cabecalho({ titulo: 'Cardapio', total: 12 });
console.log('tipo da raiz:', arvore.type);
console.log('estilo da raiz:', StyleSheet.flatten(arvore.props.style));
console.log('titulo:', arvore.props.children[0].props.children);
console.log('contador:', arvore.props.children[1].props.children);

// cada valor de `justifyContent` produz uma distribuicao diferente
const alinhamentos = ['flex-start', 'center', 'flex-end', 'space-between', 'space-around', 'space-evenly'];
for (const valor of alinhamentos) {
  const estilo = StyleSheet.flatten({ flexDirection: 'row', justifyContent: valor });
  console.log(valor + ' ->', estilo.justifyContent, '| eixo principal: horizontal');
}

// `alignItems` no eixo cruzado, que aqui e a altura
console.log('no cruzado:', StyleSheet.flatten({ alignItems: 'baseline' }).alignItems);

// `flexWrap`: os itens quebram linha, ou encolhem
function somaLarguras(itens) {
  return itens.reduce((total, item) => total + item.largura, 0);
}

const itens = [{ largura: 70 }, { largura: 90 }, { largura: 60 }];
const larguraDaLinha = 150;
console.log('soma das larguras:', somaLarguras(itens));
console.log('cabe em ' + larguraDaLinha + '?', somaLarguras(itens) <= larguraDaLinha);
console.log('com nowrap e sem flex, sobram', somaLarguras(itens) - larguraDaLinha, 'px para dividir entre 3 filhos');
console.log('com wrap, cada linha tem no maximo', itens.filter((i) => somaLarguras(itens.slice(0, itens.indexOf(i) + 1)) <= larguraDaLinha).length, 'item(ns)');

Saída real

tipo da raiz: View
estilo da raiz: {
  flexDirection: 'row',
  justifyContent: 'space-between',
  alignItems: 'center'
}
titulo: Cardapio
contador: 12 itens
flex-start -> flex-start | eixo principal: horizontal
center -> center | eixo principal: horizontal
flex-end -> flex-end | eixo principal: horizontal
space-between -> space-between | eixo principal: horizontal
space-around -> space-around | eixo principal: horizontal
space-evenly -> space-evenly | eixo principal: horizontal
no cruzado: baseline
soma das larguras: 220
cabe em 150? false
com nowrap e sem flex, sobram 70 px para dividir entre 3 filhos
com wrap, cada linha tem no maximo 1 item(ns)