Before optimizing anything, you need to understand where your application spends time and resources. Profiling helps identify bottlenecks.
Dart DevTools provides powerful tools for CPU and memory profiling.
dart run --observe your_app.dart
Then open DevTools in your browser to inspect:
void heavyTask() {
for (int i = 0; i < 100000000; i++) {
// expensive work
}
}
void main() {
heavyTask();
}
Use CPU profiling to confirm whether heavyTask() is actually the bottleneck before optimizing it.
final stopwatch = Stopwatch()..start();
heavyTask();
stopwatch.stop();
print('Execution time: ${stopwatch.elapsedMilliseconds} ms');
Dart uses automatic memory management with a generational garbage collector. This means:
Frequent allocation of short-lived objects is cheap, but promoting too many objects to the old generation can hurt performance.
for (int i = 0; i < 1000000; i++) {
var temp = List.generate(100, (i) => i);
}
This creates a massive number of temporary lists, increasing GC pressure.
final reusableList = List.filled(100, 0);
for (int i = 0; i < 1000000; i++) {
for (int j = 0; j < reusableList.length; j++) {
reusableList[j] = j;
}
}
Reuse objects when possible instead of constantly creating new ones.
const myWidget = Text('Hello');
Using const ensures the object is created once at compile time instead of runtime.
List<int> numbers = [];
Typed collections are more efficient than dynamic ones.
void main() {
var largeData = List.generate(1000000, (i) => i);
var closure = () {
print(largeData.length);
};
}
Closures can unintentionally keep large objects alive in memory.
Streams are powerful but can leak memory if not properly disposed.
final subscription = stream.listen((data) {
print(data);
});
// Always cancel when done
subscription.cancel();
Choosing the right data structure significantly impacts performance.
var set = <int>{1, 2, 3};
if (set.contains(2)) {
print("Fast lookup");
}
Dart is single-threaded by default but supports concurrency via isolates.
import 'dart:isolate';
void heavyWork(SendPort sendPort) {
int result = 0;
for (int i = 0; i < 100000000; i++) {
result += i;
}
sendPort.send(result);
}
Offloading heavy computation prevents UI or main thread blocking.