Runtime detection
It tracks instance growth so you don't have to.
Radar inspects the live heap through the Dart VM service. It counts live instances per class across forced-GC captures and flags any class that keeps growing and never comes back down — screens, blocs, controllers, subscriptions, timers.
One init call. No mixins, no interfaces, nothing to implement on your own types.
It reads the real object graph from the VM — so it sees what's actually retained, not what you remembered to instrument.
Want exactness on a specific object? track(obj) and markDisposed(obj) — two calls, optional.
Static prevention · custom_lint
Stop the classics before they ship.
Seven rules, each with an auto-fix, flag leak-prone code right in your editor — and they're false-positive-disciplined: they understand disposal inside if/try blocks, constructor-injected (externally-owned) fields, and local variables.
- Undisposed controllers
- Uncancelled
StreamSubscription&Timer - Unclosed
StreamController - Discarded
.listen()results - Missing
removeListener - Bloc subscriptions never cancelled in
close()
class _ChatScreenState extends State {
final _controller = TextEditingController();
// 🛑 leak_radar: never disposed.
// Quick fix → add dispose()
}
On-device UX
The signal comes to you.
Retaining paths, on demand
Lazily fetched so capture stays cheap. See exactly what holds the reference — gc-root → field → … → object, with source locations.
Triggers that fit your flow
A NavigatorObserver scans on screen-pop — the natural "this should be gone now" checkpoint.
Live worst-severity + count, floating over your app. Tap for findings; growth sparklines per class.
Capture a real .dartheap to file for offline DevTools analysis.
Findings as JSON or Markdown, shareable straight from the device.
In DevTools
Capture as many as you like. Diff any two.
When you want to go deep, the DevTools companion keeps a running list of heap snapshots — capture as many as you like, export any of them, and diff any two, ranking per-class growth by instances and bytes. Retaining paths come grouped by closest root, separating live-tree objects from the leak-prone ones, with a composable filter over class and library. Plus a full class histogram — or grab the same heap snapshot headlessly from the command line.
Real code
The whole memory setup.
import 'package:radarscope/radarscope.dart';
void main() {
FlutterRadar.init(
memory: MemoryConfig(
autoScan: AutoScan.onScreenPop,
threshold: Severity.warning,
),
overlay: true,
);
runApp(const MyApp());
}
// Optional — heap detection already works without this.
// Use it when you want exactness on one object.
final controller = ChatRoomController();
FlutterRadar.track(controller);
// later, when you believe it's gone:
controller.dispose();
FlutterRadar.markDisposed(controller);