Por que Widgets const ainda sofrem rebuild no Flutter — Uma otimização mal compreendida
Se você já passou algum tempo no mundo do Flutter, provavelmente já ouviu este conselho lendário: ‘Apenas coloque const. Ele não vai sofrer rebuild.’ Geralmente, isso é dito com a mesma confiança de um ‘Você já tentou desligar e ligar de novo?’. E, por um tempo, você acredita. Você espalha const por todo lugar, sente-se produtivo e então abre o Flutter DevTools… apenas para ver os widgets ainda sofrendo rebuild.
No Flutter, um rebuild não é o vilão que as pessoas pintam. Um rebuild significa simplesmente que o método build() está rodando novamente — nada mais. O Flutter faz isso constantemente e é extremamente bom nisso. O que realmente impacta a performance é o trabalho extra após um rebuild, como layout, pintura (paint) e composição (compositing) — o tipo de trabalho que faria a ventoinha do seu celular girar… se celulares tivessem ventoinhas.
É por isso também que você ouve frequentemente: ‘não coloque lógica pesada dentro do método build().’ Como o build() pode rodar com frequência, cálculos caros ali acabam sendo repetidos mais do que você imagina.
Vamos dar uma olhada em um pequeno exemplo. À primeira vista, você pode pensar que ambas as variáveis abaixo são apenas objetos diferentes guardando o mesmo valor, então nenhum dos pares deveria ser idêntico. Essa suposição parece razoável — e também está apenas metade certa.
void main() {
final a = Text('Hello');
final b = Text('Hello');
print(identical(a, b));
const c = Text('Hello');
const d = Text('Hello');
print(identical(c, d));
}
Quando você executa isso, o caso com final imprime false porque cada Text('Hello') é criado em tempo de execução (runtime), resultando em duas instâncias de widget separadas. Mesmo que pareçam iguais, elas não são o mesmo objeto. Já o caso com const imprime true. Isso acontece porque widgets const são criados em tempo de compilação e armazenados em cache pelo Dart, permitindo que a mesma instância seja reutilizada onde quer que ela apareça.
Agora, vamos olhar para um exemplo mais realista — aquele tipo que faz os desenvolvedores dizerem: ‘Mas… é const e ainda está sofrendo rebuild?’
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: CounterPage(),
);
}
}
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
@override
Widget build(BuildContext context) {
debugPrint('CounterPage build');
return Scaffold(
appBar: AppBar(
title: const Text('Const Rebuild Demo'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Count: $count',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
const Text(
'I am const,',
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: const Text('Increment'),
),
],
),
),
);
}
}
Toque no botão algumas vezes e observe o console. Você verá a mensagem CounterPage build impressa toda vez, embora um dos widgets esteja claramente marcado como const. É aqui que a confusão costuma começar. Muitos desenvolvedores esperam que o const Text('I am const') magicamente ‘se safe’ de todos os rebuilds — mas o Flutter nunca prometeu isso.
O que está acontecendo na verdade é muito mais simples (e inteligente). O setState reconstrói o pai — esse é o comportamento esperado. O Flutter, então, compara a árvore de widgets antiga com a nova. O Text('Count: $count') dinâmico muda e é atualizado, enquanto o texto const é reconhecido como o mesmo widget antigo e silenciosamente reutilizado. O widget sofreu rebuild, mas o Flutter pulou o trabalho pesado de layout e pintura (paint).
Portanto, da próxima vez que alguém disser: ‘basta usar const, ele pula os rebuilds’, lembre-se: isso é apenas metade da verdade. O const não impede os rebuilds — ele impede o trabalho desperdiçado. E agora você sabe o porquê 😉.
Se você achou isso útil, deixe um clap (palmas) — isso avisa ao algoritmo que eu não estou apenas falando sozinho. Compartilhe com seu círculo de devs e, se você gosta dessas análises, o seu follow ajuda muito e me mantém escrevendo. Até a próxima!