Optimizing Flutter Repaints For 120Hz Displays

Summary
Summary
Summary
Summary

Delivering smooth 120Hz Flutter UIs requires halving per-frame work to meet an ~8.33ms budget. Profile to find heavy raster work, reduce widget rebuilds with const and localized state, isolate costly painting using RepaintBoundary, and favor compositing-friendly transforms and opacity animations. Measure, apply targeted RepaintBoundaries, and optimize CustomPainter shouldRepaint to minimize repaints.

Delivering smooth 120Hz Flutter UIs requires halving per-frame work to meet an ~8.33ms budget. Profile to find heavy raster work, reduce widget rebuilds with const and localized state, isolate costly painting using RepaintBoundary, and favor compositing-friendly transforms and opacity animations. Measure, apply targeted RepaintBoundaries, and optimize CustomPainter shouldRepaint to minimize repaints.

Delivering smooth 120Hz Flutter UIs requires halving per-frame work to meet an ~8.33ms budget. Profile to find heavy raster work, reduce widget rebuilds with const and localized state, isolate costly painting using RepaintBoundary, and favor compositing-friendly transforms and opacity animations. Measure, apply targeted RepaintBoundaries, and optimize CustomPainter shouldRepaint to minimize repaints.

Delivering smooth 120Hz Flutter UIs requires halving per-frame work to meet an ~8.33ms budget. Profile to find heavy raster work, reduce widget rebuilds with const and localized state, isolate costly painting using RepaintBoundary, and favor compositing-friendly transforms and opacity animations. Measure, apply targeted RepaintBoundaries, and optimize CustomPainter shouldRepaint to minimize repaints.

Key insights:
Key insights:
Key insights:
Key insights:
  • Understanding Repaints And Frame Budget: 120Hz reduces your available frame time to ~8.33ms, so measure raster and compositing to find hot spots.

  • Reduce Widget Rebuilds: Use const constructors, localize state, and build small widgets so setState affects the minimum subtree.

  • Use Layers And RepaintBoundary: Wrap expensive painters or frequently changing widgets in RepaintBoundary to isolate raster work, but avoid over-layering.

  • Optimize Animations For 120Hz: Prefer transform and opacity animations that use compositing layers; avoid redrawing complex canvases every frame.

  • Measure And Profile: Use the performance overlay and DevTools timeline to target optimizations where they actually matter.

Introduction

High-refresh-rate phones (90Hz, 120Hz, and above) are increasingly common. Delivering a smooth experience on 120Hz displays requires cutting your frame work-time roughly in half: a 60Hz device gives ~16.7ms per frame, while 120Hz gives only ~8.33ms. This tutorial focuses on practical Flutter techniques to reduce repaint cost and meet the tighter frame budget. It assumes familiarity with the widget tree, RenderObjects, and basic animation concepts.

Understanding Repaints And Frame Budget

Repaints are the GPU work that occurs when a composited layer changes visual content. Flutter divides work into build, layout, compositing, and rasterization. At 120Hz, you need to minimize the expensive parts (compositing and raster) and avoid unexpected rebuilds. Use the Flutter performance overlays (Raster and CPU profiling) and the Timeline in DevTools to identify hot frames.

Key metrics: frame time distribution and the “missed frames” spike pattern. If raster time approaches 8ms, the UI will drop frames even when logic is cheap.

Practical checks:

  • Use the performance overlay and enable GPU profiling in DevTools.

  • Look for long raster sections; these indicate heavy painting.

  • Isolate widgets that change frequently and measure their paint cost with repaint boundaries.

Reduce Widget Rebuilds

The simplest wins come from reducing unnecessary builds. Prefer const constructors and immutable widgets. Break large build methods into smaller StatefulWidgets so that setState origin is localized. Avoid calling setState on a parent when only a child needs to change.

Use value-driven widgets that rebuild only what changes. Examples: ValueListenableBuilder or StreamBuilder (for streams). Avoid rebuilding entire lists; use ListView.builder with stable keys and item-level widgets that manage their own state.

Example: make list item self-contained to prevent parent rebuild cascading.

class ListItem extends StatelessWidget {
  final String title;
  const ListItem({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) => Text(title);
}

Use const where possible so Flutter can skip work at build time and reuse widgets.

Use Layers And RepaintBoundary

RepaintBoundary isolates painting into a separate layer. When placed around a frequently changing widget, only that boundary is rasterized on changes instead of the entire parent subtree. But overusing them can create many layers with cost. Place RepaintBoundary where the repaint cost of the child is high and the parent rarely changes.

Example: wrap a complex animated painter with RepaintBoundary.

RepaintBoundary(
  child: CustomPaint(
    size: Size.infinite,
    painter: MyComplexPainter(data),
  ),
);

class MyComplexPainter extends CustomPainter {
  final Data data;
  MyComplexPainter(this.data);

  @override
  void paint(Canvas canvas, Size size) {
    /* expensive paint */
  }

