Learn Dart - Performance Optimization
Series/Learn Dart/Episode 15
Episode 15 of 23

Learn Dart - Performance Optimization

This episode covers Dart performance optimization: AOT compilation and tree shaking, profiling with DevTools and allocation analysis, performance tips for Flutter and servers, and benchmarking and runtime analysis.

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

Introduction

Slow applications drive users away, and wasteful servers drain budgets. Episode 15 covers performance optimization in Dart: how AOT compilation and tree shaking work, how to profile applications with DevTools, practical tips for Flutter and servers, and benchmarking techniques.

Performance isn't a guess — it has to be measured. You'll learn to find bottlenecks with data, not intuition, and then optimize the parts that truly matter.

AOT Compilation, Tree Shaking, and Code Size

AOT for Fast Startup

AOT compilation turns Dart code into machine code before the application runs. The result: no JIT cost at startup, more stable memory allocation, and fast boot times — crucial for mobile apps and servers that get restarted often.

Tree Shaking to Reduce Size

Tree shaking removes code that's never used from the build output. The compiler analyzes the call graph and only includes functions that are actually reachable from the entry point:

Release build with optimal size
flutter build appbundle --release

flutter build appbundle --release produces a production bundle with tree shaking enabled. For web applications, dart compile js -O4 enables the most aggressive optimizations. Check the artifact size before and after to see the savings.

Avoiding Excess Code

Tree shaking only works on code that's actually used. Avoid export statements that expose an entire library without control, and avoid dependencies that are only used for one small function. Large dependencies add build time and bundle size.

Profiling and Allocation Analysis

Using DevTools

DevTools is the official profiling suite for Dart and Flutter:

Opening DevTools
dart devtools

dart devtools opens the profiling dashboard in your browser. The most useful tabs:

  • CPU Profiler: see which functions take the most time.
  • Memory: analyze allocations and detect leaks.
  • Performance: view frame time for Flutter applications.

Analyzing Allocations and Leaks

If memory keeps growing without coming down, there's likely a leak — old objects are never discarded. Watch out for common patterns: listeners that aren't cancelled, controllers that aren't closed, and unbounded caches. The Memory tab in DevTools shows objects still being held and their references.

Performance Tips for Flutter and Servers

Flutter: Frame Time and Widgets

  • Minimal rebuilds: don't build widgets that didn't change; use const for static widgets.
  • RepaintBoundary: isolate areas that change often so the whole page doesn't repaint.
  • ListView.builder: lazily build only the visible items, not the entire list.

Use DevTools' frame profiling to find slow frames. Don't optimize before you have evidence.

Server: Warm-up and Connections

  • Warm-up: run expensive operations once before accepting traffic so JIT doesn't burden the first request.
  • Connection pooling: keep database connections in a pool and reuse them, not one per request.
  • Async I/O: rely on full await and avoid heavy CPU work on the main isolate.

Benchmarking and Runtime Analysis

Benchmarking with Stopwatch

Measure performance regressions automatically with a simple benchmark:

Benchmark with Stopwatch
void main() {
  final stopwatch = Stopwatch()..start();
 
  var total = 0;
  for (var i = 0; i < 1000000; i++) {
    total += i;
  }
 
  stopwatch.stop();
  print('Waktu: ${stopwatch.elapsedMilliseconds} ms');
}

stopwatch.elapsedMilliseconds measures execution time. Save benchmark results as a baseline and rerun them on major changes to catch performance regressions. Benchmarks are also useful for comparing two approaches objectively — for example, picking a faster data structure or algorithm — as long as the measurement conditions are kept exactly the same.

Runtime Analysis in Production

Don't only measure during development. Collect data from production: p95 latency, memory usage, and error rate. Tools like tracing and observability (episode 19) give you a picture of real performance under genuine user load.

Remember the right optimization order: measure first, then optimize. Fixing code that isn't actually slow only adds complexity. Start by profiling to find the biggest bottleneck, fix things one at a time, and re-measure after every change to confirm the impact is positive.

Finally, don't ignore binary size. For mobile apps, bundle size affects users' download time; for servers, image size affects container startup time. Track both as part of your release metrics.

Conclusion

Key takeaways:

  • AOT speeds up startup; tree shaking shrinks the build size.
  • flutter build --release and dart compile js -O4 enable production optimizations.
  • DevTools profiles CPU, memory, and frame time visually.
  • Memory leaks often come from listeners and controllers that aren't closed.
  • Flutter: limit rebuilds; server: use connection pooling and do a warm-up.
  • Benchmark with Stopwatch and monitor production metrics to catch regressions.

In the next episode 16, we'll cover the Dart VM and native interop — working with the Dart VM and command-line tools, native interop with FFI, building CLI utilities, and embedding Dart in host applications.

Learn Dart - Performance Optimization | Learn Dart