Por que Widgets const ainda são reconstruídos no Flutter — Uma otimização mal compreendida

Você abre seu aplicativo. Ele trava por 2 segundos. Você entra em pânico. O PM (Gerente de Produto) entra em pânico. O usuário? Ele já desinstalou e deixou uma avaliação de 1 estrela dizendo:

“Ideia legal, mas lento pra caramba.” 💀

Enquanto isso, em algum lugar num universo paralelo, um desenvolvedor sênior come um chocolate e faz… absolutamente nada. Porque, às vezes, fazer nada é a otimização mais inteligente que você pode fazer.

Mas aqui está o “pulo do gato”: O problema não é a lógica do seu código. É quando você está fazendo as coisas — não o que você está fazendo.

Bem-vindo ao mundo mágico da lazy initialization (inicialização tardia/preguiçosa) — onde fazer nada a princípio pode ser a coisa mais esperta que você já fez.

O Que É Lazy Initialization? Lazy initialization significa que você adia a criação de algo até que ele seja realmente necessário.

É só isso. Sem configurações desnecessárias. Sem construtores inchados. Apenas o bom e velho “Vou lidar com isso mais tarde — se eu tiver que lidar.”

Pense da seguinte forma: Se o seu aplicativo fosse uma festa em casa, a lazy initialization seria como só cozinhar o macarrão quando os convidados pedirem comida — e não no momento em que eles entram pela porta.

A Maldição do Cold Start (Inicialização a Frio) Vamos conhecer o Dev Exagerado (eu faço parte desse time).

Eles são inteligentes. Ambiciosos. Otimistas. Eles querem preparar tudo antes mesmo que o aplicativo mostre um único pixel. Porque, ei — e se o usuário magicamente precisar de todas as funcionalidades de uma só vez?

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();                
  final crashlytics = Crashlytics();              // O App nem travou ainda
  final db = await HeavyLocalDatabase.init();     // O usuário nem sequer fez login
  final analytics = AnalyticsService();           // Nenhum comportamento para analisar ainda
  final prefs = await SharedPreferences.getInstance(); // Configurações que ninguém pediu ainda
  final monthlyReport = MonthlyReportGenerator(); // Para um relatório previsto para o próximo trimestre
  final ads = await AdSdk.init();                 // A gente nem sabe se anúncios serão exibidos ainda

  runApp(MyApp());
}

O Problema?

  • Você está inicializando tudo.
  • Tudo de uma vez.
  • Antes mesmo que o app consiga respirar.

Isso é como levar bagagem para uma viagem de um ano — para uma caminhada de 10 minutos.

A Solução: Lazy Initialization com get_it.
Vamos apresentar o get_it, o mordomo de serviços do seu app.

  1. crie um arquivolocator.dart:
import 'package:get_it/get_it.dart';

final locator = GetIt.instance;

void setupLocator() {
  // Register lazily — created only when first accessed
  locator.registerLazySingleton<Crashlytics>(() => Crashlytics());

  locator.registerLazySingletonAsync<HeavyLocalDatabase>(
    () async => await HeavyLocalDatabase.init(),
  );

  locator.registerLazySingleton<AnalyticsService>(() => AnalyticsService());

  locator.registerLazySingletonAsync<SharedPreferences>(
    () async => await SharedPreferences.getInstance(),
  );

  locator.registerLazySingletonAsync<AdSdk>(
    () async => await AdSdk.init(),
  );

  locator.registerFactory<MonthlyReportGenerator>(
    () => MonthlyReportGenerator(),
  );
}
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  setupLocator();

  runApp(MyApp());
}

O Salvador do StatefulWidget: Lazy Getter.
A maioria de nós trata o initState() como uma lista de compras de supermercado — ‘Vamos pegar tudo o que a gente talvez precise mais tarde… só por garantia.

Mas esse ‘só por garantia’ geralmente leva a:

  • Primeiros frames mais lentos.
  • Serviços não utilizados consumindo memória desnecessariamente.
  • Código mais difícil de gerenciar e testar.
class DashboardScreen extends StatefulWidget {
  const DashboardScreen({super.key});

  @override
  State<DashboardScreen> createState() => _DashboardScreenState();
}

class _DashboardScreenState extends State<DashboardScreen>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController( // ❌ Always created
      vsync: this,
      duration: const Duration(seconds: 1),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _controller.forward(from: 0);
          },
          child: const Text("Animate"),
        ),
      ),
    );
  }
}

Problema:

  • O _controller é inicializado no initState() de qualquer maneira.
  • Mesmo que o usuário nunca toque no botão, o _controller ainda é criado e fica na memória fazendo… nada.
  • Se for um objeto pesado (como DB, analytics, etc.), você desperdiça recursos.
  • Vamos consertar isso — do jeito preguiçoso (lazy way).

Com um Lazy Getter

class _DashboardScreenState extends State<DashboardScreen>
    with SingleTickerProviderStateMixin {
  AnimationController get _controller => _lazyController ??=
      AnimationController(
        vsync: this,
        duration: const Duration(seconds: 1),
      );
  AnimationController? _lazyController;

  @override
  void dispose() {
    _lazyController?.dispose(); // ✅ Only dispose if created
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _controller.forward(from: 0); // ✅ Created only when used
          },
          child: const Text("Animate"),
        ),
      ),
    );
  }
}

Benefício:

  • O controlador de animação só é criado quando você realmente precisa dele.
  • Se a animação nunca rodar, seu app nunca gasta recursos com ela.
  • O mesmo comportamento para o usuário, um comportamento melhor para o dispositivo.

A lazy initialization permite que você seja estrategicamente preguiçoso — como um verdadeiro dev sênior.

Então relaxe, adie aquela configuração pesada e deixe seu app respirar.

Porque, às vezes, a melhor otimização… …é não fazer absolutamente nada.

Deixe sua opinião nos comentários. E se isso te fez rir ou deixou seu app mais rápido, vá em frente, compartilhe com aquele colega dev Flutter que ainda está complicando as coisas (doing the most).

Não tenha medo de quebrar coisas. Falhe rápido. Corrija mais rápido ainda. Até a próxima ❤️.

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