# Otimização de desempenho no Flutter em 2026: Impeller, reconstruções e boas práticas > Como manter aplicativos Flutter a 60 ou 120 fps constantes em 2026 com Impeller, reconstruções de widgets disciplinadas, RepaintBoundary e profiling no DevTools. - Published: 2026-07-04 - Updated: 2026-07-07 - Author: SharpSkill - Tags: flutter, performance, impeller, best-practices, dart, mobile - Reading time: 9 min --- A otimização de desempenho no Flutter em 2026 se apoia em duas mudanças que transformaram a forma de construir aplicativos fluidos: o motor de renderização Impeller agora vem ativado por padrão no iOS e no Android, e o modelo de reconstrução do framework recompensa quem mantém árvores de widgets pequenas e imutáveis. As equipes que usam as duas alavancas sustentam 60 ou 120 fps constantes sem ajustar cada frame na mão. > **A mudança de maior impacto** > > O maior ganho na maioria dos aplicativos Flutter está em adicionar construtores `const` e dividir métodos `build` grandes, para que uma chamada de `setState` repinte um único botão em vez de uma tela inteira. ## Por que o Impeller redefine a referência de desempenho do Flutter O Impeller é o motor de renderização do Flutter e substituiu o Skia como padrão nas plataformas móveis. A diferença importa principalmente nos primeiros segundos de uma animação. O Skia compilava seus shaders em tempo real, na primeira vez que um efeito aparecia na tela, o que produzia o famoso engasgo na primeira execução pelo qual os aplicativos Flutter eram criticados. O Impeller compila esses shaders com antecedência, durante a fase de build, de modo que o primeiro frame de uma animação é tão fluido quanto o centésimo. O Impeller também mira diretamente nas APIs gráficas modernas: Metal no iOS e Vulkan no Android, com um recuo para OpenGL em hardware Android mais antigo. A [documentação do motor Impeller no GitHub](https://github.com/flutter/flutter/blob/master/docs/engine/impeller/README.md) detalha a arquitetura de renderização e a matriz de suporte de plataformas. | Aspecto | Skia (legado) | Impeller (padrão 2026) | |--------|---------------|-------------------------| | Compilação de shaders | Em tempo de execução, causa engasgos na primeira execução | Com antecedência, no build | | Backend do iOS | OpenGL / Metal | Metal | | Backend do Android | OpenGL | Vulkan, recuo para OpenGL | | Fluidez da primeira animação | Engasgos ocasionais | Constante | Para a maioria dos projetos a migração é gratuita: as versões recentes do Flutter ativam o Impeller automaticamente, e os antigos contornos de aquecimento de shaders (`--purge-persistent-cache`, arquivos `.sksl` empacotados) não são mais necessários e podem ser removidos. Os shaders `FragmentProgram` personalizados escritos com o subconjunto de GLSL que o Flutter suporta também continuam funcionando, já que o Impeller os compila pelo mesmo pipeline offline dos efeitos nativos do framework. Verificar qual motor um aplicativo executa leva uma linha: um build de depuração imprime o backend ativo na inicialização, e o inspetor do DevTools o informa nos detalhes de renderização do aplicativo. Uma vez confirmado o Impeller, o restante do trabalho de desempenho sobe para a camada de widgets, onde o framework reconstrói muito mais do que a tela realmente muda. É aí que o resto deste guia se concentra, porque um motor rápido não salva uma árvore de widgets que se repinta centenas de vezes por segundo. ## Reduzir reconstruções no Flutter com const e divisão de widgets Cada `setState` marca seu widget como sujo e executa `build` novamente para aquele elemento e sua subárvore. O truque é manter essa subárvore o mais estreita possível. Dois hábitos fazem a maior parte do trabalho: marcar widgets estáticos como `const` e empurrar o estado para baixo, de modo que a parte com estado fique pequena. Um widget `const` é canonizado em uma única instância, então, quando um pai se reconstrói, o Flutter compara a referência idêntica, não vê mudança e pula por completo o repintar dessa ramificação. ```dart // product_screen.dart // A cart update repaints the badge, not the header. class ProductScreen extends StatefulWidget { const ProductScreen({super.key}); @override State createState() => _ProductScreenState(); } class _ProductScreenState extends State { int _cartCount = 0; @override Widget build(BuildContext context) { return Column( children: [ const ProductHeader(), // const: never rebuilds on setState CartBadge(count: _cartCount), // only this subtree rebuilds FilledButton( onPressed: () => setState(() => _cartCount++), child: const Text('Add to cart'), ), ], ); } } ``` Ativar o lint `prefer_const_constructors` no `analysis_options.yaml` transforma essa disciplina em uma verificação do compilador, de modo que as oportunidades de `const` nunca regridem em silêncio. A regra relacionada `prefer_const_literals_to_create_immutables` estende a mesma garantia a listas e mapas passados para widgets. As chaves de widget também merecem menção. Quando uma lista é reordenada ou um widget mantém o mesmo tipo, mas muda de identidade, uma `ValueKey` ou uma `ObjectKey` permite que o Flutter associe o elemento antigo à instância de widget nova correta em vez de destruir e reconstruir o estado. A falta de chaves é uma causa comum de perda de posição de rolagem e de reinício de animações em listas dinâmicas, e o trabalho extra de reconstrução que ela provoca passa facilmente despercebido. ## Restringir reconstruções aos dados que mudaram Quando o estado vive no topo da árvore, mesmo um layout bem dividido se reconstrói bastante. A solução é transmitir um valor e deixar um único widget ouvi-lo. O Flutter traz `ValueNotifier` e `ValueListenableBuilder` exatamente para isso, sem nenhum pacote externo. ```dart // article_list.dart // A scroll-to-top button that rebuilds alone, not the whole page. class ArticleList extends StatefulWidget { const ArticleList({super.key}); @override State createState() => _ArticleListState(); } class _ArticleListState extends State { final _showFab = ValueNotifier(false); final _controller = ScrollController(); @override void initState() { super.initState(); // Update the notifier, not setState: the list never rebuilds _controller.addListener(() => _showFab.value = _controller.offset > 400); } @override void dispose() { _showFab.dispose(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: ListView.builder( controller: _controller, itemCount: 200, itemBuilder: (_, i) => ListTile(title: Text('Item $i')), ), floatingActionButton: ValueListenableBuilder( valueListenable: _showFab, builder: (_, visible, __) => visible ? FloatingActionButton( onPressed: () => _controller.jumpTo(0), child: const Icon(Icons.arrow_upward), ) : const SizedBox.shrink(), ), ); } } ``` As bibliotecas de gerenciamento de estado generalizam a mesma ideia. O `ref.watch(provider.select(...))` do Riverpod e o widget `Selector` do Provider só se reconstroem quando uma fatia escolhida do estado muda, o que decide entre fluido e travado em telas com muitos dados. As compensações entre essas bibliotecas são tratadas no guia sobre [gerenciamento de estado no Flutter em 2026](/blog/flutter/flutter-state-management-2026-riverpod-bloc-getx). > **Cuidado com o escopo da reconstrução** > > Chamar `setState` perto da raiz de uma tela, ou envolver uma página inteira em um `Consumer` de nível superior, repinta em silêncio centenas de widgets a cada mudança. Faça o profiling com o contador de reconstruções do DevTools antes de supor que uma tela é barata. ## Isolar repintagens caras com RepaintBoundary Reconstruir e repintar são etapas separadas. Um widget pode pular a reconstrução e mesmo assim se repintar porque um vizinho agitado compartilha sua camada. O `RepaintBoundary` dá a uma subárvore sua própria camada, de modo que um gráfico animado não força cartões estáticos a redesenhar a cada frame. ```dart // dashboard.dart // The live chart repaints on its own layer, cards stay untouched. class Dashboard extends StatelessWidget { const Dashboard({required this.ticker, super.key}); final Stream ticker; @override Widget build(BuildContext context) { return Column( children: [ // SummaryCards is static, so isolate it from the animation below const RepaintBoundary(child: SummaryCards()), RepaintBoundary( child: StreamBuilder( stream: ticker, builder: (_, snap) => LivePriceChart(value: snap.data ?? 0), ), ), ], ); } } ``` O RepaintBoundary não é de graça: cada um aloca uma camada, então espalhá-lo por toda parte prejudica mais do que ajuda. Vale reservá-lo para regiões realmente animadas e listas longas com rolagem, confirmando o ganho na linha do tempo. A [referência da API RepaintBoundary](https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html) documenta como a sobreposição de depuração de repintar destaca quais camadas de fato são redesenhadas. ## Manter o método build barato e sem efeitos colaterais Um método `build` é executado a cada reconstrução, então tudo que é caro lá dentro se multiplica. A regra é que `build` deve ler o estado e retornar widgets, nada mais. Alocar um `ScrollController`, um `AnimationController` ou um `Future` dentro de `build` o recria a cada frame e vaza a instância anterior. Esses objetos pertencem ao `initState` ou a um campo `late final`, liberado no `dispose`. O trabalho síncrono pesado é a segunda armadilha. Fazer parse de JSON, filtrar uma lista grande ou formatar datas dentro de `build` reexecuta esse trabalho a cada repintar, mesmo quando os dados subjacentes não mudaram. Calcule o resultado uma vez quando a entrada muda e guarde em cache, ou mova-o para um `FutureBuilder` para que o framework não bloqueie um frame esperando por ele. Para valores derivados de outros valores, memoize-os atrás de um getter que só recalcula quando suas dependências mudam. O widget `Opacity` é um culpado específico que vale a pena nomear. Envolver uma subárvore em `Opacity` força um `saveLayer` fora da tela, uma das operações mais caras do pipeline. Para uma animação de fade, `AnimatedOpacity` ou uma `FadeTransition` são mais baratas, e para uma tonalidade fixa, aplicar a cor no nível de pintura por meio de um `foregroundDecoration` ou de um shader evita a camada por completo. A mesma cautela vale para `ClipPath` e `ClipRRect` com anti-aliasing em superfícies grandes, que também disparam um `saveLayer` nos bastidores. Por fim, prefira coleções `const` e callbacks que não capturam novas closures a cada build. Um `const []` passado como padrão e uma referência de método em vez de uma lambda inline mantêm baratas as comparações de igualdade de widgets, o que permite que o curto-circuito de `const` descrito antes realmente dispare. ## Construir listas longas e imagens com eficiência Feeds longos e grades de imagens são onde o código Flutter ingênuo perde frames. Duas regras cobrem a maioria dos casos: construir as linhas de forma preguiçosa e decodificar as imagens no tamanho em que aparecem, e não na resolução de origem. ```dart // feed.dart // Lazy rows plus right-sized image decoding keep scrolling at 60fps. class Feed extends StatelessWidget { const Feed({required this.posts, super.key}); final List posts; @override Widget build(BuildContext context) { return ListView.builder( itemCount: posts.length, // build on demand, not all at once cacheExtent: 600, // pre-build past the viewport edge itemBuilder: (context, index) { final post = posts[index]; return ListTile( leading: Image.network( post.thumbnailUrl, width: 48, height: 48, cacheWidth: 96, // decode at display size, save memory ), title: Text(post.title), ); }, ); } } ``` Uma imagem em resolução total de 3000 pixels espremida em um avatar de 48 pixels desperdiça memória e tempo de decodificação em cada uma delas. Definir `cacheWidth` diz ao motor para decodificar no tamanho alvo, o que sozinho pode reduzir a pegada de memória de uma lista em uma ordem de grandeza. As [boas práticas de desempenho](https://docs.flutter.dev/perf/best-practices) do Flutter reúnem mais orientações sobre `Opacity`, `saveLayer` e uso de recortes que seguem o mesmo princípio. ## Medir a otimização do Flutter em 2026 com o DevTools Cada regra acima deve ser verificada, não presumida. O Flutter DevTools é a fonte da verdade. A sobreposição de desempenho (`P` no console de depuração, ou `flutter run --profile`) desenha dois gráficos, UI e raster, e qualquer barra que cruza a linha verde é um frame perdido. A visão de linha do tempo então atribui cada frame lento a uma chamada específica de `build`, layout ou pintura. Faça sempre o profiling em modo `--profile` em um dispositivo físico. Builds de depuração executam Dart não otimizado e inflam cada medição, então uma tela que trava em depuração muitas vezes roda perfeitamente depois de compilada. O [guia de desempenho do DevTools](https://docs.flutter.dev/tools/devtools/performance) mostra como ler o gráfico de frames e identificar as fontes de travamento, e esse mesmo reflexo de profiling aparece em entrevistas sobre [animações e renderização no Flutter](/technologies/flutter/interview-questions/animations). > **Um ciclo rápido de profiling** > > Execute em modo profile, reproduza a interação travada, abra a linha do tempo e ordene os frames por duração. O pior frame quase sempre aponta para uma única reconstrução exagerada ou uma imagem sem cache, o que é muito mais rápido do que adivinhar. O [hub de tecnologia Flutter](/technologies/flutter) conecta os tópicos de gerenciamento de estado, testes e renderização que completam uma estratégia de desempenho em produção. ## Conclusão O desempenho no Flutter em 2026 tem menos a ver com microtruques e mais com uma lista curta de hábitos aplicados de forma consistente: - Publicar com o Impeller e remover o código legado de aquecimento de shaders; o problema de travamento no primeiro frame está resolvido no nível do motor. - Marcar widgets estáticos como `const` e reforçar isso com o lint `prefer_const_constructors`. - Dividir os widgets com estado em unidades pequenas, para que cada `setState` repinte a subárvore mais estreita possível. - Transmitir os valores que mudam por meio de `ValueListenableBuilder` ou de uma API do tipo `select` em vez de reconstruir telas inteiras. - Envolver as regiões realmente animadas em um `RepaintBoundary`, mas medir antes de adicionar mais. - Construir as listas com `ListView.builder` e decodificar as imagens no tamanho de exibição usando `cacheWidth`. - Fazer o profiling em modo `--profile` em hardware real e deixar o DevTools, não a intuição, decidir o que otimizar em seguida. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/flutter/flutter-performance-optimization-2026-impeller-rebuilds