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.
// ✅ 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.
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.
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.
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