In Flutter, what does leak_tracker detect, and how do you enable it so testWidgets fails on undisposed objects?
answer
- dispose and GC timing
- not-disposed, not-GCed, GCed late
- only instrumented disposables
- flutter_test_config.dart testExecutable
- LeakTesting.enable()
basics
~20 sleak_tracker watches instrumented disposables and reports not-disposed leaks (collected without dispose), plus experimental not-GCed and GCed-late leaks. Add leak_tracker_flutter_testing as a dev dependency and call LeakTesting.enable() in test/flutter_test_config.dart; testWidgets then fails the file on leaks.
solid answer
~40 sleak_tracker compares **disposal** and **garbage-collection** events for instrumented disposable objects. A **not-disposed** leak is an object collected without `dispose()`; a **not-GCed** leak is one disposed but still retained after several GC cycles; **GCed-late** is disposed and collected later than expected. Not-GCed tracking is experimental and off by default. Flutter's own disposables (`AnimationController`, `ChangeNotifier` subclasses such as `ScrollController`, `FocusNode` and others) dispatch events to `FlutterMemoryAllocations`, which is on in debug builds. To use it in tests I add `leak_tracker_flutter_testing: any` to `dev_dependencies` and, in `test/flutter_test_config.dart`, call `LeakTesting.enable()` and adjust `LeakTesting.settings` before `testMain()`. `testWidgets` then tracks leaks and, at the end of the test file, fails if it found any; a single test can override settings with `experimentalLeakTesting`.
code
yaml · 4 linesdev_dependencies:
flutter_test:
sdk: flutter
leak_tracker_flutter_testing: anygo deeper
Know that leak_tracker exists to fail tests when a disposable object such as a controller is never disposed.
Explain the leak types (not-disposed, not-GCed, GCed-late), that only instrumented objects are tracked, and the flutter_test_config.dart setup.
Show how you roll it out on an existing suite: enable globally, ignore known classes per test with a tracked issue, add creation stack traces, and fix culprits before victims.
Weigh leak tests as a standing quality gate: which packages must be leak-free, how ignores are reviewed, and where manual DevTools investigation is still needed.
## What leak_tracker is `leak_tracker` is a Dart package from the dart-lang/leak_tracker repository, pinned by the Flutter SDK's `flutter_test`, that catches memory problems **in tests** before users see them. It does not scan the heap. Instead it watches two events for each tracked object, **disposal** and **garbage collection**, and flags objects whose timing is wrong. When memory is managed well, an object is disposed and then collected at the next GC cycle; deviations are leaks. ## The leak types | Type | Definition | Usual fix | |---|---|---| | **not-disposed** | a disposable was garbage collected without being disposed | call `dispose()` | | **not-GCed** | disposed, but still not collected after several GC cycles | null out or remove the references that keep it | | **GCed-late** | disposed and collected, but later than expected | same as not-GCed | | **not-GCed-without-path** | disposed and not collected, with no retaining path found | report it; it indicates a tool problem | Tracking of not-GCed leaks is **experimental and off by default**; the recommendation is to track not-disposed leaks. The tool also separates **culprits** from **victims**: a victim is retained only because a culprit is, so fixing the culprit fixes both. ## What it can see It only tracks **instrumented** objects, whose creation and disposal are reported: - Flutter's framework disposables report through `FlutterMemoryAllocations`, including `AnimationController`, `ScrollController`, `FocusNode` and other `ChangeNotifier`s. A `ChangeNotifier` reports its creation on the first `addListener` unless its constructor calls `ChangeNotifier.maybeDispatchObjectCreation`. - The events are on when `kFlutterMemoryAllocationsEnabled` is true: in debug builds by default, or with `--dart-define=flutter.memory_allocations=true`. - Your own classes can be instrumented by dispatching creation and disposal events, and a leak involving at least one instrumented object is usually enough to expose a chain of non-instrumented ones. In release builds leak tracking is disabled. ## Enabling it for widget tests 1. Add the testing package to `dev_dependencies` with `any`, because the Flutter SDK pins its version. 2. Create or edit `test/flutter_test_config.dart` and define `testExecutable`. 3. Call `LeakTesting.enable()` and optionally adjust `LeakTesting.settings`, for example `withIgnored(createdByTestHelpers: true)` to skip objects made by test helpers. 4. Run the tests as usual. `testWidgets` starts tracking for each test. 5. After all tests in the file, tracking collects the leaks, and the default reporter fails when the result is not leak-free. Per-test control uses `testWidgets`' `experimentalLeakTesting` parameter, for example `LeakTesting.settings.withIgnored(classes: ['Image'])` while a known issue is open. `withCreationStackTrace()` adds the allocation stack to the report and `withRetainingPath()` adds retaining paths for not-GCed objects. ## Running apps Tracking a running app is **experimental**: you forward `FlutterMemoryAllocations` events to `LeakTracking`, call `LeakTracking.start()` before `runApp`, run in debug mode, and watch for a summary line such as `leak_tracker: 134 memory leak(s): not disposed: 134, not GCed: 0, GCed late: 0`. ## Why it is worth the setup - It turns the pair **create and dispose** into a checked contract across the whole test suite. - It catches leaks on every change, not only when someone opens DevTools. - Its overhead is a small record per tracked object, acceptable in tests.
- With leak tracking enabled in Flutter tests, when does a leak make the run fail: during the leaking test or later?Later. `testWidgets` tracks objects during each test, but leaks are collected in a teardown that runs after all tests in the file, where not-disposed objects are declared leaks and the default reporter expects a leak-free result. The failure names the leaked classes, and `withCreationStackTrace()` points to where each was created.
- Why might leak_tracker miss an undisposed object from your own Flutter package?It only tracks instrumented objects. Framework disposables report creation and disposal, but a custom class that does not dispatch those events is invisible unless it is retained by an instrumented object that is caught. You can instrument your own disposables by dispatching creation and disposal events.
saying these in an interview costs you the question
- leak_tracker scans the heap and finds every leaked object in the app.
- Not-GCed leak tracking is on by default and fully supported.
- leak_tracker runs in release builds to catch leaks in production.
- You pin leak_tracker_flutter_testing to a specific version independent of the SDK.
- Every test must be wrapped in a special leak-tracking test function.