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!

Please follow and like us:
error0
fb-share-icon
Tweet 20
fb-share-icon20