  @override
  bool shouldRepaint(covariant MyComplexPainter old) => old.data != data;
}

Use shouldRepaint judiciously: return false when the painter output does not change. For static decorations, prefer Pictures or pre-recorded layers.

Optimize Animations For 120Hz

Animations that run at the display refresh rate must do less work per frame. Use GPU-friendly widgets (Transform, Opacity with compositing) that leverage compositing layers rather than repainting pixels. When animating simple transforms or opacity, ensure the framework promotes a compositing layer (Flutter does this automatically for some cases). Avoid animating properties that force expensive paints (e.g., redrawing many shapes inside CustomPainter every frame).

Use AnimationController with vsync and prefer Tween animations that only change transform/opacity. When you must update canvas content, consider reducing update frequency: you can down-sample animation updates or run logic at half-rate and interpolate visually.

Also consider frame skipping for non-critical animations: if render time is high, animate with lower precision or use implicit animations that Flutter optimizes internally.

Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services.

Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.

Conclusion

Optimizing for 120Hz is mostly about reducing work per frame and isolating expensive painting. Measure first, then apply const and granular widgets, add RepaintBoundary where it isolates heavy paint, and prefer compositing-friendly animations. Small architectural changes—localizing state, minimizing rebuild scope, and choosing the right painting strategy—can turn a janky 120Hz experience into a fluid one.

Introduction

High-refresh-rate phones (90Hz, 120Hz, and above) are increasingly common. Delivering a smooth experience on 120Hz displays requires cutting your frame work-time roughly in half: a 60Hz device gives ~16.7ms per frame, while 120Hz gives only ~8.33ms. This tutorial focuses on practical Flutter techniques to reduce repaint cost and meet the tighter frame budget. It assumes familiarity with the widget tree, RenderObjects, and basic animation concepts.

Understanding Repaints And Frame Budget

Repaints are the GPU work that occurs when a composited layer changes visual content. Flutter divides work into build, layout, compositing, and rasterization. At 120Hz, you need to minimize the expensive parts (compositing and raster) and avoid unexpected rebuilds. Use the Flutter performance overlays (Raster and CPU profiling) and the Timeline in DevTools to identify hot frames.

Key metrics: frame time distribution and the “missed frames” spike pattern. If raster time approaches 8ms, the UI will drop frames even when logic is cheap.

Practical checks:

  • Use the performance overlay and enable GPU profiling in DevTools.

  • Look for long raster sections; these indicate heavy painting.

  • Isolate widgets that change frequently and measure their paint cost with repaint boundaries.

Reduce Widget Rebuilds

The simplest wins come from reducing unnecessary builds. Prefer const constructors and immutable widgets. Break large build methods into smaller StatefulWidgets so that setState origin is localized. Avoid calling setState on a parent when only a child needs to change.

Use value-driven widgets that rebuild only what changes. Examples: ValueListenableBuilder or StreamBuilder (for streams). Avoid rebuilding entire lists; use ListView.builder with stable keys and item-level widgets that manage their own state.

Example: make list item self-contained to prevent parent rebuild cascading.

class ListItem extends StatelessWidget {
  final String title;
  const ListItem({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) => Text(title);
}

Use const where possible so Flutter can skip work at build time and reuse widgets.

Use Layers And RepaintBoundary

RepaintBoundary isolates painting into a separate layer. When placed around a frequently changing widget, only that boundary is rasterized on changes instead of the entire parent subtree. But overusing them can create many layers with cost. Place RepaintBoundary where the repaint cost of the child is high and the parent rarely changes.

Example: wrap a complex animated painter with RepaintBoundary.

RepaintBoundary(
  child: CustomPaint(
    size: Size.infinite,
    painter: MyComplexPainter(data),
  ),
);

class MyComplexPainter extends CustomPainter {
  final Data data;
  MyComplexPainter(this.data);

  @override
  void paint(Canvas canvas, Size size) {
    /* expensive paint */
  }

  @override
  bool shouldRepaint(covariant MyComplexPainter old) => old.data != data;
}

Use shouldRepaint judiciously: return false when the painter output does not change. For static decorations, prefer Pictures or pre-recorded layers.

Optimize Animations For 120Hz

Animations that run at the display refresh rate must do less work per frame. Use GPU-friendly widgets (Transform, Opacity with compositing) that leverage compositing layers rather than repainting pixels. When animating simple transforms or opacity, ensure the framework promotes a compositing layer (Flutter does this automatically for some cases). Avoid animating properties that force expensive paints (e.g., redrawing many shapes inside CustomPainter every frame).

Use AnimationController with vsync and prefer Tween animations that only change transform/opacity. When you must update canvas content, consider reducing update frequency: you can down-sample animation updates or run logic at half-rate and interpolate visually.

Also consider frame skipping for non-critical animations: if render time is high, animate with lower precision or use implicit animations that Flutter optimizes internally.

Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services.

Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.

Conclusion

Optimizing for 120Hz is mostly about reducing work per frame and isolating expensive painting. Measure first, then apply const and granular widgets, add RepaintBoundary where it isolates heavy paint, and prefer compositing-friendly animations. Small architectural changes—localizing state, minimizing rebuild scope, and choosing the right painting strategy—can turn a janky 120Hz experience into a fluid one.

Build Flutter Apps Faster with Vibe Studio

Build Flutter Apps Faster with Vibe Studio

Build Flutter Apps Faster with Vibe Studio

Build Flutter Apps Faster with Vibe Studio

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Other Insights

Other Insights

Other Insights

Other Insights

Join a growing community of builders today

Join a growing community of builders today

Join a growing community of builders today

Join a growing community of builders today

Join a growing community of builders today

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025