Learn Flutter - Performance Optimization
Episode 15 of 23

Learn Flutter - Performance Optimization

This episode measures and accelerates your app: rendering performance and jank reduction, profiling with DevTools, the widget inspector, and timeline, techniques to reduce rebuilds with widget caching and image optimization, and memory usage and app startup time.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

A slow app drives users away as fast as a broken feature. Episode 15 teaches you how to measure first, then optimize: rendering performance and jank reduction, profiling with DevTools and the widget inspector, techniques to reduce rebuilds and optimize images, and memory usage and startup time.

The golden rule before reading this episode: optimization without measurement is a guess. Always profile first, then change code.

Rendering Performance and Jank Reduction

The 60 FPS Target

Flutter targets 60 frames per second (or 120 on capable devices). Each frame only has about 16 milliseconds for layout, paint, and compositing. When a frame exceeds that limit, jank appears — stutter you feel while scrolling.

Common Causes of Jank

  • Heavy work inside the build method (file I/O, network, parsing).
  • Expensive operations while scrolling (excessively rebuilding long lists).
  • Repainting large areas that don't need repainting.
A clean build method
class ItemList extends StatelessWidget {
  const ItemList({super.key});
 
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 1000,
      itemBuilder: (context, index) {
        return const ItemTile(index: index);
      },
    );
  }
}

Notice the const on ListView.builder and ItemTile. const tells Flutter the widget is immutable — the framework can skip unnecessary rebuilds, one of the easiest performance savings.

RepaintBoundary to Isolate Repaints

Wrap areas that rarely change with RepaintBoundary so they aren't repainted when other areas change:

Isolate a repaint area
RepaintBoundary(
  child: CustomPaint(painter: PetaPainter()),
)

RepaintBoundary makes CustomPaint render to a separate layer. When surrounding widgets change, the map doesn't need to be redrawn — effective for elements like maps, animated backgrounds, and previews.

Profiling with DevTools

Profile Mode and DevTools

Run the app in profile mode for realistic measurements:

Run in profile mode
flutter run --profile

flutter run --profile enables AOT while still keeping DevTools available — this is the profile used for measuring, not debug mode. Click the Open DevTools icon in the terminal to open the tool suite.

Widget Inspector and Timeline

  • Widget inspector: see the widget tree and identify unnecessary rebuilds.
  • Performance / Timeline: measure frame time, spot frames that exceed the 16ms limit.
  • Memory: monitor heap usage and detect leaks.
Launch DevTools from the terminal
flutter run --profile --devtools

flutter run --profile --devtools directly opens DevTools in the browser. Filter the timeline for slow frames, check which widgets are built most often, then optimize that point — not some random place.

Reducing Rebuilds, Widget Caching, and Image Optimization

const and Reusable Widgets

Unnecessary rebuilds are usually the biggest source of waste. Three high-impact fixes:

  1. Use const for immutable widgets.
  2. Move heavy sub-trees into separate const widgets.
  3. Use itemExtent on long lists to make layout cheaper.

Image Optimization

Images are the biggest source of memory and bandwidth. Use cached_network_image for caching and correct resolution:

Add image caching
flutter pub add cached_network_image
Image with cache and placeholder
CachedNetworkImage(
  imageUrl: 'https://example.com/logo.png',
  placeholder: (context, url) => const SizedBox(
    width: 48,
    height: 48,
    child: CircularProgressIndicator(strokeWidth: 2),
  ),
  errorWidget: (context, url, error) => const Icon(Icons.broken_image),
)

CachedNetworkImage stores images on disk so repeated requests don't happen, and placeholder prevents layout jumps while loading. Resize images on the server side when possible — downloading a 4K image for a 48px thumbnail is wasteful.

Memory Usage and App Startup Time

Monitoring Memory

Use the DevTools Memory tab and watch for patterns:

  • A heap that keeps climbing without dropping indicates a leak.
  • Listeners and controllers that aren't disposed are a common cause.
Dispose a controller properly
class SearchScreen extends StatefulWidget {
  const SearchScreen({super.key});
 
  @override
  State<SearchScreen> createState() => _SearchScreenState();
}
 
class _SearchScreenState extends State<SearchScreen> {
  final TextEditingController _controller = TextEditingController();
 
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
 
  @override
  Widget build(BuildContext context) {
    return TextField(controller: _controller);
  }
}

_controller.dispose() in dispose prevents leaks. The rule: every resource opened in initState must be closed in dispose.

App Startup Time

Slow startup is usually caused by heavy work in main() and the initial build. Common fixes:

  • Avoid expensive work in main() before runApp.
  • Load large data asynchronously instead of blocking the first frame.
  • Use flutter build apk --release --obfuscate to shrink the binary size in releases.

Conclusion

Key takeaways:

  • Target 60 FPS; jank appears when a frame exceeds 16 milliseconds.
  • Profile with DevTools first, then optimize — don't guess.
  • Use const on immutable widgets to skip rebuilds.
  • RepaintBoundary isolates areas that rarely change.
  • Optimize images with cached_network_image and the right size.
  • Dispose all controllers and listeners to prevent memory leaks.

In the next episode 16 we discuss animations and UI polish — the difference between implicit and explicit animations, AnimationController, Tween, and AnimatedBuilder, Hero animations and transitions for motion design, and CustomPaint and advanced UI effects. Your app starts to feel alive and premium.

Learn Flutter - Performance Optimization | Learn Flutter