Flutter is fast by default, but production apps have complex state, large lists, and remote data. These are the ten optimisations I apply to every app before shipping.

1. Use const Constructors Everywhere

Marking a widget const tells Flutter it will never change — it's skipped in the rebuild pass entirely. Modern lint rules (prefer_const_constructors) catch these automatically.

dart
// ✅ Rebuilt only once, ever
const Text('Hello, world!');

// ❌ Evaluated on every build()
Text('Hello, world!');

2. Shrink ListView with itemExtent

ListView.builder is already lazy, but adding a fixed itemExtent lets Flutter skip the layout pass for every child — critical for lists with hundreds of items.

dart
ListView.builder(
  itemExtent: 72.0, // fixed height — massive layout savings
  itemCount: items.length,
  itemBuilder: (ctx, i) => MovieTile(items[i]),
)

3. Avoid Rebuilding Expensive Subtrees

Extract your expensive widgets into separate classes or use RepaintBoundary to isolate their repaint zones. BlocSelector is better than BlocBuilder when you only depend on a slice of state.

  • Use RepaintBoundary around heavy animations (prevents full-screen repaints)
  • Split BLoC state into smaller Cubits to narrow rebuild scope
  • Prefer BlocSelector over BlocBuilder when only one field of state matters

4. Cache Network Images

flutter_cache_manager (used by cached_network_image) stores images on disk between sessions. Never use Image.network directly for user-generated content.

dart
CachedNetworkImage(
  imageUrl: user.avatarUrl,
  placeholder: (ctx, url) => const CircleAvatar(),
  errorWidget: (ctx, url, e) => const Icon(Icons.person),
  memCacheWidth: 120, // downscale in memory to save RAM
)

5. Run Heavy Work in Isolates

JSON parsing of large payloads blocks the UI thread. Use Isolate.run() (Dart 2.19+) or compute() to offload to a background isolate.

dart
final movies = await Isolate.run(() => 
  (jsonDecode(rawResponse) as List)
      .map(Movie.fromJson)
      .toList(),
);

The Remaining 5 Tips (Summary)

Space prevents covering every tip in detail, but these are equally important in practice:

  • 6. Enable Impeller on iOS (set io.flutter.embedded_views_preview in Info.plist)
  • 7. Use DevTools CPU profiler before optimising — measure, don't guess
  • 8. Minimise app startup: initialise non-critical services lazily, after first frame
  • 9. Tree-shake unused icon fonts with --tree-shake-icons in flutter build
  • 10. Prefer assets over network for static images — avoids HTTP overhead on first load

Written by Iqbal Nova

Mobile Developer @ GMEDIA · Mobile Developer specializing in Flutter & React Native.