Pare de quebrar sua interface: aprenda LayoutBuilder como um verdadeiro profissional de Flutter.
Se você já projetou uma interface de usuário perfeita em cada detalhe…
…apenas para vê-la desmoronar completamente em um celular menor, um tablet maior ou no “celular de 2010 do seu amigo que se recusa a morrer”, então bem-vindo à realidade do Flutter.
A maioria das interfaces de usuário apresenta problemas porque o widget não tem ideia de quanto espaço realmente possui. É como tentar decorar um cômodo sem saber suas dimensões — é claro que os móveis não vão caber.
O Flutter tem uma solução para isso, e é poderosa:
O que é o LayoutBuilder?
Você pode já ter se deparado com estes erros frustrantes:
1. RenderBox was not laid out
Isso acontece quando um widget tenta se organizar sem conhecer suas restrições exatas.
É como arrumar móveis em um quarto escuro — você não sabe onde estão as paredes.
2. BoxConstraints força uma largura/altura infinita.
Este erro ocorre quando se espera que um widget se ajuste ao seu tamanho dentro de um espaço “infinito” — algo impossível no Flutter.
Ambos os erros são basicamente o Flutter gritando:
“Seu widget NÃO sabe o espaço que recebe!”
Como o LayoutBuilder resolve isso
Ao envolver um widget com LayoutBuilder, o Flutter passa as restrições exatas durante o layout:
LayoutBuilder (
builder : (context, constraints) {
// constraints.maxWidth
// constraints.maxHeight
// constraints.minHeight
// constraints.minWidth
},
)
Eu entendo maxWidth e maxHeight…
mas qual é a utilidade de minWidth e minHeight se eles são quase sempre zero?
Pode parecer que não importam.
Mas, na verdade, elas revelam algo muito importante sobre o quão rígido ou flexível é o widget pai .
Vamos analisar isso passo a passo.
Por que minWidth/minHeight parecem ser sempre zero?
Em muitos layouts comuns — como Row, Column, Stack, e ListView— o elemento pai NÃO força seus elementos filhos a terem um tamanho mínimo.
Assim, as restrições geralmente se parecem com:
larguraMínima = 0 larguraMáxima = larguraDisponível
O que minWidth/minHeight realmente indicam?
Representam o espaço mínimo garantido para o seu widget .
Então:
- Se
minWidth = 0→ o pai for flexível - Se
minWidth > 0→ o pai estiver impondo um limite mínimo - Se
minWidth = maxWidth→ o pai estiver impondo uma restrição rígida
É assim que você percebe o quão rigorosos seus pais são em relação ao tamanho das roupas .
Em que parte eu deveria envolver o LayoutBuilder?
Agora que você entende o que o LayoutBuilder oferece — as restrições exatas que seu widget recebe — a próxima grande questão é:
Isso é importante porque o LayoutBuilder só faz sentido quando você o envolve no nível correto da árvore de widgets.
Envolvê-lo muito acima resulta em restrições que afetam todo o dispositivo.
Envolvê-lo muito abaixo resulta nas mesmas restrições que você já conhece.
Envolvê-lo corretamente desbloqueia layouts poderosos e responsivos.
Vamos analisar isso passo a passo.
Quando os pais impõem restrições flexíveis ou pouco claras.
Widgets como Row, Column, Expanded, e Flexiblefrequentemente criam situações em que seu filho:
- largura/altura imprevisíveis
- zero tamanhos mínimos
- restrições rígidas ou frouxas
- possibilidade de colapso
LayoutBuilderajuda você a ver e a reagir a isso.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Demo()));
}
class Demo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("LayoutBuilder Demo")),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20),
Text(
"1️ Dentro de uma Row (obtém minWidth = 0)",
style: TextStyle(fontSize: 18),
),
SizedBox(height: 8),
Row(
children: [
Expanded(child: LayoutBuilderBox(color: Colors.blue)),
Container(width: 80, height: 50, color: Colors.red),
],
),
SizedBox(height: 30),
Text(
"2️ Dentro do SizedBox (obtem o tamanho Exato)",
style: TextStyle(fontSize: 18),
),
SizedBox(height: 8),
Center(
child: SizedBox(
width: 200,
height: 150,
child: LayoutBuilderBox(color: Colors.green),
),
),
SizedBox(height: 30),
Text(
"3️ Dentro do Padding (constraints shrink)",
style: TextStyle(fontSize: 18),
),
SizedBox(height: 8),
Padding(
padding: const EdgeInsets.all(30),
child: LayoutBuilderBox(color: Colors.orange),
),
],
),
);
}
}
class LayoutBuilderBox extends StatelessWidget {
final Color color;
const LayoutBuilderBox({required this.color});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, c) {
return Container(
color: color,
alignment: Alignment.center,
child: Text(
"""minWidth: ${c.minWidth.toStringAsFixed(0)}
maxWidth: ${c.maxWidth.toStringAsFixed(0)}
minHeight: ${c.minHeight.toStringAsFixed(0)}
maxHeight: ${c.maxHeight.toStringAsFixed(0)}""",
style: TextStyle(color: Colors.white, fontSize: 16),
textAlign: TextAlign.center,
),
);
},
);
}
}

Como NÃO usar o LayoutBuilder (aprendi isso da pior maneira 😭)
A esta altura, você provavelmente se sente poderoso com o LayoutBuilder.
Você vê as restrições. Você as entende.
Você sussurra para elas como um verdadeiro mestre da interface do usuário.
Mas espere aí — o LayoutBuilder é como eletricidade:
super útil, super legal… e super fácil de se queimar.
1. Não altere o estado dentro do LayoutBuilder (por favor 😅)
Eu já vi isso.
Eu já fiz isso.
Eu me arrependo disso.
LayoutBuilder(
builder: (_, c) {
setState(() {}); // congratulations, infinite loop activated
return Container();
},
);
O Flutter irá reconstruir.
Em seguida, ele chama setState().
Em seguida, reconstrói.
Em seguida, chama setState novamente — você criou um buraco negro.
2. Não espere que o LayoutBuilder corrija elementos pais problemáticos.
O LayoutBuilder não consegue corrigir um widget preso em um contêiner sem limites.
SingleChildScrollView(
child: LayoutBuilder(
builder: (_, c) => Container(
height: c.maxHeight, // 💀 maxHeight is infinite here
),
),
);
Ele lança o clássico: BoxConstraints força uma altura infinita
O LayoutBuilder não cria restrições definidas magicamente
quando o elemento pai é intencionalmente ilimitado (como em views de rolagem).
3. Não execute cálculos pesados no LayoutBuilder
O LayoutBuilder é reconstruído sempre que suas restrições mudam .
Portanto, se você estiver executando alguma operação custosa dentro dele:
LayoutBuilder(
builder: (_, c) {
final result = heavyTask(); // CPU just died
return Text(result.toString());
},
);
Elimine trabalhos dispendiosos. O LayoutBuilder serve para decisões de layout, não para ciência espacial.
Compartilhei meus erros — agora é a sua vez.
Qual foi a maneira mais engraçada ou estranha que você usou o LayoutBuilder de forma errada?